Skip to content

feat: Engine futures API and rebalancer multiplier fix - #4

Merged
stefan-jansen merged 7 commits into
mainfrom
feat/engine-futures-api
Mar 3, 2026
Merged

feat: Engine futures API and rebalancer multiplier fix#4
stefan-jansen merged 7 commits into
mainfrom
feat/engine-futures-api

Conversation

@stefan-jansen

Copy link
Copy Markdown
Contributor

Summary

  • Engine API: Engine.__init__, Engine.from_config, and run_backtest() now accept contract_specs, market_impact_model, and execution_limits as keyword-only params. Eliminates the two-step engine.broker = Broker.from_config(...) pattern for futures backtesting.
  • Top-level exports: ContractSpec, AssetClass, CommissionType now exported from ml4t.backtest (25 symbols total).
  • CommissionType.PER_CONTRACT: Enum alias for PER_SHARE — futures naming convention.
  • Auto margin schedule: Broker.__init__ auto-populates fixed_margin_schedule from ContractSpec.margin (initial=margin, maintenance=50%).
  • Rebalancer multiplier fix: 6 locations in TargetWeightExecutor now account for contract multipliers. Without this, ES futures computed 50x too many contracts.
  • Leverage cap redesign: Removed silent 1.0 total weight cap. New RebalanceConfig.max_gross_leverage (default None = no cap, gatekeeper decides). Removed [-1,1] cap in order_target_percent.
  • 15 new tests: tests/execution/test_rebalancer_futures.py covering multiplier wiring, margin auto-population, leverage behavior.

Test plan

  • 1,079 tests pass (3 pre-existing cross-engine failures, 5 skipped)
  • ruff check clean
  • ty check clean
  • 53 rebalancer tests pass (38 existing + 15 new futures tests)
  • CI validates on Python 3.11/3.12/3.13

…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)
Copilot AI review requested due to automatic review settings March 3, 2026 16:41

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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) to Engine and run_backtest() and pass through to Broker.from_config.
  • Fix TargetWeightExecutor sizing/weight math to account for contract multipliers; add a dedicated futures-focused test suite.
  • Introduce RebalanceConfig.max_gross_leverage and remove the previous implicit “scale to 1.0” behavior; export additional symbols at ml4t.backtest top-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.

Comment thread src/ml4t/backtest/config.py
Comment thread src/ml4t/backtest/broker.py
Comment thread src/ml4t/backtest/execution/rebalancer.py
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.
@stefan-jansen
stefan-jansen merged commit 8cbb333 into main Mar 3, 2026
7 checks passed
@stefan-jansen
stefan-jansen deleted the feat/engine-futures-api branch March 3, 2026 19:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants