Backtesting¶
Backtesting Broker¶
- class lumibot.backtesting.backtesting_broker.BacktestingBroker(data_source, option_source=None, connect_stream=True, max_workers=20, config=None, option_early_assignment_enabled=False, option_early_assignment_max_dte_days=1, option_early_assignment_max_extrinsic=0.05, **kwargs)¶
Bases:
Broker- IS_BACKTESTING_BROKER = True¶
- calculate_trade_cost(order: Order, strategy, price: float, quantity=None)¶
Calculate the trade cost of an order for a given strategy
- cancel_order(order)¶
Cancel an order
- cash_settle_options_contract(position, strategy)¶
Cash settle an options contract (used by cash-settled index options).
- property datetime¶
- get_active_tracked_orders(strategy: str, asset=None) list[Order]¶
Return active (open/submitted/new) orders for the given strategy.
Backtests can accumulate tens of thousands of filled orders over long windows. Scanning all tracked orders and calling
Order.is_active()each bar is a major performance bottleneck, so this method sources active orders directly from the broker’s active-order buckets.
- get_historical_account_value()¶
Get the historical account value of the account. TODO: Fill out the docstring with more information.
- get_last_bar(asset)¶
Returns OHLCV dictionary for last bar of the asset.
- get_time_to_close()¶
Return the remaining time for the market to close in seconds
- get_time_to_open()¶
Return the remaining time for the market to open in seconds
- initialize_market_calendars(trading_days_df)¶
Initialize trading calendar and eagerly build caches for backtesting.
- is_market_open()¶
Return True if market is open else false
- limit_order(limit_price, side, open_, high, low)¶
Limit order logic.
- process_early_assignment_contracts(strategy, force: bool = False) None¶
- process_expired_option_contracts(strategy)¶
Process expiration lifecycle events for options contracts.
- Parameters:
strategy (Strategy object.) – Strategy object.
- Return type:
List of orders
- process_pending_orders(strategy)¶
Used to evaluate and execute open orders in backtesting.
This method will evaluate the open orders at the beginning of every new bar to determine if any of the open orders should have been filled. This method will execute order events as needed, mostly fill events.
- Parameters:
strategy (Strategy object)
- settle_expired_option_contract(position, strategy)¶
- should_continue()¶
In production mode always returns True. Needs to be overloaded for backtesting to check if the limit datetime was reached
- stop_order(stop_price, side, open_, high, low)¶
Stop order logic.
- lumibot.backtesting.backtesting_broker.get_futures_margin_requirement(asset: Asset) float¶
Get the initial margin requirement for a futures contract.
This is used in backtesting to simulate the margin deduction when opening a futures position and margin release when closing.
- Parameters:
asset – The futures Asset object
- Returns:
Initial margin requirement in dollars
- Return type:
float
Note
These are TYPICAL values and may not match current broker requirements. For live trading, brokers handle margin internally.
Data Source Backtesting¶
- class lumibot.data_sources.data_source_backtesting.DataSourceBacktesting(datetime_start: datetime | None = None, datetime_end: datetime | None = None, backtesting_started: datetime | None = None, config: dict | None = None, api_key: str | None = None, show_progress_bar: bool = True, log_backtest_progress_to_file=False, progress_csv_path: str | None = None, delay: int | None = None, pandas_data: dict | list = None, funding_rates=None, mark_prices=None, perpetual_risk=None, **kwargs)¶
Bases:
DataSource,ABCThis class is the base class for all backtesting data sources. It is also an abstract class and should not be instantiated directly because it does not define all necessary methods. Instead, instantiate one of the child classes like PandasData.
- IS_BACKTESTING_DATA_SOURCE = True¶
- static estimate_requested_length(length=None, start_date=None, end_date=None, timestep='minute')¶
Infer the number of rows required to satisfy a backtest data request.
- flush_runtime_timings()¶
Publish final phases without inventing another simulation step.
- get_datetime(adjust_for_delay=False)¶
Get the current datetime of the backtest.
- Parameters:
adjust_for_delay (bool) – Not used for backtesting data sources. This parameter is only used for live data sources.
- Returns:
The current datetime of the backtest.
- Return type:
- get_datetime_range(length, timestep='minute', timeshift=None)¶
- get_funding_events(asset, start, end)¶
Explicit funding history; None means unavailable, [] means no events.
- get_perpetual_mark(asset, at)¶
Historical mark or a clearly identified closed-candle approximation.
- get_perpetual_risk(asset)¶
Caller-supplied maintenance and liquidation fee rates, by instrument.
- get_runtime_timings()¶
- log_backtest_progress_to_csv(percent, elapsed, log_eta, portfolio_value, simulation_date=None, cash=None, total_return_pct=None, positions_json=None, orders_json=None)¶
Log backtest progress to CSV file.
- Parameters:
percent (float) – Progress percentage (0-100)
elapsed (timedelta) – Time elapsed since backtest started
log_eta (timedelta) – Estimated time remaining
portfolio_value (str or float) – Current portfolio value
simulation_date (str, optional) – Current date/time in the backtest simulation (YYYY-MM-DD HH:MM:SS format)
cash (float, optional) – Current cash balance
total_return_pct (float, optional) – Running total return percentage
positions_json (str, optional) – JSON string of minimal position data from Position.to_minimal_dict(): [{“asset”: {“symbol”: “AAPL”, “type”: “stock”}, “qty”: 100, “val”: 15000.0, “pnl”: 500.0}, …]
orders_json (str, optional) – JSON string of minimal order data from Order.to_minimal_dict(): [{“asset”: {“symbol”: “AAPL”, “type”: “stock”}, “side”: “buy”, “qty”: 100, “type”: “market”, “status”: “new”}, …]
- record_runtime_milestone(name)¶
Record bounded, per-run wall-clock observations without advancing simulation.
First observation wins. These are diagnostics, never billing or success authority, and contain no strategy parameters or provider credentials.
- shutdown()¶
Cleanup any background resources (thread pools, progress heartbeat).
- stop_progress_heartbeat() None¶
Frequently asked questions¶
What does the BacktestingBroker do?¶
BacktestingBroker stands in for a real broker during a backtest. It reads
prices from a backtesting data source, keeps track of the simulated clock and
market hours, fills your orders, and updates cash and positions. It also
handles option expiration. Your strategy code calls it the same way it would
call a live broker.
What asset classes can LumiBot backtest?¶
LumiBot supports stocks, options, crypto, futures, forex, and prediction markets. What you can backtest in practice depends on the data source you pick, because each provider covers different assets. See Backtesting Trading Strategies in Python with LumiBot for the provider guides.
What is the difference between portfolio_value and get_portfolio_value()?¶
self.portfolio_value is a property that returns the last portfolio value
(cash plus positions) the broker reported. It is fast and does not ask the
broker again. self.get_portfolio_value() asks the broker for a fresh value.
In live trading it forces a balance refresh and returns None if the broker
cannot give a fresh value.
How do I add trading fees to a backtest?¶
Create TradingFee objects from lumibot.entities and pass them as
buy_trading_fees and sell_trading_fees to backtest(). A
TradingFee can have a flat_fee per order, a percent_fee of the
order value, or a per_contract_fee, which is handy for option commissions.
Which benchmark does a backtest compare against?¶
By default the benchmark is SPY. Pass benchmark_asset to backtest()
to use a different symbol or Asset.
What happens to my options at expiration in a backtest?¶
An option that is out of the money expires worthless. An in the money index option is settled in cash. For other in the money options, a long position is exercised and a short position is assigned, and the shares are delivered at the strike price. If the account cannot support exercising a long option, LumiBot settles its value in cash and logs a warning.
Does the backtester simulate early option assignment?¶
Only if you turn it on. Early assignment is off by default. Set the strategy
parameter option_early_assignment_enabled to True to enable it. You can
tune it with option_early_assignment_max_dte_days (default 1) and
option_early_assignment_max_extrinsic (default 0.05).