Porting Backtrader Strategies to LumiBot Without Assuming Parity
Summary
This guide maps common Backtrader concepts to LumiBot, including strategy lifecycle methods, market data access, orders, portfolio values, and backtest orchestration. Its example ports a target allocation rule: hold ten shares when the ten-day average exceeds the thirty-day average, otherwise hold none. The LumiBot example uses historical daily data, tracks an active order, and adjusts the position toward the target.
The guide emphasizes validating the port one behavior at a time. It recommends reconciling bar timestamps, warm-up periods, order timing, fills, fees, and corporate-action adjustments before comparing returns. The examples illustrate an allocation rule but are not proof of numerical equivalence or a parity test. Broker-connected execution uses a separate runner and still depends on broker permissions, supported order types, and data access; adding AI is suggested only after establishing a deterministic baseline.
Key ideas
- Map lifecycle, data, order, and portfolio concepts individually when porting between frameworks.
- The example targets ten shares when the ten-day average is above the thirty-day average, and zero otherwise.
- Matching strategy logic does not ensure matching backtest results.
- Compare timestamps, data adjustments, order behavior, fills, and fees before assessing performance.
- Broker-connected trading has separate setup and support requirements.
Tags
Full text
# MIGRATING FROM BACKTRADER
Migrating from Backtrader to LumiBot
====================================
.. meta::
:description: Move a strategy one behavior at a time: data timing, indicators, position sizing, orders, and execution.
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
-----------------
.. list-table:: Core concepts
:header-rows: 1
:widths: 30 35 35
* - 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
~~~~~~~~~~~~~~~~~~~
.. code-block:: python
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 <https://github.com/mementum/backtrader/blob/master/samples/sigsmacross/sigsmacross.py>`_
and `Cerebro source <https://github.com/mementum/backtrader/blob/master/backtrader/cerebro.py>`_
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.
.. code-block:: python
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 :doc:`deployment` and the relevant broker documentation.
Add AI only after the port is understood
----------------------------------------
Use :doc:`agents_quickstart` to add a research agent, then :doc:`agents_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.Shown in full with attribution under the source's licence. Licence: GPL-3.0
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.