API Reference¶
Auto-generated from source docstrings.
Core¶
Engine
¶
Engine(
feed,
strategy,
config=None,
*,
contract_specs=None,
market_impact_model=None,
execution_limits=None,
)
Event-driven backtesting engine.
The Engine orchestrates the backtest by iterating through market data, managing the broker, and calling the strategy on each bar.
Execution Flow
- Initialize strategy (on_start)
- For each bar: a. Update broker with current prices b. Process pending exits (NEXT_BAR_OPEN mode) c. Evaluate position rules (stops, trails) d. Process pending orders e. Call strategy.on_data() f. Process new orders (SAME_BAR mode) g. Update water marks h. Record equity
- Close open positions
- Finalize strategy (on_end)
Attributes:
| Name | Type | Description |
|---|---|---|
feed |
DataFeed providing price and signal data |
|
strategy |
Strategy implementing trading logic |
|
broker |
Broker handling order execution and positions |
|
config |
BacktestConfig with all behavioral settings |
|
equity_curve |
list[tuple[datetime, float]]
|
List of (timestamp, equity) tuples |
Example
from ml4t.backtest import Engine, DataFeed, Strategy, BacktestConfig
class MyStrategy(Strategy): ... def on_data(self, timestamp, data, context, broker): ... for asset, bar in data.items(): ... if bar.get('signal', 0) > 0.5: ... broker.submit_order(asset, 100)
feed = DataFeed(prices_df=df) engine = Engine(feed=feed, strategy=MyStrategy()) result = engine.run() print(result['total_return'])
Source code in src/ml4t/backtest/engine.py
run
¶
Run backtest and return structured results.
Returns:
| Type | Description |
|---|---|
BacktestResult
|
BacktestResult with trades, equity curve, metrics, and export methods. |
BacktestResult
|
Call .to_dict() for backward-compatible dictionary output. |
Source code in src/ml4t/backtest/engine.py
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 | |
run_dict
¶
Run backtest and return dictionary (backward compatible).
This is equivalent to run().to_dict() but more explicit for code that requires dictionary output.
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Dictionary with metrics, trades, and equity curve. |
Source code in src/ml4t/backtest/engine.py
from_config
classmethod
¶
from_config(
feed,
strategy,
config,
*,
contract_specs=None,
market_impact_model=None,
execution_limits=None,
)
Create an Engine instance from a BacktestConfig.
Equivalent to Engine(feed, strategy, config). Kept as a convenience
for code that reads more clearly with a named constructor.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
feed
|
DataFeed
|
DataFeed with price data |
required |
strategy
|
Strategy
|
Strategy to execute |
required |
config
|
BacktestConfig
|
BacktestConfig with all behavioral settings |
required |
contract_specs
|
dict[str, Any] | None
|
Per-asset contract specifications (futures multipliers, etc.) |
None
|
market_impact_model
|
Any | None
|
Market impact model for fill simulation |
None
|
execution_limits
|
Any | None
|
Execution limits (max order size, etc.) |
None
|
Returns:
| Type | Description |
|---|---|
Engine
|
Configured Engine instance |
Source code in src/ml4t/backtest/engine.py
run_backtest
¶
run_backtest(
prices,
strategy,
signals=None,
context=None,
config=None,
*,
feed_spec=None,
contract=None,
contract_specs=None,
market_impact_model=None,
execution_limits=None,
)
Run a backtest with minimal setup.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
prices
|
DataFrame | str
|
Price DataFrame or path to parquet file |
required |
strategy
|
Strategy
|
Strategy instance to execute |
required |
signals
|
DataFrame | str | None
|
Optional signals DataFrame or path |
None
|
context
|
DataFrame | str | None
|
Optional context DataFrame or path |
None
|
config
|
BacktestConfig | str | None
|
BacktestConfig instance, preset name (str), or None for defaults |
None
|
feed_spec
|
Any | None
|
Optional shared dataset contract for schema and temporal metadata |
None
|
contract
|
Any | None
|
Alias for feed_spec |
None
|
contract_specs
|
dict[str, Any] | None
|
Per-asset contract specifications (futures multipliers, etc.) |
None
|
market_impact_model
|
Any | None
|
Market impact model for fill simulation |
None
|
execution_limits
|
Any | None
|
Execution limits (max order size, etc.) |
None
|
Returns:
| Type | Description |
|---|---|
BacktestResult
|
BacktestResult with metrics, trades, equity curve, and export methods. |
Example
Using config preset¶
result = run_backtest(prices_df, strategy, config="backtrader") print(result.metrics["sharpe"])
Using custom config¶
config = BacktestConfig.from_preset("backtrader") config.commission_rate = 0.002 result = run_backtest(prices_df, strategy, config=config)
Futures with contract specs¶
from ml4t.backtest import ContractSpec, AssetClass specs = {"ES": ContractSpec(symbol="ES", asset_class=AssetClass.FUTURE, multiplier=50.0)} result = run_backtest(prices_df, strategy, config=config, contract_specs=specs)
Source code in src/ml4t/backtest/engine.py
Strategy
¶
Bases: ABC
Base strategy class.
DataFeed
¶
DataFeed(
prices_path=None,
signals_path=None,
context_path=None,
prices_df=None,
signals_df=None,
context_df=None,
*,
feed_spec=None,
contract=None,
entity_col=None,
timestamp_col=None,
price_col=None,
open_col=None,
high_col=None,
low_col=None,
close_col=None,
volume_col=None,
bid_col=None,
ask_col=None,
mid_col=None,
bid_size_col=None,
ask_size_col=None,
)
Polars-based multi-asset data feed with signals and context.
Pre-indexes data by timestamp at initialization for O(1) lookups during
iteration. Public prices, signals, and context attributes are
normalized copies sorted by timestamp when needed. DataFrames are kept in
their native format and converted to dicts only for the active bar,
avoiding the large memory overhead of materializing one child DataFrame per
timestamp.
Memory Efficiency
- 1M bars: ~100 MB (was ~1 GB with pre-converted dicts)
- 10M bars: ~1 GB (vs ~10+ GB with dicts)
Usage
feed = DataFeed(prices_df=prices, signals_df=signals) for timestamp, assets_data, context in feed: # assets_data: {"AAPL": {"close": 150.0, "signals": {...}}, ...} process(timestamp, assets_data)
Source code in src/ml4t/backtest/datafeed.py
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | |
Configuration¶
BacktestConfig
dataclass
¶
BacktestConfig(
allow_short_selling=False,
allow_leverage=False,
initial_margin=0.5,
long_maintenance_margin=0.25,
short_maintenance_margin=0.3,
fixed_margin_schedule=None,
margin_pct_schedule=None,
short_cash_policy=CREDIT,
execution_price=OPEN,
mark_price=PRICE,
execution_mode=NEXT_BAR,
stop_fill_mode=STOP_PRICE,
stop_level_basis=FILL_PRICE,
trail_hwm_source=CLOSE,
trail_include_entry_bar_extremes=False,
initial_hwm_source=FILL_PRICE,
trail_stop_timing=LAGGED,
share_type=INTEGER,
commission_type=NONE,
commission_rate=0.0,
commission_per_share=0.0,
commission_per_trade=0.0,
commission_minimum=0.0,
slippage_type=NONE,
slippage_rate=0.0,
slippage_fixed=0.0,
slippage_spread=0.0,
slippage_spread_by_asset=dict(),
slippage_spread_convention=FULL_SPREAD,
stop_slippage_rate=0.0,
initial_cash=100000.0,
cash_buffer_pct=0.0,
settlement_delay=0,
settlement_reduces_buying_power=True,
reject_on_insufficient_cash=True,
skip_cash_validation=False,
partial_fills_allowed=False,
fill_ordering=EXIT_FIRST,
entry_order_priority=SUBMISSION,
next_bar_submission_precheck=False,
next_bar_simple_cash_check=False,
buying_power_reservation=False,
next_bar_queue_shadow_validation=False,
immediate_fill=False,
rebalance_mode=INCREMENTAL,
rebalance_headroom_pct=1.0,
missing_price_policy=SKIP,
late_asset_policy=ALLOW,
late_asset_min_bars=1,
calendar=None,
timezone="UTC",
data_frequency=DAILY,
enforce_sessions=False,
preset_name=None,
feed_spec=None,
metadata=dict(),
)
Complete configuration for backtesting behavior.
All behavioral differences between frameworks are captured here. Load presets to match specific frameworks exactly.
This is the single source of truth for all backtest settings. Broker and Engine are configured entirely from this dataclass.
from_preset
classmethod
¶
Load a predefined configuration preset.
Available presets: - "default": Sensible defaults for general use - "backtrader": Match Backtrader's default behavior - "vectorbt": Match VectorBT's default behavior - "zipline": Match Zipline's default behavior - "lean": Match QuantConnect LEAN's default behavior - "realistic": Conservative settings for realistic simulation - "ibkr_us_stocks_fixed": Interactive Brokers US stocks fixed pricing
Source code in src/ml4t/backtest/config.py
from_yaml
classmethod
¶
Load config from YAML file.
Source code in src/ml4t/backtest/config.py
from_dict
classmethod
¶
Create config from dictionary.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
dict
|
Nested config dictionary |
required |
preset_name
|
str | None
|
Optional metadata label |
None
|
strict
|
bool
|
If True, reject unknown sections/keys |
True
|
Source code in src/ml4t/backtest/config.py
902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 | |
to_yaml
¶
to_dict
¶
Convert config to dictionary for serialization.
Source code in src/ml4t/backtest/config.py
822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 | |
validate
¶
Validate configuration and return warnings for edge cases.
Checks for configurations that may produce unexpected results or indicate potential issues. Returns a list of warning messages.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
warn
|
bool
|
If True, emit warnings via warnings.warn(). Default True. |
True
|
Returns:
| Type | Description |
|---|---|
list[str]
|
List of warning message strings (empty if no issues found). |
Example
config = BacktestConfig(execution_mode=ExecutionMode.SAME_BAR) warnings = config.validate()
["SAME_BAR execution has look-ahead bias risk..."]¶
Source code in src/ml4t/backtest/config.py
519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 | |
describe
¶
Return human-readable description of configuration.
Source code in src/ml4t/backtest/config.py
1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 | |
profiles
¶
Centralized profile definitions for framework-aligned behavior.
get_profile_config
¶
Return a deep copy of nested config data for the named profile.
Source code in src/ml4t/backtest/profiles.py
Broker¶
Broker
¶
Broker(
initial_cash=100000.0,
commission_model=None,
slippage_model=None,
stop_slippage_rate=0.0,
execution_mode=SAME_BAR,
execution_price=CLOSE,
mark_price=PRICE,
stop_fill_mode=STOP_PRICE,
stop_level_basis=FILL_PRICE,
trail_hwm_source=CLOSE,
trail_include_entry_bar_extremes=False,
initial_hwm_source=FILL_PRICE,
trail_stop_timing=LAGGED,
allow_short_selling=False,
allow_leverage=False,
initial_margin=0.5,
long_maintenance_margin=0.25,
short_maintenance_margin=0.3,
fixed_margin_schedule=None,
margin_pct_schedule=None,
short_cash_policy=CREDIT,
execution_limits=None,
market_impact_model=None,
contract_specs=None,
share_type=INTEGER,
fill_ordering=EXIT_FIRST,
entry_order_priority=SUBMISSION,
next_bar_submission_precheck=False,
next_bar_simple_cash_check=False,
buying_power_reservation=False,
next_bar_queue_shadow_validation=False,
immediate_fill=False,
reject_on_insufficient_cash=True,
skip_cash_validation=False,
cash_buffer_pct=0.0,
partial_fills_allowed=False,
rebalance_headroom_pct=1.0,
missing_price_policy=SKIP,
late_asset_policy=ALLOW,
late_asset_min_bars=1,
settlement_delay=0,
settlement_reduces_buying_power=True,
)
Broker interface - same for backtest and live trading.
Source code in src/ml4t/backtest/broker.py
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 | |
submit_order
¶
submit_order(
asset,
quantity,
side=None,
order_type=MARKET,
limit_price=None,
stop_price=None,
trail_amount=None,
_options=None,
)
Submit a new order to the broker.
Creates and queues an order for execution. Orders are validated by the Gatekeeper before fills to ensure account constraints are met.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
asset
|
str
|
Asset symbol (e.g., "AAPL", "BTC-USD") |
required |
quantity
|
float
|
Number of shares/units. Positive = buy, negative = sell (if side is not specified) |
required |
side
|
OrderSide | None
|
OrderSide.BUY or OrderSide.SELL. If None, inferred from quantity sign |
None
|
order_type
|
OrderType
|
Order type (MARKET, LIMIT, STOP, TRAILING_STOP) |
MARKET
|
limit_price
|
float | None
|
Limit price for LIMIT orders |
None
|
stop_price
|
float | None
|
Stop/trigger price for STOP orders |
None
|
trail_amount
|
float | None
|
Trail distance for TRAILING_STOP orders |
None
|
Returns:
| Type | Description |
|---|---|
Order | None
|
Order object if submitted successfully, None if rejected |
Order | None
|
(e.g., same-bar re-entry after stop exit in VBT Pro mode) |
Examples:
Market buy¶
order = broker.submit_order("AAPL", 100)
Market sell (using negative quantity)¶
order = broker.submit_order("AAPL", -100)
Limit buy¶
order = broker.submit_order("AAPL", 100, order_type=OrderType.LIMIT, limit_price=150.0)
Stop sell (stop-loss)¶
order = broker.submit_order("AAPL", -100, order_type=OrderType.STOP, stop_price=145.0)
Source code in src/ml4t/backtest/broker.py
submit_bracket
¶
submit_bracket(
asset,
quantity,
take_profit,
stop_loss,
entry_type=MARKET,
entry_limit=None,
validate_prices=True,
)
Submit entry with take-profit and stop-loss.
Creates a bracket order with entry, take-profit limit, and stop-loss orders. The exit side is automatically determined from the entry direction.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
asset
|
str
|
Asset symbol to trade |
required |
quantity
|
float
|
Position size (positive for long, negative for short) |
required |
take_profit
|
float
|
Take-profit price level (LIMIT order) |
required |
stop_loss
|
float
|
Stop-loss price level (STOP order) |
required |
entry_type
|
OrderType
|
Entry order type (default MARKET) |
MARKET
|
entry_limit
|
float | None
|
Entry limit price (if entry_type is LIMIT) |
None
|
validate_prices
|
bool
|
If True, validate that TP/SL prices are sensible for the position direction (default True) |
True
|
Returns:
| Type | Description |
|---|---|
tuple[Order, Order, Order] | None
|
Tuple of (entry_order, take_profit_order, stop_loss_order) or None if any fails. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If validate_prices=True and prices are inverted for direction. |
Notes
For LONG entries (quantity > 0): - take_profit should be > reference_price (profit on up move) - stop_loss should be < reference_price (exit on down move)
For SHORT entries (quantity < 0): - take_profit should be < reference_price (profit on down move) - stop_loss should be > reference_price (exit on up move)
Reference price is entry_limit (if LIMIT order) or current market price.
Source code in src/ml4t/backtest/broker.py
869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 | |
close_position
¶
Close an open position for the given asset.
Submits an order to fully close the position.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
asset
|
str
|
Asset symbol to close |
required |
order_type
|
OrderType
|
Exit order type (default MARKET). Use |
MARKET
|
Returns:
| Type | Description |
|---|---|
Order | None
|
Order object if position exists and order submitted, None otherwise |
Example
Close AAPL position¶
order = broker.close_position("AAPL")
Flatten at the bar close¶
order = broker.close_position("AAPL", order_type=OrderType.MOC)
Source code in src/ml4t/backtest/broker.py
cancel_order
¶
get_position
¶
Get the current position for an asset.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
asset
|
str
|
Asset symbol |
required |
Returns:
| Type | Description |
|---|---|
Position | None
|
Position object if position exists, None otherwise |
Source code in src/ml4t/backtest/broker.py
get_positions
¶
Get all current positions.
Returns:
| Type | Description |
|---|---|
dict[str, Position]
|
Dictionary mapping asset symbols to Position objects |
get_cash
¶
Get current cash balance.
Returns:
| Type | Description |
|---|---|
float
|
Current cash balance (can be negative for margin accounts) |
equity
¶
get_account_value
¶
get_rejected_orders
¶
Get all rejected orders, optionally filtered by asset.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
asset
|
str | None
|
If provided, filter to only this asset's rejected orders |
None
|
Returns:
| Type | Description |
|---|---|
list[Order]
|
List of rejected Order objects with rejection_reason populated |
Source code in src/ml4t/backtest/broker.py
set_position_rules
¶
Set position rules globally or per-asset.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rules
|
PositionRule | None
|
PositionRule or RuleChain to apply. |
required |
asset
|
str | None
|
If provided, apply only to this asset; otherwise global |
None
|
Source code in src/ml4t/backtest/broker.py
rebalance_to_weights
¶
Rebalance portfolio to target weights.
Calculates orders needed to achieve target portfolio allocation. Processes sells before buys to free up capital.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target_weights
|
dict[str, float]
|
Dict of {asset: weight} where weights are decimals (0.10 = 10%). Weights should sum to <= 1.0. |
required |
order_type
|
OrderType
|
Order type for all orders (default MARKET) |
MARKET
|
Returns:
| Type | Description |
|---|---|
list[Order]
|
List of submitted orders (may include None for rejected orders) |
Example
Equal weight three stocks¶
broker.rebalance_to_weights({ "AAPL": 0.33, "GOOGL": 0.33, "MSFT": 0.34, })
Source code in src/ml4t/backtest/broker.py
1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 | |
Domain Types¶
Order
dataclass
¶
Order(
asset,
side,
quantity,
order_type=MARKET,
limit_price=None,
stop_price=None,
trail_amount=None,
parent_id=None,
rebalance_id=None,
order_id="",
status=PENDING,
created_at=None,
filled_at=None,
filled_price=None,
filled_quantity=0.0,
rejection_reason=None,
_created_bar_index=0,
_signal_price=None,
_risk_exit_reason=None,
_exit_reason=None,
_risk_fill_price=None,
)
Fill
dataclass
¶
Fill(
order_id,
asset,
side,
quantity,
price,
timestamp,
rebalance_id=None,
commission=0.0,
slippage=0.0,
order_type="",
limit_price=None,
stop_price=None,
price_source="",
reference_price=None,
quote_mid_price=None,
bid_price=None,
ask_price=None,
spread=None,
bid_size=None,
ask_size=None,
available_size=None,
)
Trade
dataclass
¶
Trade(
symbol,
entry_time,
exit_time,
entry_price,
exit_price,
quantity,
pnl,
pnl_percent,
bars_held,
fees=0.0,
exit_slippage=0.0,
exit_reason="signal",
status="closed",
mfe=0.0,
mae=0.0,
entry_slippage=0.0,
multiplier=1.0,
entry_quote_mid_price=None,
entry_bid_price=None,
entry_ask_price=None,
entry_spread=None,
entry_available_size=None,
exit_quote_mid_price=None,
exit_bid_price=None,
exit_ask_price=None,
exit_spread=None,
exit_available_size=None,
metadata=None,
)
Round-trip trade (closed or open).
This dataclass is part of the cross-library API specification, designed to produce identical Parquet output across Python, Numba, and Rust implementations.
For open trades (status="open"), exit_time and exit_price represent mark-to-market values at the end of the backtest period.
Schema Alignment (v0.1.0a6): - symbol: Asset identifier (was 'asset' in earlier versions) - fees: Total transaction fees (was 'commission') - mfe/mae: Max favorable/adverse excursion (was 'max_favorable_excursion'/'max_adverse_excursion') - direction: Derived property from quantity sign
cost_drag
property
¶
Total cost as fraction of notional: (fees + total slippage cost) / notional.
Position
dataclass
¶
Position(
asset,
quantity,
entry_price,
entry_time,
current_price=None,
bars_held=0,
high_water_mark=None,
low_water_mark=None,
max_favorable_excursion=0.0,
max_adverse_excursion=0.0,
initial_quantity=None,
context=dict(),
multiplier=1.0,
entry_commission=0.0,
entry_slippage=0.0,
)
Unified position tracking for strategy and accounting.
Supports both long and short positions with: - Weighted average cost basis tracking - Mark-to-market price tracking - Risk metrics (MFE/MAE, water marks) - Contract multipliers for futures
Attributes:
| Name | Type | Description |
|---|---|---|
asset |
str
|
Asset identifier (e.g., "AAPL", "ES") |
quantity |
float
|
Position size (positive=long, negative=short) |
entry_price |
float
|
Weighted average entry price (cost basis) |
entry_time |
datetime
|
Timestamp when position was first opened |
current_price |
float | None
|
Latest mark-to-market price (updated each bar) |
bars_held |
int
|
Number of bars this position has been held |
Examples:
Long position: Position("AAPL", 100, 150.0, datetime.now()) -> quantity=100, unrealized_pnl depends on current_price
Short position: Position("AAPL", -100, 150.0, datetime.now()) -> quantity=-100, profit if price drops
market_value
property
¶
Current market value of the position.
For long positions: positive value (asset on balance sheet) For short positions: negative value (liability on balance sheet)
Returns:
| Type | Description |
|---|---|
float
|
Market value = quantity × current_price |
unrealized_pnl
¶
Calculate unrealized P&L including contract multiplier.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
current_price
|
float | None
|
Price to calculate P&L at. If None, uses self.current_price. |
None
|
Returns:
| Type | Description |
|---|---|
float
|
Unrealized P&L = (current_price - entry_price) × quantity × multiplier |
Source code in src/ml4t/backtest/types.py
pnl_percent
¶
Calculate direction-aware percentage return on position.
For long positions: (price - entry) / entry For short positions: (entry - price) / entry
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
current_price
|
float | None
|
Price to calculate return at. If None, uses self.current_price. |
None
|
Source code in src/ml4t/backtest/types.py
notional_value
¶
Calculate notional value of position.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
current_price
|
float | None
|
Price to calculate value at. If None, uses self.current_price. |
None
|
Source code in src/ml4t/backtest/types.py
update_water_marks
¶
update_water_marks(
current_price,
bar_high=None,
bar_low=None,
use_high_for_hwm=False,
use_low_for_lwm=False,
)
Update high/low water marks and excursion tracking.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
current_price
|
float
|
Current bar's close price |
required |
bar_high
|
float | None
|
Bar's high price (used for HWM if use_high_for_hwm=True) |
None
|
bar_low
|
float | None
|
Bar's low price (used for LWM if use_low_for_lwm=True) |
None
|
use_high_for_hwm
|
bool
|
If True, use bar_high for HWM (VBT Pro OHLC mode). If False, use current_price (close) for HWM (default). |
False
|
use_low_for_lwm
|
bool
|
If True, use bar_low for LWM (VBT Pro OHLC mode). If False, use current_price (close) for LWM (default). |
False
|
Source code in src/ml4t/backtest/types.py
__repr__
¶
String representation for debugging.
Source code in src/ml4t/backtest/types.py
Enums¶
OrderType
¶
Bases: Enum
OrderSide
¶
Bases: Enum
ExecutionMode
¶
Bases: str, Enum
Order execution timing mode.
StopFillMode
¶
Bases: str, Enum
Stop/take-profit fill price mode.
Different frameworks handle stop order fills differently: - STOP_PRICE: Fill at exact stop/target price (standard model, default) Matches VectorBT Pro with OHLC and Backtrader behavior - CLOSE_PRICE: Fill at bar's close price when stop triggers Matches VectorBT Pro with close-only data - BAR_EXTREME: Fill at bar's low (stop-loss) or high (take-profit) Worst/best case model (conservative/optimistic) - NEXT_BAR_OPEN: Fill at next bar's open price when stop triggers Matches Zipline behavior (strategy-level stops)
CommissionType
¶
Bases: str, Enum
Commission calculation method.
SlippageType
¶
Bases: str, Enum
Slippage calculation method.
FillOrdering
¶
Bases: str, Enum
Order processing sequence within a single bar.
Controls how pending orders are sequenced during fill processing:
EXIT_FIRST (default):
All exits → mark-to-market → all entries (with gatekeeper validation).
Capital-efficient: exits free cash before entries need it.
Matches VectorBT call_seq='auto' behavior.
FIFO
Orders process in submission order with sequential cash updates. Each order's gatekeeper check sees cash from all prior fills. Matches Backtrader's submission-order processing.
SEQUENTIAL
Orders process in submission order (typically alphabetical by asset) without exit/entry separation. Cash updates after each individual fill. Unlike EXIT_FIRST, exits do not pre-free cash for later entries. Matches LEAN's per-order sequential buying-power model.
Results¶
BacktestResult
dataclass
¶
BacktestResult(
trades,
equity_curve,
fills,
metrics,
predictions=None,
config=None,
equity=None,
trade_analyzer=None,
portfolio_state=list(),
_trades_df=None,
_equity_df=None,
_fills_df=None,
_portfolio_state_df=None,
)
Structured backtest result with export capabilities.
This class wraps the raw output from Engine.run() and provides: - DataFrame conversion methods (trades, equity, daily P&L) - Parquet export/import for persistence - Integration with ml4t.diagnostic library - Backward-compatible dict export
Attributes:
| Name | Type | Description |
|---|---|---|
trades |
list[Trade]
|
List of completed Trade objects |
equity_curve |
list[tuple[datetime, float]]
|
List of (timestamp, portfolio_value) tuples |
fills |
list[Fill]
|
List of Fill objects (all order fills) |
predictions |
DataFrame | None
|
Raw prediction DataFrame passed into the backtest (optional) |
metrics |
dict[str, Any]
|
Dictionary of computed performance metrics |
config |
BacktestConfig | None
|
BacktestConfig used for the backtest (optional) |
equity |
EquityCurve | None
|
EquityCurve analytics object |
trade_analyzer |
TradeAnalyzer | None
|
TradeAnalyzer analytics object |
to_fills_dataframe
¶
Convert fills to Polars DataFrame.
Source code in src/ml4t/backtest/result.py
to_portfolio_state_dataframe
¶
Convert portfolio state snapshots to Polars DataFrame.
Returns DataFrame with columns
timestamp, equity, cash, gross_exposure, net_exposure, open_positions
Returns:
| Type | Description |
|---|---|
DataFrame
|
Polars DataFrame with one row per bar, sorted by timestamp |
Source code in src/ml4t/backtest/result.py
to_predictions_dataframe
¶
Return the raw prediction DataFrame used as backtest input.
to_trades_dataframe
¶
Convert trades to Polars DataFrame.
Returns DataFrame with columns
symbol, entry_time, exit_time, entry_price, exit_price, quantity, direction, pnl, pnl_percent, bars_held, fees, exit_slippage, mfe, mae, entry_slippage, multiplier, gross_pnl, net_return, total_slippage_cost, cost_drag, exit_reason, status
Cost decomposition columns
gross_pnl: Price-move P&L before fees net_return: Direction-aware net return including fees total_slippage_cost: Entry + exit slippage in dollars cost_drag: Total cost as fraction of notional
The status column indicates "closed" (actually exited) or "open" (mark-to-market at end of backtest).
Returns:
| Type | Description |
|---|---|
DataFrame
|
Polars DataFrame with one row per trade |
Source code in src/ml4t/backtest/result.py
to_equity_dataframe
¶
Convert equity curve to Polars DataFrame.
Returns DataFrame with columns
timestamp, equity, return, cumulative_return, drawdown, high_water_mark
Returns:
| Type | Description |
|---|---|
DataFrame
|
Polars DataFrame with one row per bar, sorted by timestamp |
Source code in src/ml4t/backtest/result.py
to_dict
¶
Export as dictionary (backward compatible with Engine.run()).
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Dictionary with all metrics and raw data |
Source code in src/ml4t/backtest/result.py
to_parquet
¶
Export backtest result to Parquet files.
Creates directory structure
{path}/ trades.parquet fills.parquet predictions.parquet equity.parquet portfolio_state.parquet daily_pnl.parquet metrics.json config.yaml (if config available) spec.yaml (if config available)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | Path
|
Directory path to write files |
required |
include
|
list[str] | None
|
Components to include. Default: all. Options: ["trades", "fills", "predictions", "equity", "portfolio_state", "daily_pnl", "metrics", "config", "spec"] |
None
|
compression
|
Literal['lz4', 'uncompressed', 'snappy', 'gzip', 'brotli', 'zstd']
|
Parquet compression codec (default: "zstd") |
'zstd'
|
Returns:
| Type | Description |
|---|---|
dict[str, Path]
|
Dict mapping component names to file paths |
Source code in src/ml4t/backtest/result.py
505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 | |
Execution: Market Impact¶
LinearImpact
dataclass
¶
Bases: MarketImpactModel
Linear market impact model.
Impact = coefficient * (quantity / volume) * price
Simple model where impact scales linearly with participation rate. Appropriate for liquid markets with moderate order sizes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
coefficient
|
float
|
Impact scaling factor (default 0.1) Higher values = more impact per unit participation |
0.1
|
permanent_fraction
|
float
|
Fraction of impact that is permanent (0-1) Remainder is temporary and reverts |
0.5
|
calculate
¶
Calculate linear impact.
Source code in src/ml4t/backtest/execution/impact.py
SquareRootImpact
dataclass
¶
Bases: MarketImpactModel
Square root market impact model (Almgren-Chriss style).
Impact = coefficient * sigma * sqrt(quantity / ADV) * price
Based on academic market microstructure research. Impact scales with the square root of order size, which matches empirical observations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
coefficient
|
float
|
Scaling factor (default 0.5, typical range 0.1-1.0) |
0.5
|
volatility
|
float
|
Daily volatility (sigma, default 0.02 = 2%) |
0.02
|
adv_factor
|
float
|
Average daily volume as multiple of bar volume (default 1.0 for daily bars, 390 for minute bars) |
1.0
|
Example
model = SquareRootImpact(coefficient=0.5, volatility=0.02)
For order = 1% of ADV at 2% vol, $100 price:¶
Impact = 0.5 * 0.02 * sqrt(0.01) * 100 = $0.10¶
calculate
¶
Calculate square root impact.
Source code in src/ml4t/backtest/execution/impact.py
PowerLawImpact
dataclass
¶
Bases: MarketImpactModel
Generalized power law impact model.
Impact = coefficient * (quantity / volume)^exponent * price
Flexible model that can represent various impact regimes. - exponent = 1.0: Linear (like LinearImpact) - exponent = 0.5: Square root (like SquareRootImpact) - exponent < 0.5: Concave (impact flattens for large orders) - exponent > 1.0: Convex (impact accelerates for large orders)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
coefficient
|
float
|
Scaling factor (default 0.1) |
0.1
|
exponent
|
float
|
Power law exponent (default 0.5) |
0.5
|
min_impact
|
float
|
Minimum impact per trade (fixed cost, default 0) |
0.0
|
Example
model = PowerLawImpact(coefficient=0.1, exponent=0.6)
calculate
¶
Calculate power law impact.
Source code in src/ml4t/backtest/execution/impact.py
Risk: Position Rules¶
StopLoss
dataclass
¶
Exit when stop price is breached during the bar.
Stop orders trigger when the bar's price range touches the stop level. Fill price depends on StopFillMode configuration: - STOP_PRICE: Fill at exact stop price (standard model, default) - BAR_EXTREME: Fill at bar's low (matches VectorBT Pro behavior)
For long positions: stop triggers if bar_low <= stop_price For short positions: stop triggers if bar_high >= stop_price
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pct
|
float
|
Maximum loss as decimal (0.05 = 5% loss triggers exit) |
required |
Example
rule = StopLoss(pct=0.05) # Exit at -5%
evaluate
¶
Exit if stop price was breached during the bar.
Source code in src/ml4t/backtest/risk/position/static.py
TakeProfit
dataclass
¶
Exit when target price is reached during the bar.
Take-profit orders trigger when the bar's price range touches the target. Fill price depends on StopFillMode configuration: - STOP_PRICE: Fill at exact target price (standard model, default) - BAR_EXTREME: Fill at bar's high (matches VectorBT Pro behavior)
For long positions: triggers if bar_high >= target_price For short positions: triggers if bar_low <= target_price
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pct
|
float
|
Target profit as decimal (0.10 = 10% profit triggers exit) |
required |
Example
rule = TakeProfit(pct=0.10) # Exit at +10%
evaluate
¶
Exit if target price was reached during the bar.
Source code in src/ml4t/backtest/risk/position/static.py
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | |
TimeExit
dataclass
¶
Exit after holding for a specified number of bars.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
max_bars
|
int
|
Maximum bars to hold position |
required |
Example
rule = TimeExit(max_bars=20) # Exit after 20 bars
evaluate
¶
Exit if held too long.
Source code in src/ml4t/backtest/risk/position/static.py
TrailingStop
dataclass
¶
Exit when price retraces from high water mark.
For longs: Exit if price drops X% from highest price since entry For shorts: Exit if price rises X% from lowest price since entry
Fill price depends on StopFillMode configuration: - STOP_PRICE: Fill at exact trail level (default) - CLOSE_PRICE: Fill at bar's close price (VBT Pro behavior)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pct
|
float
|
Trail percentage as decimal (0.05 = 5% trail) |
required |
Example
rule = TrailingStop(pct=0.05) # 5% trailing stop
evaluate
¶
Exit if price retraces beyond trail.
Uses bar_low/bar_high for intrabar trigger detection. Handles gap-through: if bar opens beyond stop level, fill at open.
Fill price depends on StopFillMode configuration: - STOP_PRICE: Fill at exact trail level (default) - CLOSE_PRICE: Fill at bar's close price - BAR_EXTREME: Fill at bar's low (long) or high (short)
Water mark timing depends on TrailStopTiming configuration: - LAGGED: Use water mark from PREVIOUS bar (default, 1-bar lag) - INTRABAR: Compute "live" water mark using current bar's extreme, then check. VBT Pro compatible: respects StopFillMode for fill price.
Gap-through handling: When bar opens beyond the stop level (gap down for longs, gap up for shorts), the fill is at the open price regardless of StopFillMode. This matches VBT Pro behavior.
Source code in src/ml4t/backtest/risk/position/dynamic.py
RuleChain
dataclass
¶
Evaluate rules in order, first non-HOLD action wins.
This is the most common composition pattern - rules are checked in priority order and the first rule to trigger takes effect.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rules
|
list[PositionRule]
|
List of rules to evaluate in order |
required |
Example
chain = RuleChain([ StopLoss(pct=0.05), # Highest priority ScaledExit([(0.10, 0.5)]), # Second priority TighteningTrailingStop([...]), # Third priority TimeExit(bars=20), # Lowest priority ])
evaluate
¶
Evaluate rules in order, return first non-HOLD action.
Source code in src/ml4t/backtest/risk/position/composite.py
AllOf
dataclass
¶
All rules must return non-HOLD for the action to trigger.
Useful for requiring multiple conditions to be true before exiting. Returns the first rule's action details (pct, stop_price, etc.).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rules
|
list[PositionRule]
|
List of rules that must all agree |
required |
Example
Exit only if both profitable AND held long enough¶
rule = AllOf([ TakeProfit(pct=0.0), # Must be profitable TimeExit(bars=5), # Must have held 5+ bars ])
evaluate
¶
Return action only if ALL rules return non-HOLD.
Source code in src/ml4t/backtest/risk/position/composite.py
AnyOf
dataclass
¶
First rule to return non-HOLD wins (alias for RuleChain).
Semantically equivalent to RuleChain but named for clarity when composing complex rule logic.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rules
|
list[PositionRule]
|
List of rules where any can trigger |
required |
Example
Exit on stop-loss OR signal¶
rule = AnyOf([ StopLoss(pct=0.05), SignalExit(threshold=0.5), ])
evaluate
¶
Return first non-HOLD action (same as RuleChain).
Source code in src/ml4t/backtest/risk/position/composite.py
Risk: Portfolio Limits¶
MaxDrawdownLimit
dataclass
¶
Bases: PortfolioLimit
Liquidate or halt when drawdown exceeds threshold.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
max_drawdown
|
float
|
Maximum allowed drawdown (0.0-1.0) Default 0.20 = 20% max drawdown |
0.2
|
action
|
str
|
Action when breached ("warn", "reduce", "halt", "liquidate") Default "liquidate" - flatten positions and stop new trades |
'liquidate'
|
warn_threshold
|
float | None
|
Optional earlier threshold for warnings |
None
|
Example
limit = MaxDrawdownLimit(max_drawdown=0.20, warn_threshold=0.15)
Warns at 15% drawdown, halts at 20%¶
MaxPositionsLimit
dataclass
¶
Bases: PortfolioLimit
Limit maximum number of open positions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
max_positions
|
int
|
Maximum number of simultaneous positions |
10
|
action
|
str
|
Action when breached ("warn", "halt") |
'halt'
|
MaxExposureLimit
dataclass
¶
Bases: PortfolioLimit
Limit maximum exposure to a single asset.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
max_exposure_pct
|
float
|
Maximum position size as % of equity (0.0-1.0) Default 0.10 = 10% max per asset |
0.1
|
action
|
str
|
Action when breached |
'warn'
|
Example
limit = MaxExposureLimit(max_exposure_pct=0.10)
No single position can be > 10% of portfolio¶
DailyLossLimit
dataclass
¶
Bases: PortfolioLimit
Liquidate or halt when daily loss exceeds threshold.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
max_daily_loss_pct
|
float
|
Maximum daily loss as % of equity (0.0-1.0) Default 0.02 = 2% max daily loss |
0.02
|
action
|
str
|
Action when breached ("warn", "reduce", "halt", "liquidate") |
'liquidate'
|
Strategy Templates¶
SignalFollowingStrategy
¶
Bases: Strategy
Template for strategies that follow pre-computed signals.
Use this when you have ML predictions, technical indicators, or any pre-computed signal column in your DataFrame.
Class Attributes
signal_column: Name of the signal column in data (default: "signal") position_size: Fraction of equity per position (default: 0.10) allow_shorts: Whether to allow short positions (default: False)
Example
class MyMLStrategy(SignalFollowingStrategy): ... signal_column = "rf_prediction" ... position_size = 0.05 ... ... def should_enter_long(self, signal): ... return signal > 0.7 ... ... def should_exit(self, signal): ... return signal < 0.3
should_enter_long
abstractmethod
¶
Return True to open a long position.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
signal
|
float
|
Current signal value for the asset |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if should enter long position |
should_exit
abstractmethod
¶
Return True to close current position.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
signal
|
float
|
Current signal value for the asset |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if should exit position |
should_enter_short
¶
Return True to open a short position.
Override this method for short strategies. Default returns False.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
signal
|
float
|
Current signal value for the asset |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if should enter short position |
Source code in src/ml4t/backtest/strategies/templates.py
on_data
¶
Process each bar and generate orders based on signals.
Source code in src/ml4t/backtest/strategies/templates.py
MomentumStrategy
¶
Bases: Strategy
Template for momentum/trend-following strategies.
Enters long when asset has positive momentum over lookback period, exits when momentum turns negative.
Class Attributes
lookback: Number of bars for momentum calculation (default: 20) entry_threshold: Minimum return to enter (default: 0.05 = 5%) exit_threshold: Return level to exit (default: -0.02 = -2%) position_size: Fraction of equity per position (default: 0.10)
Example
class MyMomentum(MomentumStrategy): ... lookback = 60 # 60-day momentum ... entry_threshold = 0.10 # Enter on 10% gain ... exit_threshold = 0.0 # Exit when momentum turns negative
Source code in src/ml4t/backtest/strategies/templates.py
calculate_momentum
¶
Calculate momentum as return over lookback period.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
prices
|
list[float]
|
List of prices (most recent last) |
required |
Returns:
| Type | Description |
|---|---|
float
|
Return from first to last price |
Source code in src/ml4t/backtest/strategies/templates.py
on_data
¶
Process each bar and trade based on momentum.
Source code in src/ml4t/backtest/strategies/templates.py
MeanReversionStrategy
¶
Bases: Strategy
Template for mean-reversion strategies.
Buys when price is below moving average by a threshold, sells when price reverts to the mean.
Class Attributes
lookback: Number of bars for mean calculation (default: 20) entry_zscore: Z-score threshold to enter (default: -2.0) exit_zscore: Z-score threshold to exit (default: 0.0) position_size: Fraction of equity per position (default: 0.10)
Example
class MyMeanReversion(MeanReversionStrategy): ... lookback = 30 ... entry_zscore = -2.5 # More extreme entry ... exit_zscore = 0.5 # Take profit above mean
Source code in src/ml4t/backtest/strategies/templates.py
calculate_zscore
¶
Calculate z-score of current price vs historical distribution.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
prices
|
list[float]
|
Historical prices |
required |
current
|
float
|
Current price |
required |
Returns:
| Type | Description |
|---|---|
float | None
|
Z-score or None if insufficient data |
Source code in src/ml4t/backtest/strategies/templates.py
on_data
¶
Process each bar and trade based on mean reversion.
Source code in src/ml4t/backtest/strategies/templates.py
LongShortStrategy
¶
Bases: Strategy
Template for long/short equity strategies.
Ranks assets by a signal and goes long top N, short bottom N.
Class Attributes
signal_column: Column to rank assets by (default: "signal") long_count: Number of assets to go long (default: 5) short_count: Number of assets to go short (default: 5) position_size: Fraction of equity per position (default: 0.05) rebalance_frequency: Bars between rebalancing (default: 20)
Example
class MyLongShort(LongShortStrategy): ... signal_column = "momentum_score" ... long_count = 10 ... short_count = 10 ... rebalance_frequency = 21 # Monthly
Source code in src/ml4t/backtest/strategies/templates.py
on_prepare
¶
Resolve optional schedule-based rebalance gating before the run starts.
Source code in src/ml4t/backtest/strategies/templates.py
rank_assets
¶
Rank assets by signal and return long/short lists.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
dict[str, dict]
|
Current bar data for all assets |
required |
Returns:
| Type | Description |
|---|---|
tuple[list[str], list[str]]
|
Tuple of (long_assets, short_assets) |
Source code in src/ml4t/backtest/strategies/templates.py
on_data
¶
Rebalance portfolio periodically based on rankings.