Search players, teams, venues and pages
Under the hood
How the fantasy-points model was built, trained and tested, and how good it really is. Every chart explains itself: open “Learn more”.
Metrics
The average size of a miss, in points.
Take every prediction, subtract the actual score, drop the sign and average. An MAE of 20 means a typical prediction is about 20 fantasy points off. It treats a 10-point miss on a bowler the same as a 10-point miss on a batter, and large misses count linearly (unlike RMSE).
MAE = (1/n) · Σ |actualᵢ − predictedᵢ|In this model: Reported per player-match row, then averaged per match; compared with the last-5 baseline. Lower is better.
Related: RMSE (root mean squared error), Baseline (last-5 form), Residual
Metrics
Like MAE but large misses are punished more.
Square each error, average, then take the square root. Because errors are squared, one 80-point miss hurts much more than four 20-point misses. The mean head of our model is trained to minimise squared error (its training curve is in L2 / RMSE units).
RMSE = √[ (1/n) · Σ (actualᵢ − predictedᵢ)² ]In this model: The mean head's learning curves report l2 (mean squared error) on the inner validation season.
Related: MAE (mean absolute error), Loss function
Metrics
Does the model order players correctly within a match?
Rank the players of one match by prediction and by actual points, then correlate the two rankings. +1 means a perfect order, 0 means no relationship. For fantasy picking the order matters more than the exact number: you only need to know who will outscore whom.
ρ = Pearson correlation of rank(predicted) and rank(actual), computed within each match, then averaged over matchesIn this model: Computed per match (22–24 players), averaged across matches.
Related: MAE (mean absolute error), Best-XI points
Fantasy
The real score of the team an optimiser picks from the predictions.
For each match an exact optimiser picks the 11 players (Dream11 rules: 1–8 per role, max 10 from one side, C ×2, VC ×1.5) that maximise the predicted points. That team is then scored with the players' actual points. It is the metric closest to what a user experiences.
In this model: Model XI vs baseline XI vs the hindsight XI (the best team possible with actual points, an upper bound).
Related: Hindsight (perfect) XI, Captain hit rate (top-2), Baseline (last-5 form)
Fantasy
The best possible team, picked after the match with actual points.
An upper bound nobody can reach before the match. The gap between the model's XI and the hindsight XI shows how much of the attainable score the model captures.
Related: Best-XI points
Fantasy
How often the chosen captain is one of the match's top-2 scorers.
The captain doubles their points, so picking the right one is the single biggest lever in fantasy. We count a hit when the optimiser's captain finished first or second in actual points among all players in the match.
Related: Best-XI points
Metrics
The simple rule the model must beat: a player's average over their last 5 games.
A model is only useful if it beats an obvious, cheap rule. Ours predicts each player's points as the mean of their last 5 IPL games (0 for debutants). Every metric is shown as model vs baseline, with a confidence interval on the difference.
Related: Paired bootstrap 95% confidence interval, MAE (mean absolute error)
Metrics
A range for 'how much better than the baseline', accounting for luck.
Take the per-match differences (model − baseline). Resample the matches with replacement thousands of times and recompute the average difference each time; the middle 95% of those averages is the interval. 'Paired' means both methods are judged on the same matches, which removes match-to-match noise. If the interval excludes 0, the improvement is unlikely to be luck.
CI₉₅ = [2.5th, 97.5th] percentile of mean(d*) over B resamples d* of the per-match differences dIn this model: 2,000 resamples over matches.
Related: Baseline (last-5 form)
Metrics
Actual minus predicted for one row.
Positive residual = the player scored more than predicted (model under-predicted). Looking at residuals by group (role, experience, batting first) reveals systematic bias that one overall number hides.
residual = actual − predicted; bias = mean(residual)Related: MAE (mean absolute error), Calibration
Uncertainty
A value the outcome should fall below a given share of the time.
The 10th percentile (p10) is a 'floor': the player should score less than it only 10% of the time. p50 is the median and p90 a 'ceiling'. Fantasy scores are very skewed (many low scores, a few huge ones), so a range says much more than a single number.
In this model: Three separate LightGBM heads predict p10, p50 and p90 directly; they are sorted so they never cross.
Related: Pinball (quantile) loss, Coverage (p10–p90), Calibration
Uncertainty
The loss that teaches a model to predict a quantile.
For the 90th percentile, under-predicting costs 0.9 per point and over-predicting costs only 0.1 per point, so the best constant is the value exceeded 10% of the time. Lower pinball loss = sharper and better-calibrated quantiles.
Lτ(y, q) = τ·(y − q) if y ≥ q, else (1 − τ)·(q − y)Related: Quantile (p10 / p50 / p90), Loss function
Uncertainty
How often the actual score lands inside the predicted range.
A p10–p90 interval should contain the actual score 80% of the time. Much less means the ranges are over-confident (too narrow); much more means they are too wide to be useful.
coverage = share of rows with p10 ≤ actual ≤ p90 (target 80%)Related: Quantile (p10 / p50 / p90), Calibration
Uncertainty
Do predicted numbers mean what they say on average?
Group predictions into bins (e.g. all rows predicted 30–35 points) and compare with the actual average in each bin. A calibrated model's bins lie on the diagonal. For quantiles, calibration means the share of actuals below p10 is about 10%, below p50 about 50%, below p90 about 90%.
Related: Coverage (p10–p90), Reliability diagram
Uncertainty
Predicted vs actual average per prediction bin; the diagonal is perfect.
Points above the diagonal: players in that bin scored more than predicted (under-prediction). Below: over-prediction. Bigger bins (more rows) are more trustworthy.
Related: Calibration
Trees & boosting
A flowchart of yes/no questions on features that ends in a number.
Each internal node asks something like 'ewm_5 < 31.2?'; each leaf holds a value. A row follows the answers to one leaf and receives its value. A single small tree is a weak, coarse model.
Related: Leaf, Gradient boosting
Trees & boosting
An end node of a tree; it stores the value added to the prediction.
num_leaves caps how many leaves one tree may have, i.e. how many distinct groups of rows it can separate. More leaves = more complex trees.
Related: Decision tree, num_leaves
Trees & boosting
Many small trees, each one fixing the errors left by the ones before.
Start from a constant (e.g. the average score). Fit a small tree to the current errors (more precisely, the negative gradient of the loss), add a fraction of it (the learning rate) to the model, repeat hundreds of times. LightGBM is a fast implementation that grows trees leaf-wise and bins feature values into histograms.
F₀ = const; Fₘ(x) = Fₘ₋₁(x) + η · treeₘ(x), treeₘ fit to −∂L/∂F at Fₘ₋₁In this model: Four independent boosted models ('heads'): mean (squared error) and p10/p50/p90 (pinball loss).
Related: Learning rate (η, shrinkage), Decision tree, Early stopping
Trees & boosting
The number training tries to make small.
Squared error for the mean head (it targets the average outcome), pinball loss for each quantile head (it targets that quantile). Learning curves plot the loss per boosting round.
Related: RMSE (root mean squared error), Pinball (quantile) loss
Trees & boosting
How much of each new tree is added; smaller = slower but steadier.
With η = 0.03 each tree corrects only 3% of the remaining error, so the model needs many trees but each step is cautious, which usually generalises better. Learning rate and number of trees trade off against each other.
Related: Gradient boosting, Early stopping
Trees & boosting
Maximum leaves per tree: the main knob for tree complexity.
A tree with 15 leaves can model interactions between a few features; 255 leaves can memorise noise. Too many leaves with little data overfits.
Related: Leaf, Overfitting
Trees & boosting
Minimum rows a leaf must contain.
A leaf built from 5 rows is a guess; one built from 100 rows is an average you can trust. Larger values make the model smoother and less prone to fitting flukes.
Related: Overfitting, Regularisation (lambda_l2 …)
Trees & boosting
Each tree sees a random subset of the features.
At 0.8 each tree may only use 80% of the features. This decorrelates the trees and stops the model leaning on one dominant feature, much like a random forest.
Related: Bagging (bagging_fraction / bagging_freq), Regularisation (lambda_l2 …)
Trees & boosting
Each tree is trained on a random subset of the rows.
bagging_fraction 0.8 with bagging_freq 1 means every iteration samples 80% of the rows. The noise this adds acts as regularisation and speeds training.
Related: feature_fraction, Regularisation (lambda_l2 …)
Trees & boosting
Anything that discourages the model from fitting noise.
lambda_l2 adds a penalty on large leaf values, shrinking them toward zero; min_child_samples, feature/bagging fractions, a small learning rate and early stopping are also regularisers.
leaf value = −Σgradients / (Σhessians + λ₂)Related: Overfitting, min_child_samples
Trees & boosting
Learning the training data's noise instead of the pattern.
Training loss keeps falling while validation loss stalls and then rises. The model 'knows' past matches by heart but predicts new ones worse. Visible in the learning curves as the gap between the two lines.
Related: Early stopping, Regularisation (lambda_l2 …)
Trees & boosting
Stop adding trees when the validation loss stops improving.
Train while watching a held-out validation set; if the validation loss hasn't improved for N rounds, stop and keep the best round. That number of trees is then used to refit on all the training data.
In this model: Validation = the last ~one season of the training window (never the season being scored); patience = early_stopping_rounds.
Related: Overfitting, Learning rate (η, shrinkage)
Trees & boosting
The numbers a model sets during training.
A neural network's parameters are its weights and biases, all updated together by gradient descent. A tree ensemble's 'parameters' are the split thresholds and leaf values it chose; each tree is fixed once added. We count one threshold per internal node plus one value per leaf. Hyper-parameters (num_leaves, learning rate, …) are chosen by us, not learned.
per tree: leaves + (leaves − 1) split thresholdsRelated: Hyper-parameter, Decision tree
Trees & boosting
A setting chosen before training (not learned from the data).
num_leaves, min_child_samples, learning_rate, etc. They are tuned by trying a small grid and keeping the setting with the best validation error across the cross-validation folds.
Related: Cross-validation, Learned parameters (trees vs neural nets)
Training protocol
Estimate performance on data the model didn't train on, several times.
Split the data into folds, train on some and score on the others, repeat. For time-ordered data the folds must respect time, so we use rolling-origin folds.
Related: Rolling-origin (time-series) CV, Data leakage
Training protocol
Train on seasons before Y, validate on Y; slide Y forward.
Fold 2020 trains on 2008–2019 and scores 2020, fold 2021 trains on 2008–2020 and scores 2021, and so on. The model never sees the future, exactly like in real use.
In this model: Folds 2020–2024 choose the hyper-parameters. 2025 is then scored once as a test; 2026 is walk-forward.
Related: Cross-validation, Walk-forward evaluation, Test set (held-out season)
Training protocol
Data used exactly once, after every choice was made.
If you keep checking the test set while tuning, it stops being an honest estimate. Our 2025 season is scored once, by a model trained on 2008–2024 with the chosen settings.
Related: Rolling-origin (time-series) CV
Training protocol
Replay a season: retrain, predict the next block of matches, repeat.
Mimics live use. Before each block of matches the model is retrained on everything played so far, then predicts the block. It shows whether recent data helps and how the model behaves as a season unfolds.
In this model: 2026, retraining every wf_block matches.
Related: Rolling-origin (time-series) CV
Training protocol
Information from the future sneaking into a prediction.
E.g. using a player's season average that includes the match being predicted. Leakage makes offline metrics look great and live predictions disappoint. Our features are point-in-time: each is computed only from matches that finished before the toss of the match being predicted.
In this model: A test perturbs every outcome from a cut-off match onward and requires the features at that match to stay identical.
Related: Point-in-time feature
Training protocol
A feature computed only from what was known at prediction time.
At the toss we know both XIs, the venue, the toss result and weather, plus every past match. We don't know this match's scorecard. Every feature respects that cut-off.
Related: Data leakage
Training protocol
Recent seasons count more in training.
Cricket changes (impact player rule, scoring rates), so a 2010 match should teach the model less than a 2024 one. Each training row gets a weight that halves every few seasons back.
weight = 0.5 ^ ((newest season − season) / half-life)Related: Walk-forward evaluation
Training protocol
A validation slice carved out of the training window.
Used for early stopping, so the number of trees is chosen without ever peeking at the fold being scored. Here: the last ~74 matches (about one season) before the fold.
Related: Early stopping
Explainability
How much the trees rely on each feature.
Split importance counts how many times a feature is used in a split. Gain importance sums how much those splits reduced the loss, so it weighs useful splits more. Both are global and say nothing about the direction of the effect; correlated features share credit.
Related: SHAP values
Explainability
How much each feature pushed one prediction up or down.
From game theory (Shapley values): a feature's contribution is its average marginal effect over all orders in which features could be added. For trees there is an exact fast algorithm (TreeSHAP). Contributions add up exactly: base value + Σ SHAP = prediction. Mean |SHAP| over many rows is a global importance that, unlike gain, is in points.
prediction = base value + Σⱼ φⱼIn this model: LightGBM's pred_contrib on the mean head.
Related: Feature importance (gain / split), Base value (expected value), SHAP dependence plot
Explainability
The average prediction before looking at this row's features.
The starting point of a SHAP waterfall; each feature's SHAP value moves the prediction away from it.
Related: SHAP values
Explainability
Feature value vs its SHAP contribution, one dot per row.
Shows the shape of a feature's effect: rising dots = higher values push predictions up. Vertical spread at the same value comes from interactions with other features.
Related: SHAP values
Explainability
How strongly two features move together (−1 … +1).
Highly correlated features carry overlapping information; trees will use one or the other almost arbitrarily, which splits importance between them.
r = cov(x, y) / (σx · σy)Related: Feature importance (gain / split)