Skip to main content

State-of-the-art event-driven backtesting engine for quantitative trading

Project description

ml4t-backtest

Python 3.11+ PyPI License: MIT

Event-driven backtesting engine for quantitative trading strategies with realistic execution modeling.

Part of the ML4T Library Ecosystem

This library is one of five interconnected libraries supporting the machine learning for trading workflow described in Machine Learning for Trading:

ML4T Library Ecosystem

Each library addresses a distinct stage: data infrastructure, feature engineering, signal evaluation, strategy backtesting, and live deployment.

What This Library Does

Backtesting requires accurate simulation of order execution, position tracking, and risk management. ml4t-backtest provides:

  • Event-driven architecture with point-in-time correctness (no look-ahead bias)
  • Exit-first order processing matching real broker behavior
  • Configurable execution modes (same-bar or next-bar fills)
  • Position-level risk rules (stop-loss, take-profit, trailing stops)
  • Portfolio-level constraints (max positions, drawdown limits)
  • Cash and margin account policies

The same Strategy class used in backtesting works unchanged in ml4t-live for production deployment.

ml4t-backtest Architecture

Installation

pip install ml4t-backtest

Quick Start

from ml4t.backtest import Engine, Strategy, BacktestConfig, DataFeed
from ml4t.backtest.risk import StopLoss, TakeProfit, RuleChain

class TrendFollowing(Strategy):
    def __init__(self, fast=10, slow=30):
        self.fast = fast
        self.slow = slow

    def on_data(self, timestamp, data, context, broker):
        close = data["close"]
        fast_ma = close.rolling(self.fast).mean().iloc[-1]
        slow_ma = close.rolling(self.slow).mean().iloc[-1]

        position = broker.get_position("SPY")

        if fast_ma > slow_ma and position is None:
            broker.submit_order("SPY", quantity=100, side="BUY")
        elif fast_ma < slow_ma and position is not None:
            broker.close_position("SPY")

config = BacktestConfig(
    initial_cash=100_000,
    commission_rate=0.001,
)

feed = DataFeed(price_data)
engine = Engine(feed, TrendFollowing(), config)
result = engine.run()

print(f"Total Return: {result.total_return:.2%}")
print(f"Sharpe Ratio: {result.metrics['sharpe_ratio']:.2f}")

Risk Management

Position-level exit rules:

from ml4t.backtest.risk import StopLoss, TakeProfit, TrailingStop, RuleChain

class MyStrategy(Strategy):
    def on_start(self, broker):
        broker.set_position_rules(RuleChain([
            StopLoss(pct=0.05),
            TakeProfit(pct=0.15),
            TrailingStop(pct=0.03),
        ]))

Portfolio-level controls:

from ml4t.backtest.risk import MaxPositions, MaxDrawdown, DailyLossLimit

Execution Modes

from ml4t.backtest import ExecutionMode, StopFillMode

# Same-bar fills (VectorBT style)
config = BacktestConfig(
    execution_mode=ExecutionMode.SAME_BAR,
    stop_fill_mode=StopFillMode.STOP_PRICE,
)

# Next-bar fills (Backtrader style)
config = BacktestConfig(
    execution_mode=ExecutionMode.NEXT_BAR,
    stop_fill_mode=StopFillMode.STOP_PRICE,
)

Commission and Slippage

from ml4t.backtest import PercentCommission, PercentSlippage

config = BacktestConfig(
    commission_model=PercentCommission(rate=0.001),
    slippage_model=PercentSlippage(rate=0.0005),
)

Multi-Asset Support

class RankingStrategy(Strategy):
    def on_data(self, timestamp, data, context, broker):
        returns = data["close"].pct_change(20)
        ranked = returns.iloc[-1].sort_values(ascending=False)

        # Long top 10
        for asset in ranked.head(10).index:
            if broker.get_position(asset) is None:
                broker.submit_order(asset, quantity=100, side="BUY")

Validation

The library is validated against VectorBT Pro, Backtrader, and Zipline:

  • 119,000+ trades verified trade-by-trade across frameworks
  • 500 assets x 10 years (2,520 bars) stress testing
  • 100% PnL match on common execution scenarios

See validation/README.md for test methodology.

Technical Characteristics

  • Event-driven: Each bar processes sequentially with exit-first logic
  • Point-in-time: No access to future data within strategy callbacks
  • Configurable fills: Match behavior of different backtesting frameworks
  • Parquet export: Results serializable for analysis with ml4t-diagnostic

Related Libraries

  • ml4t-data: Market data acquisition and storage
  • ml4t-engineer: Feature engineering and technical indicators
  • ml4t-diagnostic: Signal evaluation and statistical validation
  • ml4t-live: Live trading with broker integration

Development

git clone https://github.com/applied-ai/ml4t-backtest.git
cd ml4t-backtest
uv sync
uv run pytest tests/ -q
uv run ty check

Known Limitations

See LIMITATIONS.md for documented assumptions:

  • Partial fills not supported (all-or-nothing)
  • No intrabar stop simulation (uses bar OHLC)
  • Calendar overnight sessions require configuration

License

MIT License - see LICENSE for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ml4t_backtest-0.1.0a7.tar.gz (223.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ml4t_backtest-0.1.0a7-py3-none-any.whl (141.0 kB view details)

Uploaded Python 3

File details

Details for the file ml4t_backtest-0.1.0a7.tar.gz.

File metadata

  • Download URL: ml4t_backtest-0.1.0a7.tar.gz
  • Upload date:
  • Size: 223.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for ml4t_backtest-0.1.0a7.tar.gz
Algorithm Hash digest
SHA256 8382b1c945b4a69949f7718dfab0e41c3b3d4d7f6b46ff34b1e9d7f5c2b5fa22
MD5 13c58301de0a8f472b425f7688c22260
BLAKE2b-256 54d19eed57154f2eb8bc9fa7775fe37830d54a661e13b4b880fdd52bb1e18fa7

See more details on using hashes here.

File details

Details for the file ml4t_backtest-0.1.0a7-py3-none-any.whl.

File metadata

File hashes

Hashes for ml4t_backtest-0.1.0a7-py3-none-any.whl
Algorithm Hash digest
SHA256 12af18d36e426383f65e451804c4d20533c2008838144920ebce1e262ac049c8
MD5 e5c92b266898f7d00811ff3389391010
BLAKE2b-256 59e5fa68aa62ac3c13fb8273f5be32f935df2ba58d5976a496b072e628ba1ba5

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page