This document describes the known limitations, assumptions, and edge cases in ml4t-backtest. Understanding these helps set realistic expectations for backtest results.
- Assumption: All trades settle immediately (T+0)
- Reality: US equities settle T+2, futures T+1
- Impact: Buying power is available immediately after a sale
- Workaround: Use
settlement_delay=2in BacktestConfig for T+2 settlement, orcash_buffer_pctto reserve liquidity
settlement_delaymodels T+N settlement (proceeds unavailable for N bars)- Does not model partial settlement or settlement failure
- Assumption: Input data is already split/dividend adjusted
- Backtesting with unadjusted data will produce incorrect results
- Recommendation: Use adjusted close prices from data providers
- Delisted securities are not automatically closed
- Positions persist until explicit exit signal or end of data
- Manual handling required for bankruptcy/delisting scenarios
- Dividend payments are not modeled (assumed reinvested in price)
- Short selling borrowing costs are not modeled
- Bond coupon payments are not supported
- Orders filled at the same bar's close introduce look-ahead bias
- Use
NEXT_BARmode for realistic execution - SAME_BAR mode useful for vectorized strategy comparison only
-
Deferred Exit Re-Entry: When a stop-loss triggers and defers to next bar's open, entry signals on the same bar are blocked to prevent same-bar re-entry. This matches VectorBT Pro behavior.
-
Order Queue Priority: Exits are processed before entries each bar to ensure capital freed by exits is available for new entries.
- Within a single bar, order execution sequence is:
- Deferred exits from previous bar (at open)
- Risk rule exits (stop-loss, take-profit)
- Strategy entry orders
- Cannot model specific intrabar order sequences
- Gap opens are handled: if bar opens beyond stop price, fill at open
- This models slippage during fast market conditions
- Large gaps may fill at worse prices than the stop level
- Default slippage is percentage-based, not volume-dependent
- Does not account for order book depth or market impact
- Large orders in illiquid securities may face higher slippage
ExecutionLimitscan constrain fills to percentage of bar volume- Requires volume data in the feed
- Partial fills create multi-bar execution
- All orders assume sufficient liquidity at quoted prices
- Limit orders fill if price is touched (no queue position)
- Market orders fill at close/open (no spread modeling)
- No locate requirements or borrow availability checks
- Any security can be shorted without constraint
- No hard-to-borrow fees or rebate rates
- Margin accounts enforce maintenance margin requirements
- Position liquidation on margin call is not automatic
- Strategies should monitor margin via
get_buying_power()
- No uptick rule (Rule 201) enforcement
- Short sales allowed at any price level
- Position rules (StopLoss, TakeProfit, etc.) operate independently per position
- Portfolio rules (MaxDrawdown, MaxPositions) check aggregate exposure
- Cross-position hedging is not automatically recognized
STOP_PRICE: Fill at exact stop price (default)BAR_EXTREME: Fill at bar's low/high (conservative)NEXT_BAR_OPEN: Defer to next bar's open (Zipline-style)CLOSE_PRICE: Fill at bar's close (VectorBT Pro-style)
- New positions use close price as initial high-water mark
- Entry bar's high is NOT used (matches VectorBT Pro)
- High-water mark updates begin on the bar after entry
- Full OHLCV bars provide most accurate execution simulation
- Close-only data limits stop/limit order accuracy
- Volume data required for
ExecutionLimits
- Bar-based execution only
- Tick-by-tick order book simulation not available
- Use smaller bar intervals for higher precision
- Naive datetimes default to UTC
- Calendar sessions use exchange timezone
- Mixed timezones may cause unexpected behavior
- Exchange calendars enforce trading hours
enforce_sessions=Trueskips bars outside sessions- Overnight gaps handled when calendar is enabled
- Overnight sessions (e.g., CME equity futures) are supported
- Set
overnight=Truefor calendar types that trade overnight - Session boundaries respect next-day roll
- Fractional quantities supported by default
- Set
share_type=INTEGERfor whole shares only - Very small positions may cause rounding issues
- All prices stored as Python floats (IEEE 754 double)
- May introduce minor floating-point errors
- Not suitable for sub-penny precision requirements
The following real-world factors are not simulated:
- Broker API latency - Orders execute instantly
- Quote stuffing / HFT interference - Clean price discovery assumed
- Exchange halts / circuit breakers - All bars are tradeable
- Order cancellation delays - Cancels are instant
- Partial fills due to queue position - Limit orders fill completely or not at all
- Regulatory restrictions - PDT, accredited investor, etc.
- Tax implications - No wash sale tracking
- Currency conversion - Single currency assumed
| Framework | Scenarios | Status |
|---|---|---|
| VectorBT Pro | 16/16 | PASS (exact match) |
| VectorBT OSS | 16/16 | PASS (exact match) |
| Backtrader | 16/16 | PASS (exact match) |
| Zipline | 15/15 | PASS (exact match) |
Scenarios cover: long-only, long/short, stop-loss, take-profit, commission models, slippage models, trailing stops, bracket orders, short selling, rule combinations, and 1500-bar stress tests across 9 market regimes.
| Profile | Trades | Gap | Value Gap |
|---|---|---|---|
| zipline_strict | 225,583 | 0 trades (0.00%) | $19 (0.0001%) |
| backtrader_strict | 216,980 | 1 trade (0.0005%) | $503 (0.004%) |
| vectorbt_strict | 210,352 | 91 trades (0.04%) | $0 (0.00%) |
| lean | 428,459 fills | 0 fills (0.00%) | $1.55 (0.0002%) |
Core Execution (210k+ trades per profile on real data):
- Entry/exit timing and fill prices (open, close, stop price)
- Position tracking (long and short)
- P&L calculations and multi-asset portfolio management
- Cash constraints, margin, and buying power
Risk Rules (16 scenario tests per framework):
- Stop-loss and take-profit (long and short)
- Trailing stop with close-based HWM (long and short)
- Bracket orders (SL + TP)
- Rule combinations (TSL+TP, TSL+SL, TSL+TP+SL)
- 1500-bar stress test across 9 market regimes with gap events
- LEAN parity gap (+589 trades) under investigation (buying power reservation model)
- Per-share commission and volume-based slippage not cross-validated
- VWAP and MID execution prices not cross-validated
- Use
NEXT_BARexecution mode - Set
slippage_rate>= 0.001 (0.1%) - Set
commission_rate>= 0.001 (0.1%) - Enable
reject_on_insufficient_cash - Use exchange calendar for accurate session handling
- Use
SAME_BARmode for vectorized comparison with VectorBT - Use identical settings across all frameworks
- Focus on relative performance, not absolute returns
- Validate with
BacktestConfig.from_preset("realistic") - Run with historical crisis periods (2008, 2020, 2022)
- Test with varied slippage and commission assumptions
- Paper trade before live deployment