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, ABC

This 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:

datetime

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).