vectorbt
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseVectorized Backtesting with vectorbt
使用 vectorbt 进行向量化回测
Overview
概述
vectorbt is a Python library for vectorized backtesting — running strategy simulations using NumPy/pandas array operations instead of bar-by-bar loops. This makes it 100–1000x faster than event-driven frameworks (backtrader, zipline), enabling parameter optimization across thousands of combinations in seconds.
Key strengths:
- Blazing speed via NumPy vectorization
- Built-in parameter grid search and optimization
- 50+ built-in performance metrics (Sharpe, Sortino, Calmar, max drawdown, profit factor)
- Rich plotting (equity curves, drawdowns, trade markers, heatmaps)
- Native pandas integration — your data stays in DataFrames throughout
vectorbt 是一个用于向量化回测的 Python 库——它使用 NumPy/pandas 数组操作来运行策略模拟,而不是逐根 K 线循环。这使它比事件驱动框架(backtrader、zipline)快 100–1000 倍,并能在数秒内完成数千种参数组合的优化。
主要优势:
- 通过 NumPy 向量化实现极高性能
- 内置参数网格搜索和优化
- 内置 50 多种绩效指标(Sharpe、Sortino、Calmar、最大回撤、盈利因子)
- 丰富的绘图功能(净值曲线、回撤、交易标记、热力图)
- 原生集成 pandas——数据在整个流程中始终保留为 DataFrame
Installation
安装
bash
uv pip install vectorbt pandas numpyvectorbt pulls in pandas, NumPy, and Plotly automatically. For technical indicators, also install pandas-ta:
bash
uv pip install vectorbt pandas-tabash
uv pip install vectorbt pandas numpyvectorbt 会自动安装 pandas、NumPy 和 Plotly。如需技术指标,还需安装 pandas-ta:
bash
uv pip install vectorbt pandas-taCore Concepts
核心概念
1. Signals — Boolean Entry/Exit Arrays
1. 信号——布尔型入场/退出数组
Strategies in vectorbt are expressed as boolean pandas Series (or arrays) indicating where to enter and exit positions:
python
import vectorbt as vbt
import pandas as pdvectorbt 中的策略表示为布尔型 pandas Series(或数组),用于指示何时入场和退出仓位:
python
import vectorbt as vbt
import pandas as pdEntry: buy when fast EMA crosses above slow EMA
Entry: buy when fast EMA crosses above slow EMA
entries = fast_ema > slow_ema
entries = fast_ema > slow_ema
Exit: sell when fast EMA crosses below slow EMA
Exit: sell when fast EMA crosses below slow EMA
exits = fast_ema < slow_ema
vectorbt resolves conflicting signals automatically (you can't enter while already in a position).exits = fast_ema < slow_ema
vectorbt 会自动解决冲突信号(持有仓位时无法再次入场)。2. Portfolio — The Backtesting Engine
2. 投资组合——回测引擎
vbt.Portfolio.from_signals()python
pf = vbt.Portfolio.from_signals(
close=close_prices,
entries=entries,
exits=exits,
init_cash=10_000,
fees=0.003, # 0.3% per trade
slippage=0.005, # 0.5% slippage
freq="1h", # hourly data
)vbt.Portfolio.from_signals()python
pf = vbt.Portfolio.from_signals(
close=close_prices,
entries=entries,
exits=exits,
init_cash=10_000,
fees=0.003, # 0.3% per trade
slippage=0.005, # 0.5% slippage
freq="1h", # hourly data
)3. Metrics — Built-in Performance Analysis
3. 指标——内置绩效分析
python
undefinedpython
undefinedFull stats summary
Full stats summary
print(pf.stats())
print(pf.stats())
Individual metrics
Individual metrics
print(f"Total Return: {pf.total_return():.2%}")
print(f"Sharpe Ratio: {pf.sharpe_ratio():.3f}")
print(f"Max Drawdown: {pf.max_drawdown():.2%}")
print(f"Win Rate: {pf.trades.win_rate():.2%}")
undefinedprint(f"Total Return: {pf.total_return():.2%}")
print(f"Sharpe Ratio: {pf.sharpe_ratio():.3f}")
print(f"Max Drawdown: {pf.max_drawdown():.2%}")
print(f"Win Rate: {pf.trades.win_rate():.2%}")
undefined4. Parameter Optimization — Grid Search in Seconds
4. 参数优化——数秒完成网格搜索
Pass arrays instead of scalars to test many parameter combos simultaneously:
python
import numpy as np
fast_periods = np.arange(5, 25, 2) # 10 values
slow_periods = np.arange(20, 60, 5) # 8 values
fast_ma = vbt.MA.run(close, fast_periods, short_name="fast")
slow_ma = vbt.MA.run(close, slow_periods, short_name="slow")传入数组而非标量,可以同时测试多组参数组合:
python
import numpy as np
fast_periods = np.arange(5, 25, 2) # 10 values
slow_periods = np.arange(20, 60, 5) # 8 values
fast_ma = vbt.MA.run(close, fast_periods, short_name="fast")
slow_ma = vbt.MA.run(close, slow_periods, short_name="slow")This creates 80 parameter combinations automatically
This creates 80 parameter combinations automatically
entries = fast_ma.ma_crossed_above(slow_ma)
exits = fast_ma.ma_crossed_below(slow_ma)
undefinedentries = fast_ma.ma_crossed_above(slow_ma)
exits = fast_ma.ma_crossed_below(slow_ma)
undefinedBasic Workflow
基本工作流
Step 1: Load OHLCV Data
第 1 步:加载 OHLCV 数据
python
import pandas as pdpython
import pandas as pdFrom CSV
From CSV
df = pd.read_csv("ohlcv.csv", parse_dates=["timestamp"], index_col="timestamp")
close = df["close"]
df = pd.read_csv("ohlcv.csv", parse_dates=["timestamp"], index_col="timestamp")
close = df["close"]
From Yahoo Finance (traditional markets)
From Yahoo Finance (traditional markets)
btc = vbt.YFData.download("BTC-USD", start="2023-01-01", end="2025-01-01")
close = btc.get("Close")
For Solana tokens, fetch data via the `birdeye-api` skill and load into a DataFrame.btc = vbt.YFData.download("BTC-USD", start="2023-01-01", end="2025-01-01")
close = btc.get("Close")
对于 Solana 代币,可通过 `birdeye-api` skill 获取数据并加载到 DataFrame 中。Step 2: Compute Indicators
第 2 步:计算指标
python
import pandas_ta as tapython
import pandas_ta as taUsing pandas-ta (see pandas-ta skill)
Using pandas-ta (see pandas-ta skill)
df.ta.ema(length=12, append=True)
df.ta.ema(length=26, append=True)
df.ta.rsi(length=14, append=True)
df.ta.bbands(length=20, std=2, append=True)
df.ta.ema(length=12, append=True)
df.ta.ema(length=26, append=True)
df.ta.rsi(length=14, append=True)
df.ta.bbands(length=20, std=2, append=True)
Or using vectorbt built-ins
Or using vectorbt built-ins
rsi = vbt.RSI.run(close, window=14)
bbands = vbt.BBANDS.run(close, window=20, alpha=2)
undefinedrsi = vbt.RSI.run(close, window=14)
bbands = vbt.BBANDS.run(close, window=20, alpha=2)
undefinedStep 3: Generate Entry/Exit Signals
第 3 步:生成入场/退出信号
python
undefinedpython
undefinedEMA crossover
EMA crossover
entries = df["EMA_12"] > df["EMA_26"]
exits = df["EMA_12"] < df["EMA_26"]
entries = df["EMA_12"] > df["EMA_26"]
exits = df["EMA_12"] < df["EMA_26"]
RSI mean reversion
RSI mean reversion
entries = rsi.rsi_below(30)
exits = rsi.rsi_above(70)
undefinedentries = rsi.rsi_below(30)
exits = rsi.rsi_above(70)
undefinedStep 4: Run Backtest
第 4 步:运行回测
python
pf = vbt.Portfolio.from_signals(
close=close,
entries=entries,
exits=exits,
init_cash=10_000,
fees=0.003,
slippage=0.005,
size=0.95, # use 95% of available cash
size_type="percent",
freq="1h",
)python
pf = vbt.Portfolio.from_signals(
close=close,
entries=entries,
exits=exits,
init_cash=10_000,
fees=0.003,
slippage=0.005,
size=0.95, # use 95% of available cash
size_type="percent",
freq="1h",
)Step 5: Analyze Results
第 5 步:分析结果
python
undefinedpython
undefinedSummary statistics
Summary statistics
print(pf.stats())
print(pf.stats())
Trade-level analysis
Trade-level analysis
trades = pf.trades.records_readable
print(f"\nTrade count: {len(trades)}")
print(f"Avg holding period: {trades['Duration'].mean()}")
trades = pf.trades.records_readable
print(f"\nTrade count: {len(trades)}")
print(f"Avg holding period: {trades['Duration'].mean()}")
Equity curve
Equity curve
pf.plot().show()
pf.plot().show()
Drawdown chart
Drawdown chart
pf.drawdowns.plot().show()
undefinedpf.drawdowns.plot().show()
undefinedKey Portfolio Parameters
投资组合关键参数
| Parameter | Description | Example |
|---|---|---|
| Price series (pd.Series or DataFrame) | |
| Boolean entry signals | |
| Boolean exit signals | |
| Starting capital | |
| Fee per trade (fraction) | |
| Slippage per trade (fraction) | |
| Position size | |
| How to interpret size | |
| Data frequency | |
| Trade direction | |
| Allow adding to positions | |
| Stop-loss level (fraction) | |
| Take-profit level (fraction) | |
| 参数 | 说明 | 示例 |
|---|---|---|
| 价格序列(pd.Series 或 DataFrame) | |
| 布尔型入场信号 | |
| 布尔型退出信号 | |
| 初始资金 | |
| 每笔交易的手续费(比例) | |
| 每笔交易的滑点(比例) | |
| 仓位规模 | |
| size 的解释方式 | |
| 数据频率 | |
| 交易方向 | |
| 允许增加仓位 | |
| 止损水平(比例) | |
| 止盈水平(比例) | |
Performance Metrics
绩效指标
Returns
收益率
- — cumulative return over the period
total_return() - — annualized compound return
annualized_return() - — Series of daily returns
daily_returns()
- — 期间累计收益率
total_return() - — 年化复合收益率
annualized_return() - — 每日收益率序列
daily_returns()
Risk
风险
- — maximum peak-to-trough decline
max_drawdown() - — annualized standard deviation of returns
annualized_volatility() - — VaR at specified confidence level
value_at_risk()
- — 从峰值到谷底的最大跌幅
max_drawdown() - — 收益率的年化标准差
annualized_volatility() - — 指定置信水平下的 VaR
value_at_risk()
Risk-Adjusted
风险调整后指标
- — excess return per unit volatility
sharpe_ratio() - — excess return per unit downside deviation
sortino_ratio() - — annualized return / max drawdown
calmar_ratio() - — probability-weighted gain/loss ratio
omega_ratio()
- — 单位波动率对应的超额收益
sharpe_ratio() - — 单位下行偏差对应的超额收益
sortino_ratio() - — 年化收益率 / 最大回撤
calmar_ratio() - — 概率加权的收益/亏损比率
omega_ratio()
Trade Statistics
交易统计
- — fraction of profitable trades
trades.win_rate() - — gross profit / gross loss
trades.profit_factor() - — average P&L per trade
trades.expectancy() - — mean profit on winners
trades.avg_winning_trade() - — mean loss on losers
trades.avg_losing_trade() - — total number of completed trades
trades.count()
- — 盈利交易占比
trades.win_rate() - — 总利润 / 总亏损
trades.profit_factor() - — 每笔交易的平均损益
trades.expectancy() - — 盈利交易的平均利润
trades.avg_winning_trade() - — 亏损交易的平均损失
trades.avg_losing_trade() - — 已完成交易总数
trades.count()
Parameter Optimization
参数优化
Grid Search
网格搜索
python
fast_windows = [5, 8, 12, 15, 20]
slow_windows = [20, 26, 30, 40, 50]python
fast_windows = [5, 8, 12, 15, 20]
slow_windows = [20, 26, 30, 40, 50]Run all 25 combos at once
Run all 25 combos at once
fast_ma = vbt.MA.run(close, fast_windows, short_name="fast")
slow_ma = vbt.MA.run(close, slow_windows, short_name="slow")
entries = fast_ma.ma_crossed_above(slow_ma)
exits = fast_ma.ma_crossed_below(slow_ma)
pf = vbt.Portfolio.from_signals(close, entries, exits, fees=0.003)
fast_ma = vbt.MA.run(close, fast_windows, short_name="fast")
slow_ma = vbt.MA.run(close, slow_windows, short_name="slow")
entries = fast_ma.ma_crossed_above(slow_ma)
exits = fast_ma.ma_crossed_below(slow_ma)
pf = vbt.Portfolio.from_signals(close, entries, exits, fees=0.003)
Find best params by Sharpe
Find best params by Sharpe
sharpe = pf.sharpe_ratio()
best_idx = sharpe.idxmax()
print(f"Best params: {best_idx}, Sharpe: {sharpe[best_idx]:.3f}")
undefinedsharpe = pf.sharpe_ratio()
best_idx = sharpe.idxmax()
print(f"Best params: {best_idx}, Sharpe: {sharpe[best_idx]:.3f}")
undefinedWalk-Forward Validation
滚动前向验证
Always validate optimized parameters on out-of-sample data:
python
undefined始终在样本外数据上验证优化后的参数:
python
undefinedSplit: 70% train, 30% test
Split: 70% train, 30% test
split_idx = int(len(close) * 0.7)
train_close = close.iloc[:split_idx]
test_close = close.iloc[split_idx:]
split_idx = int(len(close) * 0.7)
train_close = close.iloc[:split_idx]
test_close = close.iloc[split_idx:]
Optimize on training data
Optimize on training data
... (run grid search on train_close)
... (run grid search on train_close)
Validate best params on test data
Validate best params on test data
... (run single backtest on test_close with best params)
... (run single backtest on test_close with best params)
See `references/optimization_guide.md` for detailed walk-forward methodology and overfitting prevention.
有关详细的 Walk-Forward 方法和防止过拟合的信息,请参阅 `references/optimization_guide.md`。Crypto-Specific Considerations
加密货币特有的注意事项
24/7 Markets
24/7 市场
Crypto markets never close. Use hourly or minute-based frequencies, not business-day frequencies:
python
undefined加密货币市场从不休市。应使用小时级或分钟级频率,而不是工作日频率:
python
undefinedCorrect for crypto
Correct for crypto
pf = vbt.Portfolio.from_signals(close, entries, exits, freq="1h")
pf = vbt.Portfolio.from_signals(close, entries, exits, freq="1h")
Wrong — business days assume market closures
Wrong — business days assume market closures
pf = vbt.Portfolio.from_signals(close, entries, exits, freq="1B")
pf = vbt.Portfolio.from_signals(close, entries, exits, freq="1B")
undefinedundefinedRealistic Fees
实际费用
DEX swaps on Solana typically cost 0.25–1% including AMM fees. CEX spot fees are 0.05–0.1%.
python
undefinedSolana 上的 DEX swap 通常成本为 0.25–1%(包括 AMM 费用)。CEX 现货手续费为 0.05–0.1%。
python
undefinedSolana DEX (conservative)
Solana DEX (conservative)
pf = vbt.Portfolio.from_signals(close, entries, exits, fees=0.005)
pf = vbt.Portfolio.from_signals(close, entries, exits, fees=0.005)
CEX spot
CEX spot
pf = vbt.Portfolio.from_signals(close, entries, exits, fees=0.001)
undefinedpf = vbt.Portfolio.from_signals(close, entries, exits, fees=0.001)
undefinedSlippage
滑点
Low-liquidity tokens can have 1–5% slippage. Always model this:
python
undefined低流动性代币可能产生 1–5% 的滑点。务必对其进行建模:
python
undefinedHigh-liquidity (SOL, ETH): 0.1–0.5%
High-liquidity (SOL, ETH): 0.1–0.5%
pf = vbt.Portfolio.from_signals(close, entries, exits, slippage=0.003)
pf = vbt.Portfolio.from_signals(close, entries, exits, slippage=0.003)
Low-liquidity memecoins: 1–3%
Low-liquidity memecoins: 1–3%
pf = vbt.Portfolio.from_signals(close, entries, exits, slippage=0.02)
undefinedpf = vbt.Portfolio.from_signals(close, entries, exits, slippage=0.02)
undefinedShort History
历史数据较短
Many tokens have less than 1 year of data. Be cautious about annualizing metrics from short samples.
许多代币的数据不足 1 年。对于短样本下的年化指标应谨慎解读。
Common Strategy Patterns
常见策略模式
EMA Crossover
EMA 交叉策略
python
fast = vbt.MA.run(close, 12, short_name="fast")
slow = vbt.MA.run(close, 26, short_name="slow")
entries = fast.ma_crossed_above(slow)
exits = fast.ma_crossed_below(slow)python
fast = vbt.MA.run(close, 12, short_name="fast")
slow = vbt.MA.run(close, 26, short_name="slow")
entries = fast.ma_crossed_above(slow)
exits = fast.ma_crossed_below(slow)RSI Mean Reversion
RSI 均值回归
python
rsi = vbt.RSI.run(close, 14)
entries = rsi.rsi_crossed_below(30)
exits = rsi.rsi_crossed_above(70)python
rsi = vbt.RSI.run(close, 14)
entries = rsi.rsi_crossed_below(30)
exits = rsi.rsi_crossed_above(70)Bollinger Band Breakout
布林带突破
python
bb = vbt.BBANDS.run(close, window=20, alpha=2)
entries = close > bb.upper
exits = close < bb.lowerpython
bb = vbt.BBANDS.run(close, window=20, alpha=2)
entries = close > bb.upper
exits = close < bb.lowerStop-Loss and Take-Profit
止损与止盈
python
pf = vbt.Portfolio.from_signals(
close, entries, exits,
sl_stop=0.05, # 5% stop-loss
tp_stop=0.10, # 10% take-profit
)python
pf = vbt.Portfolio.from_signals(
close, entries, exits,
sl_stop=0.05, # 5% stop-loss
tp_stop=0.10, # 10% take-profit
)Related Skills
相关 Skill
- pandas-ta — Technical indicator computation (feeds vectorbt signals)
- birdeye-api — Fetch Solana token OHLCV data for backtesting
- trading-visualization — Advanced chart generation for backtest results
- portfolio-analytics — Deeper portfolio-level risk/return analysis
- position-sizing — Optimal position sizing methodology
- risk-management — Portfolio-level risk guardrails
- regime-detection — Market regime awareness for adaptive strategies
- pandas-ta — 技术指标计算(为 vectorbt 提供信号)
- birdeye-api — 获取用于回测的 Solana 代币 OHLCV 数据
- trading-visualization — 为回测结果生成高级图表
- portfolio-analytics — 更深入的投资组合级风险/收益分析
- position-sizing — 最佳仓位规模管理方法
- risk-management — 投资组合级风险护栏
- regime-detection — 面向自适应策略的市场状态识别
Files
文件
References
参考资料
- — Complete vectorbt API reference for Portfolio, indicators, plotting, and data loading
references/api_guide.md - — Grid search, walk-forward validation, overfitting prevention, and optimization best practices
references/optimization_guide.md
- — Portfolio、指标、绘图和数据加载的完整 vectorbt API 参考
references/api_guide.md - — 网格搜索、Walk-Forward 验证、防止过拟合和优化最佳实践
references/optimization_guide.md
Scripts
脚本
- — Three-strategy backtest comparison using synthetic data (EMA crossover, RSI mean reversion, Bollinger breakout)
scripts/backtest_example.py - — EMA crossover parameter grid search with walk-forward validation
scripts/parameter_sweep.py
- — 使用合成数据的三策略回测比较(EMA 交叉、RSI 均值回归、布林带突破)
scripts/backtest_example.py - — 带 Walk-Forward 验证的 EMA 交叉参数网格搜索
scripts/parameter_sweep.py