Strategies¶
Every strategy subclasses Strategy and implements on_data. The broker is your interface to the market -- use it to submit orders, query positions, and set risk rules.
Strategy Interface¶
from ml4t.backtest import Strategy
class MyStrategy(Strategy):
def on_start(self, broker):
"""Called once before the first bar. Optional.
Use this to set position rules, initialize state, or log config.
"""
pass
def on_data(self, timestamp, data, context, broker):
"""Called on every bar. Required.
Args:
timestamp: Current bar's datetime
data: Dict of {asset: bar_dict}
context: Dict of context data (if provided via DataFeed)
broker: Broker for orders and position queries
"""
pass
def on_end(self, broker):
"""Called once after the last bar. Optional.
Use this for final cleanup or logging.
"""
pass
What data Contains¶
data = {
"AAPL": {
"open": 150.0,
"high": 152.0,
"low": 149.5,
"close": 151.0,
"volume": 1_000_000,
"signals": {"prediction": 0.85, "momentum": 0.12},
},
"MSFT": { ... },
}
Signals come from the optional signals DataFrame passed to DataFeed. Access them via bar.get("signals", {}).get("column_name").
Broker Methods¶
Submitting Orders¶
# Market order: buy 100 shares
broker.submit_order("AAPL", 100)
# Short sell: negative quantity
broker.submit_order("AAPL", -100)
# Limit order
from ml4t.backtest.types import OrderType
broker.submit_order("AAPL", 100, order_type=OrderType.LIMIT, limit_price=150.0)
# Stop order
broker.submit_order("AAPL", 100, order_type=OrderType.STOP, stop_price=145.0)
# Stop-limit order
broker.submit_order("AAPL", 100, order_type=OrderType.STOP_LIMIT,
stop_price=145.0, limit_price=144.0)
Bracket Orders¶
Submit entry + take-profit + stop-loss in one call:
orders = broker.submit_bracket(
asset="AAPL",
quantity=100,
take_profit=165.0, # TP target price
stop_loss=145.0, # SL price
)
# Returns (entry_order, tp_order, sl_order) or None if rejected
Querying Positions¶
# Single position
pos = broker.get_position("AAPL")
if pos is not None:
print(pos.quantity, pos.entry_price, pos.bars_held)
# All positions
for asset, pos in broker.get_positions().items():
print(f"{asset}: {pos.quantity} shares @ ${pos.entry_price:.2f}")
# Account value and cash
equity = broker.get_account_value()
cash = broker.get_cash()
Closing Positions¶
Rebalancing¶
# Target weight allocation
broker.rebalance_to_weights({
"AAPL": 0.30, # 30% of portfolio
"MSFT": 0.30,
"GOOG": 0.20,
# Remaining 20% stays in cash
})
Order Management¶
# Cancel a pending order
broker.cancel_order(order_id)
# Check rejected orders
rejected = broker.get_rejected_orders()
for order in rejected:
print(f"{order.asset}: {order.rejection_reason}")
Setting Risk Rules¶
Set position rules in on_start. They apply automatically on every bar:
from ml4t.backtest import StopLoss, TakeProfit, TrailingStop, RuleChain
class ProtectedStrategy(Strategy):
def on_start(self, broker):
broker.set_position_rules(RuleChain([
StopLoss(pct=0.05),
TakeProfit(pct=0.15),
TrailingStop(pct=0.03),
]))
See Risk Management for the full rule catalog and composition patterns.
Strategy Templates¶
The library includes four ready-to-use templates. Subclass them and override the decision methods.
SignalFollowingStrategy¶
For ML predictions, technical indicators, or any pre-computed signal:
from ml4t.backtest.strategies.templates import SignalFollowingStrategy
class MyMLStrategy(SignalFollowingStrategy):
signal_column = "rf_prediction" # Column name in signals DataFrame
position_size = 0.05 # 5% of equity per position
def should_enter_long(self, signal):
return signal > 0.7
def should_exit(self, signal):
return signal < 0.3
MomentumStrategy¶
Enter on positive momentum, exit on negative:
from ml4t.backtest.strategies.templates import MomentumStrategy
class MyMomentum(MomentumStrategy):
lookback = 60 # 60-bar momentum window
entry_threshold = 0.10 # Enter on 10% return
exit_threshold = 0.0 # Exit when momentum turns negative
position_size = 0.10 # 10% of equity
MeanReversionStrategy¶
Buy oversold, sell at reversion:
from ml4t.backtest.strategies.templates import MeanReversionStrategy
class MyMeanReversion(MeanReversionStrategy):
lookback = 30
entry_zscore = -2.5 # Enter when 2.5 std below mean
exit_zscore = 0.5 # Exit when 0.5 std above mean
position_size = 0.10
LongShortStrategy¶
Rank assets and go long top N, short bottom N:
from ml4t.backtest.strategies.templates import LongShortStrategy
class MyLongShort(LongShortStrategy):
signal_column = "alpha_score"
long_count = 10
short_count = 10
position_size = 0.05
rebalance_frequency = 21 # Monthly rebalance
Patterns¶
Multi-Asset with Position Sizing¶
class SizedStrategy(Strategy):
max_positions = 10
def on_data(self, timestamp, data, context, broker):
positions = broker.get_positions()
if len(positions) >= self.max_positions:
return
equity = broker.get_account_value()
per_position = equity / self.max_positions
for asset, bar in data.items():
if broker.get_position(asset) is not None:
continue
signal = bar.get("signals", {}).get("score", 0)
if signal > 0.7:
shares = int(per_position / bar["close"])
if shares > 0:
broker.submit_order(asset, shares)
Per-Asset Risk Rules¶
class AssetSpecificRules(Strategy):
def on_start(self, broker):
# Different rules for different assets
broker.set_position_rules(
RuleChain([StopLoss(pct=0.03), TrailingStop(pct=0.02)]),
asset="AAPL",
)
broker.set_position_rules(
RuleChain([StopLoss(pct=0.10), TakeProfit(pct=0.20)]),
asset="BTC",
)
# Global fallback for any other asset
broker.set_position_rules(StopLoss(pct=0.05))
Best Practices¶
- Avoid look-ahead bias -- only use data from the current bar and earlier
- Account for costs -- test with realistic commission and slippage
- Size positions conservatively -- don't risk more than 1-2% per trade
- Use NEXT_BAR mode -- for production strategies, avoid SAME_BAR
- Validate with profiles -- compare results across framework profiles
See It in Action¶
The Machine Learning for Trading book demonstrates these patterns across multiple case studies:
- Ch16 / NB03 (
single_asset_ml4t_backtest) — RSI mean-reversion Strategy with submit_order/close_position - Ch16 / NB04 (
framework_parity) — same strategy compared across VectorBT and ml4t-backtest - Ch16 / NB13 (
futures_backtesting) — futures strategies with ContractSpec and per-contract costs - Ch16 case studies — 6 Engine-based case studies (ETFs, FX, equities, crypto, futures, options) using TargetWeightExecutor with ML predictions
Next Steps¶
- Book Guide -- chapter and case-study map for strategy workflows
- Stateful Strategies -- advanced patterns that require event-driven execution
- Risk Management -- full rule catalog and composition
- Order Types -- limit, stop, bracket orders in detail
- Data Feed -- how data and signals are structured