Using a model in a backtest
A model is not the finish line. Its output is one prediction per point in time, and that prediction becomes an input to a backtest, where the same fill, marking, and session rules apply as to any other signal. Use this page to take a model that validated out of sample and wire its prediction into entry, exit, or sizing, the terminal stage every workflow ends in. A model that looked strong in validation can still lose here once fills and sizing are applied, and that is the point of running it.
From model to input
A model produces one prediction at each point on the spine. What that prediction is depends on the model family, and it decides how the prediction plugs in.
- A regression model outputs a value: a predicted return, or a predicted market quantity like intraday volatility. You use the value directly.
- A classifier outputs a class probability, for example the modeled probability that the trade wins. A probability is the natural gate or sizing input. Threshold it to gate entries, or scale size by it.
In a backtest the prediction drives entry, exit, or position sizing, the same roles any other signal fills. The prediction carries the model's whole training and validation discipline into the run as a single number per minute.
Referencing a model's prediction inside the Signal language is planned; there is no accessor for a model output today, and that wiring is the part still being built.
Driving entry, exit, or sizing
A prediction plugs into a backtest in one of two shapes, matched to the model family.
- Gate on it. Threshold the prediction to produce an entry or exit condition. For a classifier, enter only when the modeled win-probability clears a threshold. For a regressor, compare the predicted value against a level.
- Size by it. Scale position size by the prediction. A classifier's probability is a natural size multiplier: larger size when the model is more confident. A regressor's value plugs in directly, for example scaling size down linearly as a volatility forecast rises above a threshold.
Both shapes route through the execution model's truthiness rule, so build the condition to match it. An entry or exit signal is level-based with no edge detection, and only a value of 1 counts as truthy:
nullproduces no action.NaNproduces no action.- Any other number (
0,0.5,2) is falsy.
A comparison in the Signal language returns a Float64 1.0 / 0.0 Signal, which is exactly the shape the entry slot requires. A thresholded prediction has to resolve to that same 1.0 / 0.0 before it gates anything. Here is the shape, written with an IV-rank signal that already has an accessor:
// Entry signal: enter only when 1-year IV rank clears 0.60.
// The comparison returns a Float64 1.0 / 0.0 Signal, the shape the entry slot requires.
iv_daily = option.iv(30, sampling=sampling(period="day", time="close"))
rolling_rank(iv_daily, 252) > 0.60A thresholded model prediction gates in the same way, once the wiring above is available.
Because entry is level-based with no edge detection, a condition that stays truthy attempts a new entry every eligible minute, up to the position limits. A prediction that sits above your threshold all session is a held-truthy entry, not a single trade. Size the threshold and the limits so a persistent signal does not open a position every minute.
What the backtest assumes about the model's input
The engine treats a model prediction like any other signal. Three properties govern how it is read.
The prediction series is aligned onto the engine's per-minute spine with backward as-of fill: each spine minute takes the most recent prediction whose timestamp is at or before that minute. It carries no future information. Rows dated after the spine are dropped before alignment, so a prediction can never reach back from the future. This is the same no-lookahead guarantee every signal gets.
The prediction is evaluated as Float64.
A daily or bucketed prediction is visible only at its session-close label, so it does not update intraday. A model that predicts once per day becomes readable at that day's close and then carries forward on every minute of the next session until the next close. If you want a prediction to move within the session, it has to be computed on a within-session cadence.
The realism the backtest still imposes
A model does not escape execution realism. Every trade the prediction triggers is priced and marked the same way as any other backtest, which is why an out-of-sample edge can shrink or vanish once it is run.
Fills price under one of three modes: the Backtest.ai fill model (calibrated to estimate where between mid and the touch an order fills, and the mode paper and live always use), mid (an optimistic bound), or bid_ask (a pessimistic bound: buy at the ask, sell at the bid). Marks between trades are always mid, regardless of fill mode. Forced liquidations and equity-halt closes always cross the spread, whichever fill mode you chose. See fills, marks and slippage for the full pricing table.
What the backtest does not model: no market impact, no partial fills, no capacity modeling.
The session bounds apply to a model-driven strategy exactly as to any other. The first fill is at 09:35 and the last new trade is at 15:55 on a normal day (12:55 on a half day). A prediction that fires before 09:35 waits for the first eligible minute.
Every backtest run is measured against a benchmark, S&P 500 total return by default. It sits alongside the strategy's own results so a model's edge is read against a baseline, not in isolation.
Re-validating end to end
A model that validated out of sample has cleared one bar: it predicted on data it did not train on. The backtest is a different and harder bar. It applies fills, marks, margin, session timing, and sizing, and it turns a per-minute prediction into a sequence of real positions with real costs.
The gap between the two is where edges die. A classifier with strong discrimination can still lose money if the trades it gates open into wide spreads, or if a held-truthy signal over-trades, or if sizing commits capital when the prediction is weakest. A volatility regressor can forecast well and still leave nothing after fills cross the spread on every adjustment. None of that shows up in validation metrics, because validation measures prediction quality, not tradability.
Treat the backtest as the real test. The validation layer tells you the prediction is sound. The backtest tells you whether the prediction is tradable under the same realism you would face live.
The backtest defaults the model inherits
The model's edge is measured under the backtest's defaults, not under the conditions it trained on. State them wherever you report a model-driven result.
- The prediction and every signal are read with no-lookahead visibility; a daily value appears only at its session-close label.
- Rolling and lag windows are observation-count on the current spine, not calendar days.
- Marks between trades are mid.
- The fill mode is your choice of three: the Backtest.ai fill model (calibrated, matching paper and live),
mid(optimistic), orbid_ask(pessimistic).
For how these defaults shape a result and how to report them, see backtest assumptions and realism.