Migrating from Backtrader to LumiBot

Move a strategy one behavior at a time: data timing, indicators, position sizing, orders, and execution. Both libraries support strategy lifecycles and broker abstractions. Choosing LumiBot does not make two backtests numerically equivalent.

The API mapping below was checked against the linked Backtrader source and the current LumiBot source on September 8, 2026. It makes no claim that Backtrader is abandoned or that every broker, asset, and Python version has identical support.

Map the lifecycle

Core concepts

Backtrader

LumiBot

Migration check

bt.Strategy.__init__

Strategy.initialize

Configure the decision cadence explicitly.

next()

on_trading_iteration()

Check which completed bars are visible at that timestamp.

self.buy() / self.sell()

create_order then submit_order

Preserve quantity, side, order type, and pending-order handling.

self.data.close[0]

get_last_price / get_historical_prices

Check adjustment, bar interval, timezone, and missing data.

self.broker.getcash() / getvalue()

self.cash / self.portfolio_value

Use matching capital, fees, and valuation conventions.

Cerebro.adddata and Cerebro.run

Data source and MyStrategy.backtest

Trader is the broker-run orchestrator, not a replacement historical data feed.

A small allocation example

The examples express the same target: hold ten shares while the ten-day average is above the thirty-day average, otherwise hold none. This is an allocation rule, not an identical crossover implementation or a proven parity test. Use a single symbol while checking the port; do not liquidate unrelated positions.

Backtrader strategy

import backtrader as bt

class SmaAllocation(bt.Strategy):
    def __init__(self):
        self.fast = bt.ind.SMA(self.data.close, period=10)
        self.slow = bt.ind.SMA(self.data.close, period=30)
        self.pending = None

    def notify_order(self, order):
        if not order.alive():
            self.pending = None

    def next(self):
        if self.pending:
            return
        target = 10 if self.fast[0] > self.slow[0] else 0
        if self.position.size != target:
            self.pending = self.order_target_size(target=target)

Add this class to your existing Cerebro runner and existing data feed. Keep that feed’s dates and settings as the baseline. Backtrader’s own SMA example and Cerebro source document those interfaces.

Complete LumiBot backtest

Install lumibot in a Python 3.10+ virtual environment. Save the following as sma_allocation.py and run python sma_allocation.py. This daily Yahoo-data example makes no LLM calls and requires no broker keys. It is a porting example, not a claim of matching the data from your existing Backtrader run.

from datetime import datetime
from lumibot.backtesting import YahooDataBacktesting
from lumibot.strategies import Strategy

class SmaAllocation(Strategy):
    def initialize(self):
        self.sleeptime = "1D"
        self.vars.pending = None

    def on_trading_iteration(self):
        pending = self.vars.pending
        if pending is not None and pending.is_active():
            return
        bars = self.get_historical_prices("AAPL", 30, timestep="day")
        if bars is None or len(bars.df) < 30:
            return
        closes = bars.df["close"]
        target = 10 if closes.tail(10).mean() > closes.tail(30).mean() else 0
        position = self.get_position("AAPL")
        current = position.quantity if position is not None else 0
        difference = target - current
        if difference:
            order = self.create_order(
                "AAPL", abs(difference), "buy" if difference > 0 else "sell"
            )
            self.vars.pending = order
            self.submit_order(order)

if __name__ == "__main__":
    SmaAllocation.backtest(
        YahooDataBacktesting,
        datetime(2025, 1, 6),
        datetime(2025, 4, 1),
        budget=100_000,
        benchmark_asset="SPY",
    )

Compare timestamps and orders first

Before comparing returns, reconcile the input bars, indicator warm-up, completed bar boundary, order timing, quantities, fills, fees, and corporate-action adjustments. A chart that looks similar is not sufficient evidence of parity. Inspect pending orders and partial fills before adding more symbols or leverage.

Broker-connected execution uses a different runner

Keep the strategy class, configure a supported broker, instantiate the strategy with that broker, add it to Trader, and run the trader. Do not pass a broker class in place of YahooDataBacktesting to backtest. Broker authentication, account permissions, supported order types, and data access still need setup. See Deploy a LumiBot Trading Strategy and the relevant broker documentation.

Add AI only after the port is understood

Use Run your first AI backtest to add a research agent, then AI Trading Examples for stock, macro, and options workflows. Keep the deterministic port as a baseline. Record model cost and the limits of historical LLM knowledge alongside any performance comparison.