Backtest

Backtest

class qtrade.backtest.Backtest(data: DataFrame | dict[str, DataFrame], strategy_class: type[Strategy], cash: float = 10_000, commission: Commission | None = None, margin_ratio: float | dict[str, float] | None = None, trade_on_close: bool = False, verbose: bool = False, contract_multiplier: float | dict[str, float] | None = None, contracts: dict[str, Contract] | Contract | None = None)[source]

Bases: object

Run a strategy against historical data — single asset or a portfolio.

Pass a DataFrame for single-asset; pass dict[str, DataFrame] to run a portfolio strategy across multiple assets.

Parameters:
  • data – Single OHLCV DataFrame, or a dict mapping asset symbol to its OHLCV DataFrame (multi-asset). All DataFrames must have a DatetimeIndex and the same index for proper portfolio accounting.

  • strategy_class – Strategy class (subclass of Strategy) to instantiate.

  • cash – Starting cash.

  • commission – Commission calculator (None ⇒ no commission).

  • contractsPreferred way to specify per-asset multiplier and margin. Pass a single Contract to apply to all assets, or a dict keyed by asset symbol — assets you don’t list resolve to STOCK_CASH (no leverage, multiplier 1.0). Built-in specs live in qtrade.contracts (STOCK_CASH, GC_COMEX, ES_CME, etc.); custom Contract(multiplier=…, margin_ratio=…) instances work the same way.

  • margin_ratio – Lower-level escape hatch (scalar or dict). Mutually exclusive with contracts.

  • contract_multiplier – Lower-level escape hatch (scalar or dict). Mutually exclusive with contracts.

  • trade_on_close – If True, market orders fill at the current bar’s close price; otherwise at the next bar’s open.

  • verbose – Verbose logging.

export_report(output_path: str, *, title: str = 'QTrade Backtest Report', strategy_name: str | None = None) str[source]

Export a single-file HTML report (stats + charts + trades).

Parameters:
  • output_path – Destination file path.

  • title – Document title and main heading.

  • strategy_name – Optional label shown in the header. Defaults to the strategy class name.

Returns:

Absolute path (string)

get_trade_history() DataFrame[source]

Trade-by-trade DataFrame across all assets.

optimize(maximize: str, constraint: Callable[[Any], bool] | None = None, **params_grid)[source]

Grid-search strategy parameters and return the best.

Parameters:
  • maximize – Metric name from calculate_stats to maximize.

  • constraint – Optional filter on parameter dicts (return False to skip).

  • **params_grid – Parameter ranges, e.g. n1=range(5, 30, 5).

Returns:

`` (best_params, best_stats, all_results)

plot()[source]
plot_heatmap(results: list[dict[str, Any]], x: str, y: str, metric: str, **kwargs)[source]

Render a 2D heatmap of an optimization grid.

Thin wrapper around qtrade.utils.heatmap.plot_optimization_heatmap() — pass the all_results returned by optimize().

Parameters:
  • resultsall_results list from optimize().

  • x – Parameter names to lay out on the axes.

  • y – Parameter names to lay out on the axes.

  • metric – Stats key to color by (e.g. "Sharpe Ratio").

  • **kwargs – Forwarded to plot_optimization_heatmap (filename, palette, title, aggfunc, show_plot, width, height).

Returns:

The Bokeh ``figure`` object.

run(**strategy_params)[source]

Run the backtest end-to-end.

show_stats()[source]
walk_forward_optimize(*, train_window: int, test_window: int, maximize: str, step: int | None = None, constraint: Callable[[Any], bool] | None = None, **params_grid) dict[source]

Walk-forward optimization: rolling-window training + immediate out-of-sample test.

For each (train, test) window pair:

  1. Run optimize() on the train slice to pick the best params.

  2. Run a fresh backtest on the test slice using those params.

  3. Record the out-of-sample (OoS) statistics.

This is the standard antidote to the in-sample overfitting that bare optimize() produces — every reported metric is on data the strategy was never tuned on.

Parameters:
  • train_window – Number of bars in each training window.

  • test_window – Number of bars in the test window immediately after.

  • maximize – Metric name to maximize within each train window (same as optimize()).

  • step – How many bars to advance between successive train starts. Defaults to test_window (non-overlapping test windows).

  • constraint – Optional filter on parameter dicts.

  • **params_grid – Parameter ranges, same as optimize().

Returns:
  • ``{‘windows’: […], ‘summary’: {…}}``.

  • Each entry in ``windows`` is a dict with ``train_start``,

  • ``train_end``, ``test_start``, ``test_end``, ``best_params``,

  • ``train_stats``, ``test_stats``, and ``test_equity``

  • (the test-window equity Series).

  • ``summary`` contains aggregate OoS metricsn_windows,

  • ``mean_oos_return``, ``hit_rate``, ``min_oos_return``,

  • ``max_oos_return``.