feat: Engine futures API and rebalancer multiplier fix - #4
Merged
Conversation
…imits kwargs - Engine.__init__, Engine.from_config, and run_backtest() now accept contract_specs, market_impact_model, and execution_limits as keyword-only params, threaded through to Broker.from_config(). Eliminates the two-step engine.broker = Broker.from_config(...) pattern for futures backtesting. - Export ContractSpec, AssetClass, CommissionType at top level (25 symbols) - Add CommissionType.PER_CONTRACT as alias for PER_SHARE (futures convention) - Auto-populate fixed_margin_schedule from ContractSpec.margin in Broker.__init__ - Fix rebalancer to account for contract multiplier in 6 locations (weight calc, share sizing, preview). Without this, ES futures computed 50x too many contracts. - Replace silent 1.0 total weight cap with configurable max_gross_leverage on RebalanceConfig. Default None = no cap, gatekeeper decides. Removes [-1,1] cap in order_target_percent. - Add 15 futures-specific rebalancer tests (test_rebalancer_futures.py)
There was a problem hiding this comment.
Pull request overview
This PR extends the backtesting engine and execution layer to better support futures (contract specs, multipliers, margin wiring) while redesigning rebalancer leverage handling to remove a silent 1.0 weight cap and replace it with an explicit gross-leverage guardrail.
Changes:
- Add keyword-only futures/execution inputs (
contract_specs,market_impact_model,execution_limits) toEngineandrun_backtest()and pass through toBroker.from_config. - Fix
TargetWeightExecutorsizing/weight math to account for contract multipliers; add a dedicated futures-focused test suite. - Introduce
RebalanceConfig.max_gross_leverageand remove the previous implicit “scale to 1.0” behavior; export additional symbols atml4t.backtesttop-level.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
tests/execution/test_rebalancer_futures.py |
New tests validating multiplier-aware sizing/weights, margin schedule auto-population, and gross leverage behavior for futures. |
tests/execution/test_rebalancer.py |
Updates leverage-scaling expectations and adds a “no cap” behavior test. |
src/ml4t/backtest/execution/rebalancer.py |
Implements multiplier-aware sizing/valuation and adds configurable gross leverage cap logic. |
src/ml4t/backtest/engine.py |
Extends Engine/run_backtest APIs to accept and forward futures/execution dependencies into Broker construction. |
src/ml4t/backtest/config.py |
Adds CommissionType.PER_CONTRACT alias intended for futures terminology. |
src/ml4t/backtest/broker.py |
Auto-populates per-symbol fixed margin schedule from ContractSpec.margin; updates order_target_percent docs and removes [-1,1] cap. |
src/ml4t/backtest/__init__.py |
Exposes CommissionType, AssetClass, and ContractSpec at the package top-level. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Trailing stop trigger decoupling: - Trigger detection always uses bar extremes (bar_low for long, bar_high for short), regardless of trail_hwm_source setting - HWM source only affects how the trailing level tracks between bars - Removed dead _get_trail_hwm_source from dynamic.py VBT OSS driver (15/15 PASS): - Pass OHLC data to VBT Portfolio.from_signals() for intrabar stop checks - Previously only passed close, causing trailing stop mismatches Backtrader driver (16/16 PASS): - Rewrote _risk_entry_strategy with proper OCO order linking - Split exit submission: fixed exits (SL/TP) from signal close in next(), trailing stop deferred to notify_order after entry fills - Fixed short direction detection in ml4t_runner for risk_entry_only Zipline driver (12/15 PASS): - Added NYSE calendar alignment in run_scenario.py for signal/data sync - Added PerDollar commission and open-price slippage models - Skip commission comparison when framework doesn't provide data Scenario definitions expanded from 10 to 16 scenarios.
Top-level __init__.py now exports RebalanceConfig and TargetWeightExecutor, eliminating the need to import from ml4t.backtest.execution.rebalancer. Also updates cross-engine contract tests to use the consolidated validation runner (in-process instead of subprocess with separate venvs).
User guide additions: - stateful-strategies.md: 5 design patterns demonstrating event-driven value - market-impact.md: LinearImpact and SquareRootImpact documentation - Book cross-references added to all existing user guide pages README updated with validation parity status and clearer feature overview. Examples directory with 5 stateful strategy implementations and tests: - AdaptiveKellySizingStrategy, PyramidingStrategy, PairsTradingStrategy, DrawdownCircuitBreakerStrategy, GridTradingStrategy
Three fixes driving Zipline from 12/15 to 15/15 PASS: 1. TrailingStop defer_fill: Added NEXT_BAR_OPEN handling to dynamic.py. StopLoss/TakeProfit already deferred exits to next bar's open, but TrailingStop filled immediately on the trigger bar. Now all three rule types correctly defer when stop_fill_mode=NEXT_BAR_OPEN. 2. OHLC-aware risk rules in Zipline driver: TrailingStop now uses bar_high for HWM tracking and bar_low for trigger detection (long), matching ml4t's INTRABAR + BAR_EXTREME behavior. 3. Short direction detection in all drivers: Zipline and VBT OSS drivers now detect short from allow_short_selling + data_generator name, matching the ml4t_runner logic. Previously only handled strategy_type="short_only". Zipline ml4t config updated with trail_hwm_source=BAR_EXTREME and trail_stop_timing=INTRABAR for all trailing stop scenarios.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Engine.__init__,Engine.from_config, andrun_backtest()now acceptcontract_specs,market_impact_model, andexecution_limitsas keyword-only params. Eliminates the two-stepengine.broker = Broker.from_config(...)pattern for futures backtesting.ContractSpec,AssetClass,CommissionTypenow exported fromml4t.backtest(25 symbols total).PER_SHARE— futures naming convention.Broker.__init__auto-populatesfixed_margin_schedulefromContractSpec.margin(initial=margin, maintenance=50%).TargetWeightExecutornow account for contract multipliers. Without this, ES futures computed 50x too many contracts.RebalanceConfig.max_gross_leverage(default None = no cap, gatekeeper decides). Removed[-1,1]cap inorder_target_percent.tests/execution/test_rebalancer_futures.pycovering multiplier wiring, margin auto-population, leverage behavior.Test plan