# VectorBT
VectorBT is a Python library for backtesting and analyzing trading strategies at scale. It leverages vectorized operations with pandas, NumPy, and Numba to enable testing thousands of trading ideas in seconds. The library provides a composable API for rapid experimentation, making it suitable for both human researchers and AI-driven workflows.
The core functionality includes fast portfolio backtesting with trades, positions, drawdowns, and performance analysis. It features a rich indicator ecosystem with support for custom indicators and popular TA libraries (TA-Lib, Pandas TA), built-in data access from multiple sources (Yahoo Finance, CCXT, Binance, Alpaca), signal-based tooling, and interactive visualization with Plotly. The library excels at hyperparameter optimization through flexible broadcasting for multi-asset analysis and large parameter sweeps.
## Data Download - Yahoo Finance
Download historical price data from Yahoo Finance using `YFData`. Supports stocks, ETFs, cryptocurrencies, and other Yahoo Finance symbols with flexible date ranges and intervals.
```python
import vectorbt as vbt
# Download single symbol with default settings (max period)
data = vbt.YFData.download("BTC-USD")
price = data.get("Close")
print(price.tail())
# Download with specific date range and interval
yf_data = vbt.YFData.download(
"TSLA",
start='2021-04-12 09:30:00 -0400',
end='2021-04-12 15:30:00 -0400',
interval='1m'
)
ohlcv = yf_data.get()
print(ohlcv)
# Output: DataFrame with Open, High, Low, Close, Volume, Dividends, Stock Splits
# Download multiple symbols
symbols = ["BTC-USD", "ETH-USD", "XRP-USD"]
data = vbt.YFData.download(symbols, missing_index="drop")
prices = data.get("Close")
print(prices.head())
# Update existing data with new bars
updated_data = yf_data.update(end='2021-04-12 16:00:00 -0400')
```
## Data Download - Binance
Download cryptocurrency OHLCV data from Binance exchange using `BinanceData`. Requires python-binance package and supports all Binance trading pairs and timeframes.
```python
import vectorbt as vbt
# Download recent 2 hours of 1-minute data
binance_data = vbt.BinanceData.download(
"BTCUSDT",
start='2 hours ago UTC',
end='now UTC',
interval='1m'
)
df = binance_data.get()
print(df.columns)
# Output: Open, High, Low, Close, Volume, Close time, Quote volume,
# Number of trades, Taker base volume, Taker quote volume
# Download with custom client credentials
from binance.client import Client
client = Client(api_key="your_key", api_secret="your_secret")
data = vbt.BinanceData.download("ETHUSDT", client=client, interval='1h')
# Update data incrementally
import time
time.sleep(60)
binance_data = binance_data.update()
```
## Data Download - CCXT
Download data from 100+ cryptocurrency exchanges using the unified CCXT interface with `CCXTData`. Supports any exchange that provides OHLCV data.
```python
import vectorbt as vbt
# Download from Binance (default) using CCXT
ccxt_data = vbt.CCXTData.download(
"BTC/USDT",
start='2 hours ago UTC',
end='now UTC',
timeframe='1m'
)
print(ccxt_data.get())
# Download from different exchanges
kraken_data = vbt.CCXTData.download(
"BTC/USD",
exchange='kraken',
timeframe='1h',
start='1 week ago UTC'
)
# With exchange configuration
config = {'enableRateLimit': True, 'timeout': 30000}
data = vbt.CCXTData.download(
"ETH/USDT",
exchange='binance',
config=config,
timeframe='15m'
)
```
## Data Download - Alpaca
Download stock and crypto data from Alpaca Markets using `AlpacaData`. Requires alpaca-py package and Alpaca API credentials.
```python
import vectorbt as vbt
# Configure Alpaca credentials in settings or environment
# vbt.settings.data['alpaca']['api_key'] = 'your_key'
# vbt.settings.data['alpaca']['secret_key'] = 'your_secret'
# Download stock data
alpaca_data = vbt.AlpacaData.download(
"AAPL",
start='2 hours ago UTC',
end='15 minutes ago UTC',
timeframe='1m'
)
print(alpaca_data.get())
# Download crypto data (use "/" in symbol)
crypto_data = vbt.AlpacaData.download(
"BTC/USD",
timeframe='1h',
start='1 day ago UTC'
)
# Multiple timeframes
daily_data = vbt.AlpacaData.download("MSFT", timeframe='1d', start='1 year ago')
```
## Synthetic Data - GBM
Generate synthetic price data using Geometric Brownian Motion with `GBMData`. Useful for strategy testing and Monte Carlo simulations.
```python
import vectorbt as vbt
# Generate single synthetic price series
gbm_data = vbt.GBMData.download(
'GBM',
start='2 hours ago',
end='now',
freq='1min',
seed=42 # For reproducibility
)
print(gbm_data.get().head())
# Generate with custom parameters
gbm_data = vbt.GBMData.download(
'SYNTHETIC',
start='2020-01-01',
end='2021-01-01',
freq='1D',
S0=100.0, # Initial price
mu=0.05, # Drift (mean return)
sigma=0.2, # Volatility
I=5, # Number of paths (columns)
seed=42
)
paths = gbm_data.get()
print(paths.head())
# Update with new data points
gbm_data = gbm_data.update()
```
## Portfolio - Buy and Hold
Create a buy-and-hold portfolio using `Portfolio.from_holding`. The simplest portfolio simulation that invests initial cash at the start and holds until the end.
```python
import vectorbt as vbt
# Download data
data = vbt.YFData.download("BTC-USD", start="2020-01-01")
price = data.get("Close")
# Create buy-and-hold portfolio
pf = vbt.Portfolio.from_holding(price, init_cash=10000)
# Get key metrics
print(f"Total Profit: ${pf.total_profit():.2f}")
print(f"Total Return: {pf.total_return() * 100:.2f}%")
print(f"Max Drawdown: {pf.max_drawdown() * 100:.2f}%")
print(f"Sharpe Ratio: {pf.sharpe_ratio():.3f}")
# Full statistics
print(pf.stats())
# Plot portfolio performance
fig = pf.plot()
fig.show()
```
## Portfolio - From Signals
Create a portfolio from entry/exit signals using `Portfolio.from_signals`. Supports long-only, short-only, and both directions with stop-loss and take-profit orders.
```python
import vectorbt as vbt
# Download data and calculate indicators
data = vbt.YFData.download("BTC-USD", start="2020-01-01")
price = data.get("Close")
# Calculate moving averages
fast_ma = vbt.MA.run(price, 10)
slow_ma = vbt.MA.run(price, 50)
# Generate crossover signals
entries = fast_ma.ma_crossed_above(slow_ma)
exits = fast_ma.ma_crossed_below(slow_ma)
# Create portfolio with default settings (invest all cash)
pf = vbt.Portfolio.from_signals(
price,
entries,
exits,
init_cash=10000,
fees=0.001, # 0.1% fees
slippage=0.001 # 0.1% slippage
)
print(f"Total Return: {pf.total_return() * 100:.2f}%")
print(f"Number of Trades: {pf.trades.count()}")
# With fixed position size
pf = vbt.Portfolio.from_signals(
price,
entries,
exits,
size=0.5, # 50% of cash per trade
init_cash=10000
)
# With stop-loss and take-profit
pf = vbt.Portfolio.from_signals(
price,
entries,
exits,
init_cash=10000,
sl_stop=0.05, # 5% stop-loss
tp_stop=0.10 # 10% take-profit
)
# Short-only strategy
pf_short = vbt.Portfolio.from_signals(
price,
entries,
exits,
direction='shortonly',
init_cash=10000
)
```
## Portfolio - From Orders
Create a portfolio from explicit order sizes using `Portfolio.from_orders`. Most direct simulation method where you specify exact order sizes for each timestamp.
```python
import vectorbt as vbt
import pandas as pd
import numpy as np
# Create price data
price = pd.DataFrame({
'a': [1, 2, 3, 4, 5],
'b': [5, 4, 3, 2, 1]
})
# Define order sizes (positive = buy, negative = sell)
size = pd.Series([1, -1, 1, -1, 0])
# Create portfolio with different directions per column
pf = vbt.Portfolio.from_orders(
price,
size,
direction=['longonly', 'shortonly'],
fees=0.01,
init_cash=100
)
# View order records
print(pf.orders.records_readable)
# Using value-based sizing (order value in $)
result = pd.DataFrame(np.random.randn(100, 3) * 10) # Signal values
size = result / price # Convert to share counts
pf = vbt.Portfolio.from_orders(price, size, init_cash='autoalign')
# Accumulating orders (multiple positions)
pf = vbt.Portfolio.from_orders(
price,
size=1, # Buy 1 share each bar
accumulate=True,
init_cash=1000
)
```
## Portfolio - From Order Function
Create a portfolio with custom logic using `Portfolio.from_order_func`. Most flexible method using Numba-compiled callbacks for complex strategies.
```python
import vectorbt as vbt
import numpy as np
from numba import njit
from vectorbt.portfolio import nb
from vectorbt.portfolio.enums import Direction
# Define order function
@njit
def order_func_nb(c, size, direction, fees):
return nb.order_nb(
price=c.close[c.i, c.col],
size=size[c.i],
direction=direction[c.col],
fees=fees
)
# Setup data
price = pd.DataFrame({'a': [1, 2, 3, 4], 'b': [4, 3, 2, 1]})
size = np.array([1, -1, 1, -1])
direction = np.array([Direction.LongOnly, Direction.ShortOnly])
# Run simulation
pf = vbt.Portfolio.from_order_func(
price,
order_func_nb,
size, direction, 0.01 # Arguments passed to order_func_nb
)
print(pf.orders.records_readable)
# With segment preparation (process groups of columns together)
@njit
def segment_prep_func_nb(c):
# Called once per row before processing columns
return ()
@njit
def order_func_with_prep_nb(c, size):
if c.i == 0:
return nb.order_nb(price=c.close[c.i, c.col], size=size)
return nb.order_nb() # No order
pf = vbt.Portfolio.from_order_func(
price,
order_func_with_prep_nb,
10.0, # Buy 10 shares at start
segment_prep_func_nb=segment_prep_func_nb
)
```
## Portfolio - Random Signals
Generate portfolios with random entry/exit signals using `Portfolio.from_random_signals`. Useful for benchmarking strategies against random trading.
```python
import vectorbt as vbt
import numpy as np
# Download data
symbols = ["BTC-USD", "ETH-USD"]
data = vbt.YFData.download(symbols, missing_index="drop")
price = data.get("Close")
# Generate 1000 random strategies with varying trade counts
n = np.random.randint(10, 101, size=1000).tolist()
pf = vbt.Portfolio.from_random_signals(
price,
n=n, # Number of entries per strategy
init_cash=100,
seed=42 # For reproducibility
)
# Analyze results
mean_expectancy = pf.trades.expectancy().groupby(["randnx_n", "symbol"]).mean()
print(mean_expectancy.describe())
# Compare to systematic strategy
fast_ma = vbt.MA.run(price, 10)
slow_ma = vbt.MA.run(price, 50)
entries = fast_ma.ma_crossed_above(slow_ma)
exits = fast_ma.ma_crossed_below(slow_ma)
systematic_pf = vbt.Portfolio.from_signals(price, entries, exits, init_cash=100)
print(f"Random Mean Return: {pf.total_return().mean():.2%}")
print(f"Systematic Return: {systematic_pf.total_return().mean():.2%}")
```
## Portfolio - Statistics and Analysis
Access comprehensive portfolio statistics and performance metrics. VectorBT provides dozens of built-in metrics including risk-adjusted returns, drawdowns, and trade analysis.
```python
import vectorbt as vbt
# Create portfolio
data = vbt.YFData.download("BTC-USD", start="2020-01-01")
price = data.get("Close")
pf = vbt.Portfolio.from_holding(price, init_cash=10000)
# Full statistics
stats = pf.stats()
print(stats)
# Includes: Start/End Value, Total Return, Benchmark Return,
# Max Drawdown, Sharpe/Calmar/Sortino Ratios, Win Rate, etc.
# Individual metrics
print(f"Total Return: {pf.total_return():.2%}")
print(f"Annual Return: {pf.annualized_return():.2%}")
print(f"Max Drawdown: {pf.max_drawdown():.2%}")
print(f"Sharpe Ratio: {pf.sharpe_ratio():.3f}")
print(f"Sortino Ratio: {pf.sortino_ratio():.3f}")
print(f"Calmar Ratio: {pf.calmar_ratio():.3f}")
# Trade analysis
print(f"Total Trades: {pf.trades.count()}")
print(f"Win Rate: {pf.trades.win_rate():.2%}")
print(f"Profit Factor: {pf.trades.profit_factor():.2f}")
print(f"Expectancy: ${pf.trades.expectancy():.2f}")
# Drawdown analysis
print(f"Max Drawdown Duration: {pf.drawdowns.max_duration()}")
print(f"Average Drawdown: {pf.drawdowns.avg_drawdown():.2%}")
# Value over time
portfolio_value = pf.value()
daily_returns = pf.returns()
cumulative_returns = pf.cumulative_returns()
```
## Portfolio - Multi-Asset and Grouping
Analyze portfolios across multiple assets with optional cash sharing between groups. Supports column grouping for combined portfolio analysis.
```python
import vectorbt as vbt
import pandas as pd
import numpy as np
# Download multi-asset data
symbols = ['BTC-USD', 'ETH-USD', 'XRP-USD', 'BNB-USD', 'LTC-USD', 'BCH-USD']
data = vbt.YFData.download(symbols, start='2020-01-01', end='2021-01-01')
ohlcv = data.concat()
price = ohlcv['Close']
# Generate signals
size = pd.DataFrame(np.random.randn(len(price), len(symbols)) * 100,
index=price.index, columns=price.columns)
size = size / ohlcv['Open']
# Independent portfolios per asset
pf = vbt.Portfolio.from_orders(
ohlcv['Close'],
size,
price=ohlcv['Open'],
init_cash='autoalign', # Auto-calculate required cash
fees=0.001
)
print(pf.total_profit()) # Per symbol
# Grouped portfolios with cash sharing
group_by = pd.Index(['crypto1', 'crypto1', 'crypto1', 'crypto2', 'crypto2', 'crypto2'])
comb_pf = vbt.Portfolio.from_orders(
ohlcv['Close'],
size,
price=ohlcv['Open'],
init_cash='autoalign',
fees=0.001,
group_by=group_by,
cash_sharing=True # Share cash within groups
)
# Group-level metrics
print(comb_pf.total_profit()) # Per group
# Column-level metrics within grouped portfolio
print(comb_pf.total_profit(group_by=False)) # Per symbol
# Indexing grouped portfolio
crypto1_pf = comb_pf['crypto1']
print(crypto1_pf.stats())
```
## Indicators - Moving Average
Calculate moving averages using the built-in `MA` indicator. Supports both simple (SMA) and exponential (EMA) moving averages with multiple window sizes.
```python
import vectorbt as vbt
# Download data
data = vbt.YFData.download("BTC-USD", start="2020-01-01")
price = data.get("Close")
# Simple moving average
ma = vbt.MA.run(price, window=20)
print(ma.ma.tail())
# Multiple windows at once
ma_multi = vbt.MA.run(price, window=[10, 20, 50])
print(ma_multi.ma.tail())
# Output has MultiIndex columns: (ma_window, ma_ewm)
# Exponential moving average
ema = vbt.MA.run(price, window=20, ewm=True)
print(ema.ma.tail())
# Crossover signals
fast_ma = vbt.MA.run(price, 10)
slow_ma = vbt.MA.run(price, 50)
# Built-in crossover detection
crossed_above = fast_ma.ma_crossed_above(slow_ma)
crossed_below = fast_ma.ma_crossed_below(slow_ma)
print(f"Bullish crossovers: {crossed_above.sum()}")
print(f"Bearish crossovers: {crossed_below.sum()}")
# Plot
fig = ma.plot()
fig.show()
```
## Indicators - Bollinger Bands
Calculate Bollinger Bands using the `BBANDS` indicator. Includes upper band, lower band, middle band, %B, and bandwidth.
```python
import vectorbt as vbt
# Download data
data = vbt.YFData.download("BTC-USD", start="2020-01-01")
price = data.get("Close")
# Calculate Bollinger Bands (default: 20-period, 2 std)
bbands = vbt.BBANDS.run(price)
# Access band values
print("Middle Band:", bbands.middle.tail())
print("Upper Band:", bbands.upper.tail())
print("Lower Band:", bbands.lower.tail())
# Custom parameters
bbands_custom = vbt.BBANDS.run(
price,
window=20,
alpha=2.5, # Standard deviations
ewm=True # Use EMA instead of SMA
)
# %B indicator (position within bands)
percent_b = bbands.percent_b
print("Percent B:", percent_b.tail())
# 0 = at lower band, 0.5 = at middle, 1 = at upper band
# Bandwidth (volatility indicator)
bandwidth = bbands.bandwidth
print("Bandwidth:", bandwidth.tail())
# Generate signals
oversold = percent_b < 0 # Price below lower band
overbought = percent_b > 1 # Price above upper band
# Plot
fig = bbands.plot()
fig.show()
```
## Indicators - RSI
Calculate the Relative Strength Index using the `RSI` indicator. Measures momentum to identify overbought and oversold conditions.
```python
import vectorbt as vbt
# Download data
data = vbt.YFData.download("BTC-USD", start="2020-01-01")
price = data.get("Close")
# Calculate RSI (default: 14-period)
rsi = vbt.RSI.run(price)
print(rsi.rsi.tail())
# Multiple periods
rsi_multi = vbt.RSI.run(price, window=[7, 14, 21])
print(rsi_multi.rsi.tail())
# EMA-based RSI (Wilder's smoothing)
rsi_ema = vbt.RSI.run(price, window=14, ewm=True)
# Generate signals
oversold = rsi.rsi < 30
overbought = rsi.rsi > 70
# Crossover signals
crossed_above_30 = rsi.rsi_crossed_above(30)
crossed_below_70 = rsi.rsi_crossed_below(70)
# RSI divergence detection (price makes new high, RSI doesn't)
price_highs = price.rolling(20).max() == price
rsi_highs = rsi.rsi.rolling(20).max() == rsi.rsi
bearish_divergence = price_highs & ~rsi_highs
# Plot with overbought/oversold levels
fig = rsi.plot(levels=(30, 70))
fig.show()
```
## Indicators - MACD
Calculate the Moving Average Convergence Divergence using the `MACD` indicator. Includes MACD line, signal line, and histogram.
```python
import vectorbt as vbt
# Download data
data = vbt.YFData.download("BTC-USD", start="2020-01-01")
price = data.get("Close")
# Calculate MACD (default: 12/26/9)
macd = vbt.MACD.run(price)
# Access components
print("MACD Line:", macd.macd.tail())
print("Signal Line:", macd.signal.tail())
print("Histogram:", macd.hist.tail())
# Custom parameters
macd_custom = vbt.MACD.run(
price,
fast_window=8,
slow_window=21,
signal_window=5
)
# Generate signals
bullish_cross = macd.macd_crossed_above(macd.signal)
bearish_cross = macd.macd_crossed_below(macd.signal)
# Zero line crossovers
above_zero = macd.macd_crossed_above(0)
below_zero = macd.macd_crossed_below(0)
# Histogram turning points
hist_turning_up = (macd.hist > macd.hist.shift(1)) & (macd.hist.shift(1) < macd.hist.shift(2))
hist_turning_down = (macd.hist < macd.hist.shift(1)) & (macd.hist.shift(1) > macd.hist.shift(2))
# Plot
fig = macd.plot()
fig.show()
```
## Indicators - ATR and Stochastic
Calculate Average True Range (volatility) and Stochastic Oscillator (momentum) indicators.
```python
import vectorbt as vbt
# Download OHLCV data
data = vbt.YFData.download("BTC-USD", start="2020-01-01")
high = data.get("High")
low = data.get("Low")
close = data.get("Close")
# ATR - Average True Range
atr = vbt.ATR.run(high, low, close, window=14)
print("ATR:", atr.atr.tail())
# ATR for position sizing (volatility-adjusted)
atr_pct = atr.atr / close * 100
position_size = 1 / atr_pct # Inverse volatility sizing
# Stochastic Oscillator
stoch = vbt.STOCH.run(high, low, close, k_window=14, d_window=3)
print("Stochastic %K:", stoch.percent_k.tail())
print("Stochastic %D:", stoch.percent_d.tail())
# Stochastic signals
oversold = stoch.percent_k < 20
overbought = stoch.percent_k > 80
bullish_cross = stoch.percent_k_crossed_above(stoch.percent_d)
bearish_cross = stoch.percent_k_crossed_below(stoch.percent_d)
# Combined signal: oversold + bullish cross
buy_signal = oversold.shift(1) & bullish_cross
# Plot
fig = stoch.plot()
fig.show()
```
## Indicators - Custom with IndicatorFactory
Create custom indicators using `IndicatorFactory`. Build any indicator with automatic parameter optimization and signal generation.
```python
import vectorbt as vbt
import numpy as np
from numba import njit
# Simple custom indicator
@njit
def custom_ma_nb(close, window):
"""Custom moving average with Numba acceleration."""
out = np.full(close.shape, np.nan)
for col in range(close.shape[1]):
for i in range(window, close.shape[0]):
out[i, col] = np.mean(close[i-window:i, col])
return out
# Create indicator class
CustomMA = vbt.IndicatorFactory(
input_names=['close'],
param_names=['window'],
output_names=['ma']
).from_apply_func(custom_ma_nb)
# Use the indicator
price = vbt.YFData.download("BTC-USD").get("Close")
custom_ma = CustomMA.run(price, window=[10, 20, 50])
print(custom_ma.ma.tail())
# More complex indicator with multiple outputs
@njit
def bb_custom_nb(close, window, mult):
"""Custom Bollinger Bands."""
ma = np.full(close.shape, np.nan)
upper = np.full(close.shape, np.nan)
lower = np.full(close.shape, np.nan)
for col in range(close.shape[1]):
for i in range(window, close.shape[0]):
data = close[i-window:i, col]
ma[i, col] = np.mean(data)
std = np.std(data)
upper[i, col] = ma[i, col] + mult * std
lower[i, col] = ma[i, col] - mult * std
return ma, upper, lower
CustomBB = vbt.IndicatorFactory(
input_names=['close'],
param_names=['window', 'mult'],
output_names=['ma', 'upper', 'lower']
).from_apply_func(bb_custom_nb)
bb = CustomBB.run(price, window=20, mult=[1.5, 2.0, 2.5])
print(bb.upper.tail())
# Parameter combinations with run_combs
fast, slow = CustomMA.run_combs(price, window=[10, 20, 30, 50])
crossovers = fast.ma_crossed_above(slow.ma)
print(f"Total crossover signals: {crossovers.sum().sum()}")
```
## Indicators - TA-Lib Integration
Use TA-Lib indicators through VectorBT's `IndicatorFactory.from_talib`. Provides vectorized versions of all TA-Lib functions with automatic parameter optimization.
```python
import vectorbt as vbt
# Download OHLCV data
data = vbt.YFData.download("BTC-USD", start="2020-01-01")
open_price = data.get("Open")
high = data.get("High")
low = data.get("Low")
close = data.get("Close")
# Create TA-Lib indicator class
SMA = vbt.IndicatorFactory.from_talib('SMA')
sma = SMA.run(close, timeperiod=[10, 20, 50])
print(sma.real.tail())
# RSI from TA-Lib
RSI = vbt.IndicatorFactory.from_talib('RSI')
rsi = RSI.run(close, timeperiod=14)
print(rsi.real.tail())
# BBANDS from TA-Lib
BBANDS = vbt.IndicatorFactory.from_talib('BBANDS')
bbands = BBANDS.run(close, timeperiod=20, nbdevup=2, nbdevdn=2)
print("Upper:", bbands.upperband.tail())
print("Middle:", bbands.middleband.tail())
print("Lower:", bbands.lowerband.tail())
# Pattern recognition
CDL_DOJI = vbt.IndicatorFactory.from_talib('CDLDOJI')
doji = CDL_DOJI.run(open_price, high, low, close)
doji_signals = doji.integer != 0
# Run all pattern recognition
import talib
patterns = []
for pattern in talib.get_function_groups()['Pattern Recognition']:
PatternInd = vbt.IndicatorFactory.from_talib(pattern)
result = PatternInd.run(open_price, high, low, close)
patterns.append(result.integer)
# Shortcut function
rsi = vbt.talib('RSI').run(close, timeperiod=14)
```
## Hyperparameter Optimization
Test thousands of parameter combinations efficiently using VectorBT's broadcasting and `run_combs`. Visualize results with heatmaps.
```python
import vectorbt as vbt
import numpy as np
# Download data
symbols = ["BTC-USD", "ETH-USD", "XRP-USD"]
data = vbt.YFData.download(symbols, start="2020-01-01", missing_index="drop")
price = data.get("Close")
# Test all combinations of fast/slow MA windows
windows = np.arange(5, 51)
fast_ma, slow_ma = vbt.MA.run_combs(
price,
window=windows,
r=2, # Combinations of 2
short_names=['fast', 'slow']
)
# Generate signals and backtest
entries = fast_ma.ma_crossed_above(slow_ma)
exits = fast_ma.ma_crossed_below(slow_ma)
pf = vbt.Portfolio.from_signals(
price,
entries,
exits,
size=np.inf,
fees=0.001,
freq="1D"
)
# Analyze results
total_returns = pf.total_return()
print(f"Total combinations tested: {len(total_returns)}")
print(f"Best return: {total_returns.max():.2%}")
print(f"Worst return: {total_returns.min():.2%}")
# Find best parameters
best_idx = total_returns.idxmax()
print(f"Best parameters: {best_idx}")
# Create heatmap visualization
fig = pf.total_return().vbt.heatmap(
x_level='fast_window',
y_level='slow_window',
slider_level='symbol',
symmetric=True,
trace_kwargs=dict(
colorbar=dict(title="Total Return", tickformat="%")
)
)
fig.show()
# Get stats for specific configuration
best_pf = pf[best_idx]
print(best_pf.stats())
```
## Visualization - Portfolio Plots
Create interactive visualizations of portfolio performance, trades, and metrics using Plotly integration.
```python
import vectorbt as vbt
# Create portfolio
data = vbt.YFData.download("BTC-USD", start="2020-01-01")
price = data.get("Close")
fast_ma = vbt.MA.run(price, 10)
slow_ma = vbt.MA.run(price, 50)
entries = fast_ma.ma_crossed_above(slow_ma)
exits = fast_ma.ma_crossed_below(slow_ma)
pf = vbt.Portfolio.from_signals(price, entries, exits, init_cash=10000, fees=0.001)
# Full portfolio plot (value, drawdowns, trades)
fig = pf.plot()
fig.show()
# Individual component plots
fig_value = pf.value().vbt.plot(title="Portfolio Value")
fig_value.show()
fig_returns = pf.cumulative_returns().vbt.plot(title="Cumulative Returns")
fig_returns.show()
fig_drawdowns = pf.drawdowns.plot()
fig_drawdowns.show()
# Trade visualization
fig_trades = pf.trades.plot()
fig_trades.show()
# Orders visualization
fig_orders = pf.orders.plot()
fig_orders.show()
# Subplots with custom layout
fig = vbt.make_subplots(rows=2, cols=1, shared_xaxes=True)
pf.value().vbt.plot(add_trace_kwargs=dict(row=1, col=1), fig=fig)
pf.drawdowns.drawdown.vbt.plot(add_trace_kwargs=dict(row=2, col=1), fig=fig)
fig.update_layout(height=600)
fig.show()
```
## Visualization - Heatmaps and Analysis
Create heatmaps, scatter plots, and other analytical visualizations for multi-dimensional data exploration.
```python
import vectorbt as vbt
import numpy as np
# Multi-parameter optimization results
data = vbt.YFData.download("BTC-USD", start="2020-01-01")
price = data.get("Close")
windows = np.arange(5, 31, 5)
fast_ma, slow_ma = vbt.MA.run_combs(price, window=windows, r=2, short_names=['fast', 'slow'])
entries = fast_ma.ma_crossed_above(slow_ma)
exits = fast_ma.ma_crossed_below(slow_ma)
pf = vbt.Portfolio.from_signals(price, entries, exits, fees=0.001)
# Heatmap of returns by parameters
returns = pf.total_return()
fig = returns.vbt.heatmap(
x_level='fast_window',
y_level='slow_window',
trace_kwargs=dict(colorscale='RdYlGn', zmid=0)
)
fig.show()
# Scatter plot
sharpe = pf.sharpe_ratio()
fig = vbt.make_figure()
fig.add_scatter(
x=returns.values,
y=sharpe.values,
mode='markers',
text=[str(i) for i in returns.index],
hovertemplate='Return: %{x:.2%}
Sharpe: %{y:.2f}
Params: %{text}'
)
fig.update_layout(xaxis_title='Total Return', yaxis_title='Sharpe Ratio')
fig.show()
# Time series heatmap (for multiple assets/parameters over time)
multi_data = vbt.YFData.download(["BTC-USD", "ETH-USD", "XRP-USD"], start="2020-01-01")
multi_price = multi_data.get("Close")
returns = multi_price.pct_change()
fig = returns.vbt.ts_heatmap(
trace_kwargs=dict(colorscale='RdYlGn', zmid=0)
)
fig.show()
# Box plot for distribution analysis
trades = pf.trades.pnl
fig = trades.vbt.boxplot()
fig.show()
```
## Walk-Forward Optimization
Implement walk-forward optimization using VectorBT's splitters for robust strategy validation.
```python
import vectorbt as vbt
import numpy as np
import pandas as pd
# Download data
data = vbt.YFData.download("BTC-USD", start="2018-01-01", end="2023-01-01")
price = data.get("Close")
# Create rolling window splitter
splitter = vbt.RollingSplitter(
price.index,
n=252 * 2, # 2-year windows
split=0.7, # 70% train, 30% test
set_labels=['train', 'test']
)
# Visualize splits
fig = splitter.plot()
fig.show()
# Walk-forward optimization
windows = np.arange(10, 51, 5)
results = []
for i, (train_idx, test_idx) in enumerate(splitter.split(price.index)):
train_price = price.iloc[train_idx]
test_price = price.iloc[test_idx]
# Optimize on training set
fast_ma, slow_ma = vbt.MA.run_combs(train_price, window=windows, r=2,
short_names=['fast', 'slow'])
entries = fast_ma.ma_crossed_above(slow_ma)
exits = fast_ma.ma_crossed_below(slow_ma)
train_pf = vbt.Portfolio.from_signals(train_price, entries, exits, fees=0.001)
# Find best parameters
best_params = train_pf.sharpe_ratio().idxmax()
best_fast, best_slow = best_params[0], best_params[1]
# Test on out-of-sample data
fast_ma = vbt.MA.run(test_price, best_fast)
slow_ma = vbt.MA.run(test_price, best_slow)
entries = fast_ma.ma_crossed_above(slow_ma)
exits = fast_ma.ma_crossed_below(slow_ma)
test_pf = vbt.Portfolio.from_signals(test_price, entries, exits, fees=0.001)
results.append({
'fold': i,
'best_fast': best_fast,
'best_slow': best_slow,
'train_sharpe': train_pf[best_params].sharpe_ratio(),
'test_sharpe': test_pf.sharpe_ratio(),
'test_return': test_pf.total_return()
})
results_df = pd.DataFrame(results)
print(results_df)
print(f"\nAverage OOS Sharpe: {results_df['test_sharpe'].mean():.3f}")
print(f"Average OOS Return: {results_df['test_return'].mean():.2%}")
```
## Pandas Accessors
Use VectorBT's pandas accessors (`.vbt`) for enhanced DataFrame/Series operations including plotting, resampling, and transformations.
```python
import vectorbt as vbt
import pandas as pd
import numpy as np
# Download data
data = vbt.YFData.download("BTC-USD", start="2020-01-01")
price = data.get("Close")
returns = price.pct_change()
# Plotting accessor
fig = price.vbt.plot(title="BTC Price")
fig.show()
fig = returns.vbt.histplot(title="Return Distribution")
fig.show()
# Resampling with aggregation
weekly_price = price.vbt.resample('W').last()
monthly_returns = returns.vbt.resample('M').sum()
# Rolling operations
rolling_mean = price.vbt.rolling_mean(20)
rolling_std = price.vbt.rolling_std(20)
# Drawdown analysis
drawdown = price.vbt.to_drawdown_series()
print(f"Current Drawdown: {drawdown.iloc[-1]:.2%}")
# Signals accessor
signals = pd.Series([True, False, True, True, False, True], index=pd.date_range('2020', periods=6))
first_signals = signals.vbt.signals.first() # First True in each sequence
print(first_signals)
# Ranking and normalization
normalized = price.vbt.normalize() # 0-1 scaling
zscore = (price - price.rolling(20).mean()) / price.rolling(20).std()
# Concatenation with proper labeling
prices = vbt.YFData.download(["BTC-USD", "ETH-USD"]).get("Close")
combined = pd.DataFrame.vbt.concat(
prices['BTC-USD'],
prices['ETH-USD'],
keys=['Bitcoin', 'Ethereum']
)
print(combined.tail())
# Empty DataFrame with structure
empty = pd.DataFrame.vbt.empty_like(prices, fill_value=0.0)
```
## Summary
VectorBT provides a comprehensive toolkit for quantitative trading research with three primary use cases: rapid strategy backtesting through vectorized portfolio simulation (`Portfolio.from_signals`, `Portfolio.from_orders`), large-scale parameter optimization leveraging broadcasting and `run_combs` for testing thousands of configurations simultaneously, and multi-asset portfolio analysis with grouping and cash-sharing capabilities. The library integrates seamlessly with the pandas ecosystem through custom accessors while achieving high performance via Numba-accelerated computations.
Integration patterns typically follow a data-indicator-portfolio-analysis workflow: fetch market data using built-in downloaders (`YFData`, `BinanceData`, `CCXTData`), compute technical indicators with the indicator factory system, simulate portfolios with the appropriate method based on strategy complexity, and analyze results using the extensive metrics and visualization tools. For production systems, VectorBT supports incremental data updates, scheduled automation via the `DataUpdater` class, and Telegram notifications for alerts. The `IndicatorFactory` pattern enables creation of reusable, optimized indicator classes that can be shared across projects and integrated with existing TA libraries through `from_talib` and `from_pandas_ta` adapters.