quantlab.backtest.config.BacktestConfig#

class quantlab.backtest.config.BacktestConfig(*, price_dataset, start_date, end_date, rebalance_periods, output_dir, model=None, model_mode=None, checkpoint=None, cv_project_dir=None, fees=0.0005, slippage=0.0005, init_cash=1000000.0, sizing_basis='fill', benchmark_dataset=None, risk_model=None, tracker=<factory>, name=None)[source]#

Bases: object

Fields every backtester shares.

Selection parameters are not here but on subclasses such as CrossSectionBacktestConfig, so a time-series backtester does not carry cross-sectional fields. See docs/user-guide/backtesting.md.

Examples

With price_dataset a market dataset and model a model built earlier:

>>> cfg = BacktestConfig(
...     price_dataset=price_dataset,
...     model=model,
...     model_mode="load",
...     checkpoint="/data/models/xgb/best.joblib",
...     start_date="2023-01-01",
...     end_date="2023-12-31",
...     output_dir="/data/backtests",
...     rebalance_periods=5,
... )
>>> cfg.fees, cfg.slippage, cfg.init_cash
(0.0005, 0.0005, 1000000.0)

A config for run_weights() needs no model, and with output_dir=None the run stays in memory:

>>> weights_cfg = BacktestConfig(
...     price_dataset=price_dataset,
...     start_date="2023-01-01",
...     end_date="2023-12-31",
...     output_dir=None,
...     rebalance_periods=5,
... )
>>> weights_cfg.model, weights_cfg.model_mode, weights_cfg.output_dir
(None, None, None)
benchmark_dataset = None#

A market dataset holding exactly one symbol, for example the QQQ store built by CrspDatasetConfig.qqq_benchmark. It is a (timestamp, symbol) panel like price_dataset and needs the same fill and valuation columns. When set, every run also simulates buying and holding it on the strategy’s bars and reports the portfolio against it (benchmark and relative metric blocks, the benchmark NAV and the excess-return and excess-drawdown charts).

checkpoint = None#

Checkpoint to restore in "load" mode for run(): the file the model’s train() returned (a model’s checkpoint, an ensemble’s run.json).

cv_project_dir = None#

The walk-forward unit a train_cv run wrote, read by run_cv() to replay each fold with its own checkpoint.

end_date#

Last date of the backtest window, inclusive.

property execution#

The run’s execution settings: its sizing basis, fees and slippage.

Examples

>>> cfg.execution == ExecutionSettings(cfg.sizing_basis, cfg.fees, cfg.slippage)
True
fees = 0.0005#

Proportional fee per trade.

init_cash = 1000000.0#

Starting cash of the simulated portfolio.

model = None#

The model that produces the scores the target weights are built from: any object with the members of quantlab.backtest.base.Predictor, such as a BaseModel (the annotation names the usual case, because this module does not import the backtest layer). run() and run_cv() require it; run_weights() backtests precomputed weights and ignores it, so it may be None there. model and model_mode are both set or both None.

model_mode = None#

"train" trains model on its own dates first; "load" restores a checkpoint (checkpoint for run(), cv_project_dir for run_cv()). Required by run() and run_cv(), like model.

name = None#

Dotted import path of the backtester class; filled by the config setter.

output_dir#

Directory each run writes its own run directory under. None keeps the run in memory: the backtest writes no run directory and the result’s run_dir is None. It covers the backtest’s own files only: with model_mode="train" the model still writes its checkpoint where its own config points.

price_dataset#

The dataset whose prices the simulation trades on.

rebalance_periods#

Rebalance every this many bars.

risk_model = None#

A factor risk model (quantlab.risk.base.FactorRiskModel) whose factors the run’s return and risk are attributed to (factor attribution, ADR 0026): each bar’s NAV return split into factor, specific, uncovered, risk-free and trading terms over the holdings the engine held at the start of the bar, the book’s forecast (ex-ante) and realized (ex-post) volatility split over factors and specific risk, grouped into country, industry and style, in the metrics’ factor_attribution block (whole, in_sample and out_of_sample like the other metrics; whole only for run_weights(); the stitched curve only for run_cv()) and the run directory’s factor_attribution.zarr. Its regression and estimate stores must already cover the window; the backtest reads them and never builds them. None (the default) attributes nothing.

sizing_basis = 'fill'#

The price a target weight is sized against. "fill" (the default) sizes at the fill price of the bar the order executes on, against the portfolio valued at those prices: vectorbt’s own default. "valuation" sizes at the valuation price of the signal bar t (its close), against the portfolio valued at t’s close, as a broker order placed after the close must be sized; the order still fills at t+1’s fill price. The vectorbt engine and portfolio construction’s replay both read it, through execution.

slippage = 0.0005#

Proportional slippage per trade.

start_date#

First date of the backtest window, inclusive.

to_dict()[source]#

Return the config as a plain dict, each component as its own config.

The dataset, model, rule and tracker fields are declared with quantlab.core.component.component and written as their get_config(); nothing is deep-copied through dataclasses.asdict, so no panel or trained model is copied.

Examples

>>> cfg.to_dict()["model"]["name"] == model.import_path
True
>>> cfg.to_dict()["rebalance_periods"]
5
tracker#

Where each run’s metrics and report are tracked (ADR 0015); the default NullTracker sends nothing anywhere.