Execution Engine
How the backtest engine evaluates strategies — entry logic, the configurable risk model, position management, and determinism rules.
Overview
The backtest engine evaluates your strategy against historical OHLCV data candle by candle, in chronological order. For each candle, it runs the full evaluation pipeline: compute inputs, evaluate conditions, sum scores, check the decision rule, and open a trade if the rule is met.
The engine is deterministic. Given the same strategy, asset pair, and date range, it always produces the same result.
Evaluation Pipeline
For each candle, the engine runs in this order:
Entry Execution
Entries are executed at the close of the candle where the decision rule evaluates to true. There is no look-ahead — the signal is generated using data up to and including the current close, and the entry price is that close.
#Risk Model (R-Units)
Every trade's stop-loss and take-profit are calculated at entry by the strategy'sriskManagement configuration. It is optional — if a strategy omits it, the engine falls back to a default:
- Stop-loss1% from entry price (default).
- Take-profit3% from entry price — 1:3 risk-reward ratio (default).
- Win (in R)+3.0R per winning trade (default).
- Loss (in R)-1.0R per losing trade (default).
Results are measured in R, not in currency amounts. This makes strategy results comparable regardless of account size, asset price, or position sizing method.
Configuring riskManagement
To override the default, add a riskManagement object to the strategy with a stopLoss and/or takeProfit block.
| Section | Type | Parameters |
|---|---|---|
| stopLoss | percent | percent — distance from entry, e.g. 1.0 for 1% |
| stopLoss | atr | period, multiplier — stop distance = ATR(period) × multiplier |
| takeProfit | riskReward | multiple — take-profit distance = stop-loss distance × multiple |
| takeProfit | percent | percent — distance from entry, independent of the stop-loss |
"riskManagement": {
"stopLoss": { "type": "atr", "period": 14, "multiplier": 1.5 },
"takeProfit": { "type": "percent", "percent": 2.0 }
}riskManagement, win/loss R magnitudes are no longer always exactly +3.0R / -1.0R — an ATR-based stop-loss, for example, varies the risk distance (and therefore the R value of each trade) based on volatility at entry time.Why R-units?
R-units remove the illusion of dollar profits. A strategy withpnlR: +50 returned 50 units of risk across the backtest period. Whether each unit was $10 or $1,000 is a position sizing decision made separately. This is the correct way to evaluate statistical edge.
#Multiple Positions
By default, the engine supports multiple open positions simultaneously. Every candle where the decision rule evaluates to true opens a new, independent trade — even if other trades are already open.
This is intentional: the goal is to evaluate the statistical edge of the strategy signal itself, not the portfolio behavior under any particular position sizing scheme.
Capping concurrent positions
Set configuration.maxOpenPositions to cap how many trades can be open at once. When the cap is reached, new entry signals are skipped until a position closes. Omit it (or leave it null) for unlimited concurrent positions — the default, unchanged behavior.
"configuration": {
"timeframe": "1H",
"maxOpenPositions": 1
}maxOpenPositions: 1 gives single-position mode — the closest approximation to how a trader managing one position at a time would run the strategy.Same-Candle Ambiguity
When both the stop-loss and take-profit levels are reached within the same candle (i.e., the candle's low is below SL and its high is above TP), the engine conservatively assumes the stop-loss triggered first.
The count of these ambiguous trades is reported in the bothHitfield of the results. A high bothHit count relative to total trades may indicate the strategy is being used on a timeframe that is too coarse for the intended entry precision.
Warmup Period
Indicators require a minimum number of candles to compute (the lookback period). For example, ema(close, 21) requires at least 21 candles of history before its value is meaningful — before that, it is still converging (or, for window-based indicators like rsi and atr, undefined).
The engine does not infer this automatically. Set configuration.warmupBars to the largest lookback period used by any indicator in your strategy. Candles within the warmup window are still included in the results (so you can inspect indicator values as they stabilize), butentry is forced to false andscore to 0 for that range — no trade can open during warmup.
"configuration": {
"timeframe": "1H",
"warmupBars": 21
}warmupBars defaults to 0. If your strategy uses an indicator with a longer lookback than the warmup you configured (or you omit it entirely), early candles may generate signals off of indicator values that have not fully stabilized yet.Strategy Diagnostics
Beyond performance metrics, the engine tracks two diagnostic datasets that are critical for strategy quality analysis:
conditionsDistributionPct
For each condition in the strategy, the fraction of all evaluated candles where it was true. A condition that is true on 95% of candles adds almost no discriminating power and may be wasting score weight.
scoreDistributionPct
For each possible score value, the fraction of candles that reached that score. This shows how often your strategy is "close to triggering" vs. fully aligned, and helps tune your entry threshold.
#Limitations
- No slippage simulation. Entries and exits are at exact close/SL/TP prices.
- No trading fees or commissions are deducted from results.
- No capital compounding. Each trade is sized independently at 1R.
- Only long (buy) positions are supported in the current version. Short selling is not yet available.
- Data is limited to supported asset pairs and their available historical range.
- The engine processes candles sequentially — there is no intra-candle execution simulation.