Portfolio Backtesting
Relevant source files
The following files were used as context for generating this wiki page:
The Portfolio Backtesting system in vectorbt is designed for modeling portfolio performance and calculating risk and performance metrics at scale. It utilizes a vectorized approach, packing thousands of strategy configurations into NumPy arrays and accelerating the simulation path using Numba-compiled functions or an optional Rust engine. The core logic revolves around creating positions allocated against a cash component, producing equity curves, and generating detailed statistics regarding trades, drawdowns, and profit metrics.
Sources: vectorbt/portfolio/base.py:12-25, README.md
Architecture and Workflow
The backtesting process is divided into four distinct phases: Preparation, Simulation, Construction, and Analysis. This lifecycle ensures that data is properly broadcast and validated before entering the high-performance simulation kernels.
Simulation Lifecycle
The simulation phase traverses the broadcasted shape element by element (row-wise for time, column-wise for assets). It manages the internal state including cash balances, asset positions, and debt for shorting.
Sources: vectorbt/portfolio/base.py:53-76
Simulation Modes
VectorBT provides three primary entry points for simulating a portfolio, ranging from simple predefined orders to complex event-driven logic.
1. From Orders
Portfolio.from_orders is the fastest simulation mode. It accepts arrays for size, price, and fees, broadcasting them to a target shape. It is ideal for strategies where orders are known at predefined timestamps.
Sources: vectorbt/portfolio/base.py:84-111
2. From Signals
Portfolio.from_signals adds an abstraction layer for automated signaling. It handles entry/exit logic, prevents redundant entries by default, and implements automated stop-loss (SL) and take-profit (TP) orders.
Sources: vectorbt/portfolio/base.py:133-145
3. From Order Function
Portfolio.from_order_func is an event-driven mode that uses callbacks to define arbitrary logic. It provides maximum flexibility, allowing multiple orders per symbol per bar (when flexible=True) and providing real-time access to position PnL during simulation.
Sources: vectorbt/portfolio/base.py:165-184
Performance and Acceleration
The "hot path" of the simulation is accelerated through two primary engines:
- Numba Engine: Uses JIT-compiled Python functions in
vectorbt/portfolio/nb.py. - Rust Engine: A precompiled engine implemented in
rust/src/portfolio.rsfor maximum speed without JIT overhead.
Data Structures (Records)
The simulation results are stored as structured NumPy arrays (records) to minimize memory footprint while allowing rapid analysis.
| Record Type | Description | Key Fields |
|---|---|---|
| Order | Individual execution events | id, col, idx, size, price, fees, side |
| Trade | Aggregated entry/exit pairs | size, entry_idx, exit_idx, pnl, return, direction |
| Log | Full state snapshots | cash, position, debt, free_cash, val_price, value |
Sources: vectorbt/portfolio/enums.py, rust/src/portfolio.rs:341-390, vectorbt/portfolio/nb.py:344-411
Risk and Performance Metrics
The Portfolio class offers extensive statistics through its .stats() and .returns_stats() methods.
Key Performance Indicators
- Profit Metrics: Total Return, Benchmark Return, Total Fees Paid, Profit Factor, Expectancy.
- Trade Metrics: Win Rate, Best/Worst Trade, Average Winning/Losing Trade, Max Win/Loss Streak.
- Risk Metrics: Sharpe Ratio, Calmar Ratio, Omega Ratio, Sortino Ratio, Max Drawdown, Max Drawdown Duration.
- Exposure: Gross Exposure (sum of absolute positions / value), Net Exposure.
Sources: vectorbt/portfolio/base.py:1145-1250, vectorbt/portfolio/trades.py:270-350
Trade Analysis Logic
Trades are sequences of orders starting with an opening order. VectorBT categorizes these into Entry Trades, Exit Trades, and Positions.
Sources: vectorbt/portfolio/trades.py:14-38
Capital Management
Cash Sharing and Grouping
Columns can be grouped to share capital using cash_sharing=True. This enables rebalancing strategies where assets compete for a single pool of cash.
- Call Sequence: When sharing cash, the order of execution matters.
CallSeqType.Autoautomatically executes sell orders before buy orders in a single tick to release funds. - Initial Cash: Can be fixed, or set to
InitCashMode.Autoto automatically determine the minimum required capital to avoid a negative balance.
Sources: vectorbt/portfolio/base.py:440-475, vectorbt/portfolio/nb.py:465-485
Shorting and Debt
The system supports shorting with optional cash locking (lock_cash). In short positions, debt represents the value of borrowed assets, and free_cash tracks the available balance after accounting for margins and locked collateral.
Sources: vectorbt/portfolio/nb.py:126-150, rust/src/portfolio.rs:480-505
Visualization
The system integrates with Plotly for interactive subplots.
- OHLCV: Overlays signals (Bullish/Bearish) on price candles.
- Portfolio Value: Equity curves compared against benchmarks.
- Trades/Orders: Visualizes entry and exit points on the price chart with profit/loss color-coding.
- Underwater Plot: Displays drawdown cycles over time.
Sources: vectorbt/portfolio/base.py:1330-1450, vectorbt/portfolio/trades.py:640-700
Conclusion
Portfolio Backtesting in vectorbt combines the flexibility of event-driven systems with the speed of vectorized computation. By leveraging Numba and Rust, it allows researchers to perform massive parameter sweeps across thousands of assets and configurations simultaneously, producing professional-grade risk analytics and interactive visualizations.