quantlab.backtest.engine_vectorbt.VectorBtBacktester#
- class quantlab.backtest.engine_vectorbt.VectorBtBacktester(config)[source]#
Bases:
BaseBacktesterAbstract backtester that simulates target weights with vectorbt.
Subclasses supply
config_cls,MARKETand_generate_signals; everything from the weights onward is implemented here. The constructor takes oneconfigargument, an instance ofconfig_cls; seeBaseBacktester. Three engine conventions matter to a reader of the results:A weight row formed at bar
tfills at bart + 1at the market’s fill price (the weights are shifted one bar before they reachPortfolio.from_orders).A target percentage is sized against the price
config.sizing_basisnames."fill"(the default, vectorbt’s own) measures it against the portfolio valued at the fill price of the bar it executes on and divides by that price."valuation"uses the valuation price of the signal bar t instead (vectorbt’sval_price): the portfolio valued at t’s close, divided by t’s close, as an order placed after the close must be sized. Either way the order fills at t + 1’s fill price.No borrow or short-financing cost is modelled, so short-side returns are optimistic.
Execution at the fill bar (ADR 0014): both price columns are forward-filled before they reach vectorbt, which would otherwise keep a NaN-priced holding at its last value and silently skip every later rebalance of the whole group. On each fill bar the engine then does what a market would:
A NaN weight keeps the symbol’s holding; a row may mix NaN and finite targets.
An order whose raw (not forward-filled) fill price is NaN is a rejected order: the holding is kept and the order expires. It is recorded in
SimulationResult.rejected_orderswhen it would have traded (a non-zero target, or a symbol held before the bar).A symbol the price dataset marks as delisted on bar
b(MarketDataset.delisting_bars) is settled on barb + 1: the holding becomes cash at its last valuation price, with no fee or slippage, whatever the weights ask for it there. Each settlement is recorded inSimulationResult.settlements.
Records name the symbol by
symbol(the display name: the ticker the symbol traded under that day when the price store has a ticker lookup file, otherwise the axis label) andaxis_symbol(the label on the panel’ssymbolaxis). A symbol whose prices are NaN at the start of the window is not yet listed rather than delisted, and trades normally once it lists.Trade statistics use vectorbt’s position view: one trade is one symbol’s round trip from entry to flat, so trimming a holding back to its target weight is not a closed trade. vectorbt’s default exit-trade view counts every trim, and because only winners get trimmed under equal weighting it inflates the win rate. The number of fills is reported separately as
Total Ordersin the metrics.Examples
A concrete engine names its config class and market conventions and turns predictions into target weights:
class MyBacktester(VectorBtBacktester): config_cls = CrossSectionBacktestConfig MARKET = MarketSpec( fill_price_column="open", valuation_price_column="close", trading_days_per_year=252, session_minutes_per_day=390, ) def _generate_signals(self, predictions, prices, delisted): ... # a Dataset with ``weight`` on (timestamp, symbol) result = MyBacktester(config).run() result.simulation.orders # one record per fill
- STATS_METRICS = ('start', 'end', 'period', 'start_value', 'end_value', 'total_return', 'max_gross_exposure', 'total_fees_paid', 'max_dd', 'max_dd_duration', 'total_trades', 'total_closed_trades', 'total_open_trades', 'open_trade_pnl', 'win_rate', 'best_trade', 'worst_trade', 'avg_winning_trade', 'avg_losing_trade', 'avg_winning_trade_duration', 'avg_losing_trade_duration', 'profit_factor', 'expectancy', 'sharpe_ratio', 'calmar_ratio', 'omega_ratio', 'sortino_ratio')#
The
Portfolio.statsmetric names reported for the whole window.benchmark_returnis left out: vectorbt would compare against the equal-weighted traded universe, not the configured benchmark, which thebenchmarkandrelativemetric blocks report instead.
- fill_delay_bars = 1#
A weight formed at bar t fills at bar t + 1 (the Execution rules’
FILL_DELAY_BARS); labels must use this delay.