Skip to main content

Migrating from backtrader

You have a working backtrader strategy and want to run it on MTS1B. Here's the mapping.

High-level mapping

backtraderMTS1B equivalent
bt.Strategy subclassA factor (@register("f_X")) + a strategy spec (StrategyRegistry.register(...))
self.buy() / self.sell()mts1b-oms.submit(Order(...))
bt.Cerebromts1b-GPUbacktester.run_single
bt.feeds.PandasDataUniversePanel from mts1b-datalake.build_panel
bt.indicators.SMA(...)factor function with rolling computation
bt.Analyzer.SharpeRatiomts1b_quantkit.metrics.sharpe
cerebro.brokermts1b-brokers.paper.PaperClient (backtest) or live broker
cerebro.broker.setcash(...)FundConfig.target_nav_usd
cerebro.broker.setcommission(...)cost_bps parameter to run_single

Example: SMA crossover

In backtrader

import backtrader as bt


class SMACross(bt.Strategy):
params = (("p_fast", 20), ("p_slow", 50))

def __init__(self):
sma_fast = bt.indicators.SMA(period=self.p.p_fast)
sma_slow = bt.indicators.SMA(period=self.p.p_slow)
self.crossover = bt.indicators.CrossOver(sma_fast, sma_slow)

def next(self):
if not self.position:
if self.crossover > 0:
self.buy()
elif self.crossover < 0:
self.close()


cerebro = bt.Cerebro()
cerebro.addstrategy(SMACross)
data = bt.feeds.PandasData(dataname=df)
cerebro.adddata(data)
cerebro.broker.setcash(100000)
cerebro.broker.setcommission(commission=0.001)
cerebro.run()
print(cerebro.broker.getvalue())

In MTS1B

import numpy as np
from mts1b_quantkit.factors import register


@register("f_sma_cross")
def f_sma_cross(panel, /, p_fast: int = 20, p_slow: int = 50):
"""Long when fast SMA > slow SMA; flat otherwise."""
close = panel.close # (T, A)
sma_fast = np.array([
close[max(0, t - p_fast):t].mean(axis=0) for t in range(len(close))
])
sma_slow = np.array([
close[max(0, t - p_slow):t].mean(axis=0) for t in range(len(close))
])
signal = (sma_fast > sma_slow).astype(float) * 2.0 - 1.0 # +1 / -1
return signal


# Backtest
from mts1b_GPUbacktester import run_single
from mts1b_quantkit.factors import get

result = run_single(
factor=get("f_sma_cross"),
params={"p_fast": 20, "p_slow": 50},
universe="us-large-cap",
start="2014-01-01", end="2024-01-01",
rebal="daily",
sizing={"method": "equal_weight_long", "gross": 1.0},
cost_bps=10,
)
print(f"Sharpe: {result.sharpe:.2f}")
print(f"Final NAV: ${100000 * (1 + result.cagr) ** 10:,.0f}")

Key differences

1. Factor functions are cross-sectional

backtrader runs ONE strategy per symbol in parallel. MTS1B factors operate on (T, A) panels — they produce CROSS-SECTIONAL rankings (z-score across the universe).

This makes MTS1B better at long/short, pair trades, and basket strategies; less natural for "trade SPY in isolation".

If you want backtrader-style single-symbol behavior, set universe=["SPY"] (universe of one).

2. Position state lives outside the strategy

backtrader's self.position is per-strategy state. In MTS1B:

  • mts1b-oms owns positions
  • Factor output is just target WEIGHTS
  • The OMS computes the delta vs current positions and emits orders

You don't track "are we long?" in the factor — you set the target weight (-1, 0, or +1 in the SMA example) and the OMS handles the rest.

3. Real risk gates

backtrader has minimal risk controls (commission, slippage). MTS1B has 7 gates including drawdown halt, position-pct cap, broker allowlist, etc.

When migrating, configure a RiskEnvelope for your fund to match what your backtrader strategy implicitly assumed (no drawdown halt, unlimited position size).

4. Multiple funds, multiple strategies

In backtrader, one Cerebro = one strategy. In MTS1B:

  • A fund has a NAV and a broker
  • Multiple strategies can write into one fund (sleeves)
  • Composer (mts1b-portfolio.multi_sleeve.compose) combines them

You can model "backtrader-style" by creating one fund per strategy.

Where backtrader is better

Use caseRecommendation
Single-symbol strategy, simple SMA cross / RSIStay with backtrader; MTS1B is overkill
Event-driven (per-tick logic)Stay with backtrader; MTS1B is bar-driven
Live trading with one symbolStay with backtrader

Where MTS1B is better

Use caseRecommendation
Cross-sectional / multi-assetMTS1B
Large universes (>100 symbols)MTS1B with GPU backend
Parameter sweeps (>1k combos)MTS1B with ladder
Multi-fund treasury allocationMTS1B
Production risk gates + haltMTS1B
Audit chain for complianceMTS1B

Migration checklist

  • Convert backtrader Strategy into a factor function (cross-sectional output)
  • Map indicators (SMA, RSI, MACD) to factor primitives in mts1b_quantkit.factors
  • Wrap the factor with @register("f_your_name")
  • Build a UniversePanel from your data source
  • Run mts1b_GPUbacktester.run_single for backtest validation
  • Run mts1b_quantkit.cv.walk_forward for OOS validation
  • Create a FundConfig + RiskEnvelope matching your trading assumptions
  • Register the strategy with mts1b_research.strategies.StrategyRegistry
  • Test on paper broker (broker="paper") before live

See also