这是一份关于 VectorBT 的深度解析和完整教程,旨在帮助你理解其核心概念、功能以及如何将其应用于量化交易策略的开发和回测。
VectorBT 深度解析与完整教程
目录
- 引言
- 1.1 什么是 VectorBT?
- 1.2 为什么选择 VectorBT?(核心优势)
- 1.3 目标读者与前置知识
- 安装与环境设置
- 2.1 使用 pip 安装
- 2.2 可选依赖项
- 核心概念
- 3.1 向量化回测 (Vectorized Backtesting)
- 3.2 基于 Pandas 的数据结构
- 3.3 VectorBT 的工作流程 Pipelining
- 3.4 广播机制 (Broadcasting)
- 数据获取与处理
- 4.1 使用
vbt.YFData
获取雅虎财经数据 - 4.2 使用
vbt.CCXTData
获取加密货币数据 (需安装 ccxt) - 4.3 处理自定义数据 (Pandas DataFrame)
- 4.4 数据对齐与重采样
- 4.1 使用
- 技术指标计算
- 5.1 内建指标 (
vbt.*Indicator
) - 5.2 使用 TA-Lib (需安装
talib-binary
) - 5.3 自定义指标
- 5.4 批量计算指标(参数化)
- 5.1 内建指标 (
- 交易信号生成
- 6.1 基于阈值的信号
- 6.2 交叉信号 (
crossed_above
,crossed_below
) - 6.3 组合多个信号
- 6.4 信号处理(例如:信号延迟、过滤)
- 投资组合模拟 (Portfolio Simulation)
- 7.1
vbt.Portfolio.from_signals
核心方法 - 7.2 关键参数解析 (初始资金, 手续费, 滑点, 头寸大小, 方向等)
- 7.3 订单管理 (
vbt.Portfolio.from_orders
) - 7.4 资产与分组管理
- 7.1
- 性能分析与可视化
- 8.1 核心统计指标 (
portfolio.stats()
)- 总回报率 (Total Return)
- 夏普比率 (Sharpe Ratio)
- 最大回撤 (Max Drawdown)
- 胜率 (Win Rate)
- 盈亏比 (Profit Factor)
- 8.2 丰富的可视化 (
portfolio.plot()
)- 净值曲线
- 水下曲线 (Drawdowns)
- 订单分布
- 指标与信号图
- 8.3 访问原始数据 (订单、日志、价值)
- 8.1 核心统计指标 (
- 参数优化 (Parameter Optimization)
- 9.1 VectorBT 的优化优势
- 9.2 使用 NumPy 数组定义参数范围
- 9.3 在指标、信号、组合阶段应用参数
- 9.4 分析优化结果 (热力图, 最佳参数)
- 实战案例:移动平均线交叉策略
- 10.1 获取数据
- 10.2 定义参数范围 (快慢均线周期)
- 10.3 计算指标
- 10.4 生成信号
- 10.5 运行组合回测
- 10.6 分析结果与优化
- 进阶主题 (选读)
- 11.1 自定义信号函数与指标函数
- 11.2 处理多资产 (Multi-Asset) 策略
- 11.3 与
numba
结合提升性能 - 11.4 保存与加载回测结果 (
save
,load
)
- 总结与资源
- 12.1 VectorBT 的优缺点回顾
- 12.2 学习建议
- 12.3 官方文档与社区
1. 引言
1.1 什么是 VectorBT?
VectorBT 是一个用于量化金融和算法交易的 Python 库。它专门设计用于快速、高效地回测交易策略,特别是那些可以向量化执行的策略。它的核心思想是利用 NumPy 和 Pandas 的底层优化能力,对整个时间序列或参数空间进行批量计算,从而避免了传统回测中逐条数据、逐笔交易的循环(loop-based)方式,极大地提升了回测速度。
1.2 为什么选择 VectorBT?(核心优势)
- 极速回测: 利用向量化操作,速度远超传统循环回测,尤其在测试多参数组合时优势明显。
- 参数优化能力: 可以轻松地对策略中的多个参数同时进行测试和评估,快速找到最优参数组合。
- 与 Pandas 深度集成: 基于 Pandas DataFrame 构建,易于数据操作、对齐和可视化。
- 灵活性: 支持多种数据源、自定义指标和信号逻辑。
- 丰富的功能: 涵盖数据获取、指标计算、信号生成、组合模拟、性能分析和可视化等完整流程。
- 可扩展性: 可以与其他 Python 库(如 TA-Lib, Numba)结合使用。
1.3 目标读者与前置知识
- 目标读者: 量化交易员、策略研究员、对算法交易感兴趣的开发者、金融数据分析师。
- 前置知识:
- 基本的 Python 编程知识。
- 熟悉 Pandas (DataFrame, Series) 的基本操作。
- 了解 NumPy 的数组操作概念。
- 对技术指标和交易策略有基本概念。
2. 安装与环境设置
2.1 使用 pip 安装
最简单的安装方式是通过 pip:
pip install vectorbt
建议在一个独立的虚拟环境中安装,以避免包版本冲突。
2.2 可选依赖项
VectorBT 的一些功能依赖于其他库:
plotly
: 用于生成交互式图表 (强烈推荐安装)。yfinance
: 用于vbt.YFData
获取雅虎财经数据。ccxt
: 用于vbt.CCXTData
获取加密货币交易所数据。talib-binary
: 用于直接使用 TA-Lib 的技术指标(注意:安装 TA-Lib 可能需要额外的系统依赖)。numba
: 用于加速某些自定义计算。
按需安装:
pip install plotly yfinance ccxt talib-binary numba
3. 核心概念
3.1 向量化回测 (Vectorized Backtesting)
这是 VectorBT 的灵魂。与传统回测逐 K 线(bar-by-bar)或逐事件(event-driven)处理不同,向量化回测尝试一次性处理整个时间序列。例如,计算移动平均线,不是循环每一天的数据,而是直接对整个价格序列调用一个函数。生成信号也是如此,比较两个指标序列(如快慢均线)可以直接得到一个布尔型的信号序列。这种方式利用了底层库(NumPy/Pandas)的 C 或 Cython 优化,速度非常快。
3.2 基于 Pandas 的数据结构
VectorBT 的几乎所有操作都围绕 Pandas DataFrame 和 Series 展开。
- 价格数据: 通常是包含 ‘Open’, ‘High’, ‘Low’, ‘Close’, ‘Volume’ 列的 DataFrame,索引是时间戳。
- 指标: 通常是一个 Series (单指标) 或 DataFrame (多指标或多参数指标)。
- 信号: 通常是布尔型的 Series 或 DataFrame (True 代表信号触发)。
- 参数: 可以是标量,也可以是 NumPy 数组或 Pandas Series/DataFrame,用于参数优化。
3.3 VectorBT 的工作流程 Pipelining
一个典型的 VectorBT 工作流程如下:
- 获取数据 (Data Acquisition): 从数据源加载价格数据。
- 指标计算 (Indicator Calculation): 基于价格数据计算技术指标。
- 信号生成 (Signal Generation): 根据指标值生成入场 (entry) 和出场 (exit) 信号。
- 组合模拟 (Portfolio Simulation): 使用信号和价格数据模拟交易,计算 P&L。
- 性能分析 (Performance Analysis): 评估策略表现,计算各种统计指标和可视化。
VectorBT 的设计使得这个流程中的每一步都可以高效地进行向量化操作。
3.4 广播机制 (Broadcasting)
类似于 NumPy 和 Pandas 的广播机制,VectorBT 允许在不同维度的数据之间进行操作。这在参数优化时尤其重要。例如,你可以用一个包含多个周期的 NumPy 数组去计算移动平均线,VectorBT 会自动为每个周期计算一个 MA 序列,并将结果组织在一个 DataFrame 中,列名对应不同的周期参数。
4. 数据获取与处理
4.1 使用 vbt.YFData
获取雅虎财经数据
import vectorbt as vbt
import pandas as pd
# 下载单个股票数据
btc_price = vbt.YFData.download('BTC-USD', start='2021-01-01', end='2023-01-01').get('Close')
# 下载多个股票数据
symbols = ['AAPL', 'MSFT']
prices = vbt.YFData.download(symbols, start='2020-01-01', interval='1d').get(['Open', 'High', 'Low', 'Close'])
print(btc_price.head())
print(prices.head()) # 注意多层列索引 (Symbol, OHLC)
.get()
方法用于提取需要的列(如 ‘Close’, 或 OHLC 数据)。
4.2 使用 vbt.CCXTData
获取加密货币数据
# 需要安装 ccxt: pip install ccxt
# 注意:并非所有交易所和交易对都受支持,且可能有 API 限制
# 示例:从 Binance 获取 BTC/USDT 日线数据
# try:
# ccxt_data = vbt.CCXTData.download(
# symbols=['BTC/USDT'],
# exchange='binance',
# timeframe='1d',
# start='2022-01-01'
# )
# crypto_close = ccxt_data.get('Close')
# print(crypto_close.head())
# except Exception as e:
# print(f"获取 CCXT 数据时出错: {e}")
# print("请确保安装了 ccxt,并且交易所/交易对可用。可能需要 API Key。")
注意:CCXT 数据获取可能因交易所限制、API 密钥要求等因素而失败。
4.3 处理自定义数据 (Pandas DataFrame)
如果你有自己的数据源(如 CSV 文件),只需将其加载为 Pandas DataFrame,确保索引是时间戳(pd.to_datetime
),列名符合预期(如 ‘Open’, ‘High’, ‘Low’, ‘Close’, ‘Volume’)。
# 假设你有一个 'my_data.csv' 文件
# df = pd.read_csv('my_data.csv', index_col='timestamp', parse_dates=True)
# 确保列名正确
# df = df.rename(columns={'open_price': 'Open', 'closing_price': 'Close', ...})
# close_prices = df['Close']
4.4 数据对齐与重采样
VectorBT 依赖于 Pandas 的时间序列功能。
- 对齐: 当处理多个不同时间序列(如不同股票)时,Pandas 会自动根据索引(时间戳)对齐数据,这是向量化操作的基础。
- 重采样: 可以使用 Pandas 的
resample()
方法改变数据频率(如日线转周线)。
# 假设 prices 是日线数据
# weekly_prices = prices['Close'].resample('W').last() # 获取每周最后一个收盘价
# print(weekly_prices.head())
5. 技术指标计算
VectorBT 提供了多种计算技术指标的方式。
5.1 内建指标 (vbt.*Indicator
)
VectorBT 包含了一些常用的指标,如移动平均线 (MA)、布林带 (BBANDS)、相对强弱指数 (RSI) 等。这些内建指标可以直接调用 run
方法。
# 计算收盘价的 10 日和 20 日简单移动平均线
ma = vbt.MA.run(btc_price, window=[10, 20], short_name='ma')
# 访问指标输出
fast_ma = ma.ma.iloc[:, 0] # 第一列是 window=10
slow_ma = ma.ma.iloc[:, 1] # 第二列是 window=20
print(ma.ma.head()) # DataFrame 包含两列 MA
# 计算 RSI
rsi = vbt.RSI.run(btc_price, window=14, short_name='rsi')
print(rsi.rsi.head()) # Series
5.2 使用 TA-Lib
如果安装了 talib-binary
,可以使用 vbt.IndicatorFactory
包装 TA-Lib 函数。
try:
# from talib import abstract # 也可以用这种方式,但 IndicatorFactory 更 VBT 风格
# sma_talib = abstract.Function('sma')
# fast_ma_talib = sma_talib(btc_price, timeperiod=10)
# slow_ma_talib = sma_talib(btc_price, timeperiod=20)
# 使用 IndicatorFactory (推荐方式)
MyMA = vbt.IndicatorFactory(
class_name='MyMA',
short_name='my_ma',
input_names=['close'], # TA-Lib 函数需要的输入参数名 (通常是 'close' 或 'high', 'low', 'close')
param_names=['timeperiod'], # TA-Lib 函数的参数名
output_names=['ma'] # TA-Lib 函数的输出名 (需查阅 TA-Lib 文档)
).from_talib('SMA') # 指定 TA-Lib 函数名 'SMA'
# 计算
my_ma_indicator = MyMA.run(btc_price, timeperiod=[10, 20]) # 可以传入参数列表
print(my_ma_indicator.ma.head())
except ImportError:
print("TA-Lib 未安装或未找到。请运行 'pip install talib-binary'。")
except Exception as e:
print(f"使用 TA-Lib 时出错: {e}")
5.3 自定义指标
可以定义自己的 Python 函数,并使用 vbt.IndicatorFactory
包装它。函数应接受 Pandas Series/DataFrame 作为输入,并返回 Series/DataFrame。为了性能,可以使用 Numba 进行加速(函数前加 @njit
装饰器)。
import numpy as np
from numba import njit
@njit
def custom_ma_nb(close, window):
# Numba 加速的简单移动平均线
return vbt.nb.rolling_mean_nb(close, window)
# 使用 IndicatorFactory 包装 Numba 函数
CustomMAIndicator = vbt.IndicatorFactory(
class_name='CustomMA',
short_name='custom_ma',
input_names=['close'],
param_names=['window'],
output_names=['ma']
).from_apply_func( # 使用 from_apply_func
custom_ma_nb, # 传入 Numba 函数
takes_1d=True, # 表明函数处理 1D 数组
window_arg='window' # 指定哪个参数是窗口参数(如果需要)
)
# 计算
custom_ma = CustomMAIndicator.run(btc_price, window=[10, 20])
print(custom_ma.ma.head())
5.4 批量计算指标(参数化)
这是 VectorBT 的核心优势之一。在调用 run
方法时,可以为参数(如 window
)传递一个列表或 NumPy 数组,VectorBT 会自动计算所有参数组合下的指标。
# 计算窗口期从 10 到 30 (步长为 5) 的所有 MA
ma_windows = np.arange(10, 31, 5)
ma_batch = vbt.MA.run(btc_price, window=ma_windows, short_name='ma')
# 输出是一个 DataFrame,列名是参数值
print(ma_batch.ma.head())
# 10 15 20 25 30
# Date
# 2021-01-01 29374.152344 29374.152344 29374.152344 29374.152344 29374.152344
# 2021-01-02 29374.152344 29374.152344 29374.152344 29374.152344 29374.152344
# ...
6. 交易信号生成
信号通常是布尔型的 Pandas Series 或 DataFrame,True
表示在该时间点触发信号。
6.1 基于阈值的信号
例如,当 RSI 低于 30 时买入,高于 70 时卖出。
rsi_indicator = vbt.RSI.run(btc_price, window=14)
rsi_series = rsi_indicator.rsi
# 低于 30 为入场信号
entries = rsi_series < 30
# 高于 70 为出场信号
exits = rsi_series > 70
print("Entry signals:\n", entries.head())
print("\nExit signals:\n", exits.head())
6.2 交叉信号 (crossed_above
, crossed_below
)
这是最常用的信号之一,例如快慢均线交叉。VectorBT 的指标对象通常直接提供交叉方法。
ma = vbt.MA.run(btc_price, window=[10, 20], short_name='ma')
fast_ma = ma.ma.iloc[:, 0] # window=10
slow_ma = ma.ma.iloc[:, 1] # window=20
# 金叉:快线上穿慢线 -> 入场信号
entries_ma = fast_ma.vbt.crossed_above(slow_ma)
# 死叉:快线下穿慢线 -> 出场信号
exits_ma = fast_ma.vbt.crossed_below(slow_ma)
print("MA Cross Entries:\n", entries_ma[entries_ma == True].head()) # 只显示 True 的行
print("\nMA Cross Exits:\n", exits_ma[exits_ma == True].head())
注意 .vbt
访问器用于调用 VectorBT 提供的 Series/DataFrame 扩展方法。
6.3 组合多个信号
可以使用逻辑运算符 (&
for AND, |
for OR, ~
for NOT) 组合信号。
# 示例:金叉 且 RSI 低于 50 时入场
combined_entries = entries_ma & (rsi_series < 50)
# 示例:死叉 或 RSI 高于 80 时出场
combined_exits = exits_ma | (rsi_series > 80)
6.4 信号处理
VectorBT 提供了一些工具函数来处理信号,例如:
- 延迟信号:
entries.vbt.signals.delay(1)
将信号推迟一个周期。 - 首次出现:
entries.vbt.signals.first()
只保留每组连续 True 中的第一个。 - 过滤: 可以根据其他条件过滤信号。
7. 投资组合模拟 (Portfolio Simulation)
这是将信号转化为交易并计算盈亏的核心步骤。
7.1 vbt.Portfolio.from_signals
核心方法
这是最常用的模拟方法,它根据入场和出场信号执行交易。
# 使用之前的 MA 交叉信号
portfolio = vbt.Portfolio.from_signals(
close=btc_price, # 用作执行价格的序列 (通常是收盘价)
entries=entries_ma, # 入场信号
exits=exits_ma, # 出场信号
init_cash=10000, # 初始资金
freq='D', # 数据频率 (D=日, H=小时, M=分钟等)
fees=0.001, # 每笔交易的手续费率 (例如 0.1%)
slippage=0.0005, # 每笔交易的滑点率 (例如 0.05%)
)
# 打印组合的简要信息
print(portfolio)
7.2 关键参数解析
close
: 用于计算 P&L 和资产价值的价格序列。entries
,exits
: 布尔型信号序列。init_cash
: 初始账户资金。freq
: 数据的频率,用于计算年化指标等。必须提供。fees
: 交易手续费,可以是固定值或百分比。slippage
: 交易滑点,模拟实际成交价与理想价的差异。sl_stop
,tp_stop
: 设置止损和止盈点(相对价格或固定值)。size
: 每次交易的大小(手数或价值)。默认是全仓买入。可以使用vbt.SizeInfo
进行更复杂的设置。direction
: ‘longonly’ (只做多), ‘shortonly’ (只做空), ‘both’ (多空都做)。默认为 ‘longonly’。accumulate
: 是否允许在已有仓位上加仓。
7.3 订单管理 (vbt.Portfolio.from_orders
)
如果你的策略逻辑更复杂,需要生成具体的订单(买入/卖出,价格,大小),可以使用 from_orders
。你需要自己创建一个包含订单信息的 DataFrame。这种方式更灵活,但也更复杂。
# 这是一个简化的示例,实际订单生成逻辑会更复杂
# 假设你有一个 orders DataFrame: columns=['size', 'price', 'side'] (side: 1=buy, -1=sell)
# orders = pd.DataFrame(...)
# portfolio_from_orders = vbt.Portfolio.from_orders(btc_price, orders, init_cash=10000)
7.4 资产与分组管理
Portfolio
可以同时管理多个资产或策略分组(例如,不同参数组合的回测结果)。当输入数据(价格、信号)是 DataFrame 时,Portfolio
会为每一列创建一个子组合。
8. 性能分析与可视化
回测完成后,需要评估策略的表现。
8.1 核心统计指标 (portfolio.stats()
)
stats()
方法返回一个包含大量性能指标的 Series 或 DataFrame (如果有多列)。
stats = portfolio.stats()
print(stats)
# 访问特定指标
print(f"Total Return: {stats['Total Return [%]']:.2f}%")
print(f"Max Drawdown: {stats['Max Drawdown [%]']:.2f}%")
print(f"Sharpe Ratio: {stats['Sharpe Ratio']:.2f}")
print(f"Win Rate: {stats['Win Rate [%]']:.2f}%")
print(f"Profit Factor: {stats['Profit Factor']:.2f}")
一些关键指标:
- Total Return [%]: 策略期间的总回报率。
- Max Drawdown [%]: 峰值到谷底的最大回撤幅度,衡量风险。
- Sharpe Ratio: 夏普比率,衡量经风险调整后的回报(越高越好)。
- Sortino Ratio: 索提诺比率,类似于夏普,但只考虑下行风险。
- Win Rate [%]: 盈利交易次数占总交易次数的比例。
- Profit Factor: 总盈利 / 总亏损。大于 1 表示盈利。
- Avg Winning Trade [%]: 平均每次盈利交易的回报率。
- Avg Losing Trade [%]: 平均每次亏损交易的回报率。
8.2 丰富的可视化 (portfolio.plot()
)
plot()
方法(需要 plotly
)可以生成交互式的图表,直观展示策略表现。
# 绘制净值曲线、水下曲线、订单等
fig = portfolio.plot()
fig.show()
# 也可以绘制特定图表
fig_cum_returns = portfolio.plot(subplots=['cum_returns']) # 只绘制累计回报
fig_cum_returns.show()
fig_drawdowns = portfolio.plot(subplots=['drawdowns']) # 只绘制水下曲线
fig_drawdowns.show()
# 如果组合包含多个策略(多列),会自动绘制对比图
# 例如,参数优化后的 portfolio_optimized.plot().show()
8.3 访问原始数据 (订单、日志、价值)
Portfolio
对象包含了详细的回测过程数据:
portfolio.orders
: 包含所有执行订单的 DataFrame。portfolio.trade_log
: 包含交易日志的 DataFrame (显示持仓变化)。portfolio.value
: 账户总价值的时间序列。portfolio.cash
: 现金余额的时间序列。portfolio.positions
: 持仓价值的时间序列。
print("Orders:\n", portfolio.orders.head())
print("\nAccount Value:\n", portfolio.value.head())
9. 参数优化 (Parameter Optimization)
这是 VectorBT 的强项,可以同时回测成百上千种参数组合。
9.1 VectorBT 的优化优势
由于其向量化特性,VectorBT 在测试参数范围时不是逐个运行回测,而是尽可能地将参数数组“广播”到计算中,一次性完成所有组合的计算。这比传统的循环优化快几个数量级。
9.2 使用 NumPy 数组定义参数范围
使用 np.arange
, np.linspace
或直接定义列表来创建参数数组。
# 优化 MA 交叉策略的快慢均线周期
fast_windows = np.arange(5, 21, 5) # 快线周期: 5, 10, 15, 20
slow_windows = np.arange(20, 51, 10) # 慢线周期: 20, 30, 40, 50
# 参数组合将是笛卡尔积 (4 x 4 = 16 种组合)
9.3 在指标、信号、组合阶段应用参数
将参数数组传递给相应的 run
或 from_signals
方法。VectorBT 会自动处理参数的广播。
# 1. 计算指标 (使用参数数组)
# 注意:这里需要确保快线窗口始终小于慢线窗口
# VectorBT 的 vbt.MA.run 可以处理参数矩阵,但交叉需要对齐
# 更常见的方法是生成所有组合的指标对
fast_ma_opt = vbt.MA.run(btc_price, window=fast_windows, short_name='fast_ma')
slow_ma_opt = vbt.MA.run(btc_price, window=slow_windows, short_name='slow_ma')
# 2. 生成信号 (对所有参数组合)
# vbt.MA.run 返回的 fast_ma_opt.ma 是 DataFrame (列是参数)
# vbt.MA.run 返回的 slow_ma_opt.ma 也是 DataFrame
# 交叉操作需要广播,确保维度匹配
# entries_opt = fast_ma_opt.ma_crossed_above(slow_ma_opt.ma) # 这会进行广播
# exits_opt = fast_ma_opt.ma_crossed_below(slow_ma_opt.ma)
# 注意:上面直接交叉可能不符合快慢线逻辑(可能快线周期>慢线周期)
# 更严谨的方式是确保快 < 慢
import itertools
params = list(itertools.product(fast_windows, slow_windows)) # 生成所有 (快, 慢) 组合
valid_params = [(f, s) for f, s in params if f < s] # 过滤掉 快 >= 慢 的组合
valid_fast_windows = [p[0] for p in valid_params]
valid_slow_windows = [p[1] for p in valid_params]
# 重新计算有效组合的 MA
fast_ma_valid = vbt.MA.run(btc_price, window=valid_fast_windows, short_name='fast')
slow_ma_valid = vbt.MA.run(btc_price, window=valid_slow_windows, short_name='slow')
# 生成有效组合的信号
entries_opt = fast_ma_valid.ma_crossed_above(slow_ma_valid.ma)
exits_opt = fast_ma_valid.ma_crossed_below(slow_ma_valid.ma)
# 3. 运行组合回测 (针对所有有效参数组合)
portfolio_opt = vbt.Portfolio.from_signals(
close=btc_price,
entries=entries_opt,
exits=exits_opt,
freq='D',
init_cash=10000,
fees=0.001,
# 参数名称会自动从信号 DataFrame 的列 MultiIndex 中继承
# (fast_window, slow_window)
)
9.4 分析优化结果 (热力图, 最佳参数)
优化后的 portfolio_opt
对象的 stats()
和 plot()
会包含所有参数组合的结果。
# 获取所有组合的统计数据 (DataFrame,索引是指标名,列是参数组合 MultiIndex)
stats_opt = portfolio_opt.stats()
# print(stats_opt)
# 找到总回报率最高的参数组合
best_total_return_idx = stats_opt.loc['Total Return [%]'].idxmax()
print(f"Best Total Return Params (fast_window, slow_window): {best_total_return_idx}")
print(f"Best Total Return: {stats_opt.loc['Total Return [%]', best_total_return_idx]:.2f}%")
# 找到夏普比率最高的参数组合
best_sharpe_idx = stats_opt.loc['Sharpe Ratio'].idxmax()
print(f"\nBest Sharpe Ratio Params (fast_window, slow_window): {best_sharpe_idx}")
print(f"Best Sharpe Ratio: {stats_opt.loc['Sharpe Ratio', best_sharpe_idx]:.2f}")
print(f"Corresponding Total Return: {stats_opt.loc['Total Return [%]', best_sharpe_idx]:.2f}%")
print(f"Corresponding Max Drawdown: {stats_opt.loc['Max Drawdown [%]', best_sharpe_idx]:.2f}%")
# 可视化:绘制夏普比率的热力图
# 需要将 MultiIndex 列转换为二维表示
sharpe_stats = stats_opt.loc['Sharpe Ratio']
# 重塑数据以便绘制热力图
sharpe_heatmap = sharpe_stats.unstack(level=['ma_fast_window', 'ma_slow_window']) # 根据你的参数名称调整 level
# print(sharpe_heatmap)
# 使用 vbt 的绘图功能 (如果支持直接绘制多维参数)
# 或者使用 seaborn/matplotlib 绘制
# fig_heatmap = sharpe_stats.vbt.heatmap() # 尝试 vbt 内建的热力图
# fig_heatmap.show()
# 绘制所有参数组合的净值曲线对比
fig_opt_plots = portfolio_opt.plot()
fig_opt_plots.show()
10. 实战案例:移动平均线交叉策略
将前面的步骤整合起来。
import vectorbt as vbt
import numpy as np
import pandas as pd
# 1. 获取数据
symbol = 'AAPL'
start_date = '2018-01-01'
end_date = '2023-01-01'
price = vbt.YFData.download(symbol, start=start_date, end=end_date).get('Close')
# 2. 定义参数范围
fast_windows = np.arange(10, 31, 5) # 10, 15, 20, 25, 30
slow_windows = np.arange(40, 81, 10) # 40, 50, 60, 70, 80
# 3. 计算指标 (使用 IndicatorFactory 确保参数名正确)
FastMA = vbt.IndicatorFactory(
class_name='FastMA', short_name='fast', input_names=['close'], param_names=['window'], output_names=['ma']
).from_apply_func(vbt.nb.rolling_mean_nb, takes_1d=True, window_arg='window')
SlowMA = vbt.IndicatorFactory(
class_name='SlowMA', short_name='slow', input_names=['close'], param_names=['window'], output_names=['ma']
).from_apply_func(vbt.nb.rolling_mean_nb, takes_1d=True, window_arg='window')
fast_ma = FastMA.run(price, window=fast_windows, param_product=True) # param_product=True 会自动处理笛卡尔积
slow_ma = SlowMA.run(price, window=slow_windows, param_product=True)
# 4. 生成信号 (交叉时自动广播对齐)
# 确保 fast_window < slow_window
# (注意:param_product=True 后的交叉会自动处理对齐,但仍需过滤无效组合)
# entries = fast_ma.ma_crossed_above(slow_ma.ma)
# exits = fast_ma.ma_crossed_below(slow_ma.ma)
# 手动过滤无效组合 (更安全)
from vectorbt.signals.factory import SignalFactory
import itertools
params = list(itertools.product(fast_windows, slow_windows))
valid_params = [(f, s) for f, s in params if f < s]
valid_fast_windows = [p[0] for p in valid_params]
valid_slow_windows = [p[1] for p in valid_params]
# 重新计算有效组合的 MA
fast_ma_valid = FastMA.run(price, window=valid_fast_windows)
slow_ma_valid = SlowMA.run(price, window=valid_slow_windows)
# 生成有效组合的信号
entries = fast_ma_valid.ma_crossed_above(slow_ma_valid.ma)
exits = fast_ma_valid.ma_crossed_below(slow_ma_valid.ma)
# 5. 运行组合回测
portfolio = vbt.Portfolio.from_signals(
close=price,
entries=entries,
exits=exits,
freq='D',
init_cash=100000,
fees=0.001, # 0.1% fees
slippage=0.0005, # 0.05% slippage
# VBT 会自动从信号的列名(MultiIndex)中提取参数
# (fast_window, slow_window)
)
# 6. 分析结果与优化
stats = portfolio.stats()
# 找到最佳夏普比率组合
best_sharpe_idx = stats.loc['Sharpe Ratio'].idxmax()
print(f"Best Sharpe Ratio Params (fast_window, slow_window): {best_sharpe_idx}")
print("--- Best Sharpe Ratio Strategy Stats ---")
print(stats[best_sharpe_idx]) # 显示最佳参数组合的详细统计
# 找到最佳总回报组合
best_return_idx = stats.loc['Total Return [%]'].idxmax()
print(f"\nBest Total Return Params (fast_window, slow_window): {best_return_idx}")
print("--- Best Total Return Strategy Stats ---")
print(stats[best_return_idx])
# 可视化最佳夏普策略的表现
best_sharpe_portfolio = portfolio[best_sharpe_idx] # 选择最佳参数的子组合
fig_best_sharpe = best_sharpe_portfolio.plot(title=f'MA Cross Strategy {symbol} - Best Sharpe Params {best_sharpe_idx}')
fig_best_sharpe.show()
# 可视化所有策略的对比
fig_all = portfolio.plot()
fig_all.show()
# 可视化夏普比率热力图
sharpe_ratio_map = stats.loc['Sharpe Ratio'].vbt.unstack_to_df(index_levels='slow_window', column_levels='fast_window')
fig_heatmap = sharpe_ratio_map.vbt.heatmap(
title='Sharpe Ratio Heatmap (Slow vs Fast Window)',
xaxis_title='Fast MA Window',
yaxis_title='Slow MA Window'
)
fig_heatmap.show()
11. 进阶主题 (选读)
- 11.1 自定义信号函数与指标函数: 使用
vbt.IndicatorFactory
和vbt.SignalFactory
包装你自己的复杂逻辑,可结合 Numba 加速。 - 11.2 处理多资产 (Multi-Asset) 策略:
- 获取多资产数据(
vbt.YFData.download(['AAPL', 'MSFT'])
)。 - 指标和信号将在 DataFrame 的每一列(每个资产)上独立计算。
Portfolio
将为每个资产创建一个子账户。你可以分析整体表现或单个资产的表现。- 需要考虑跨资产的资金分配、风险管理等问题。
- 获取多资产数据(
- 11.3 与
numba
结合提升性能: 对于计算密集型的自定义函数(如复杂的指标、信号逻辑),使用 Numba 的@njit
装饰器可以大幅提升速度。VectorBT 的很多内部函数已经是 Numba 优化的。 - 11.4 保存与加载回测结果 (
save
,load
):portfolio.save('my_backtest_results.pkl')
loaded_portfolio = vbt.Portfolio.load('my_backtest_results.pkl')
- 可以保存 Portfolio 对象、指标对象等,方便后续分析。
12. 总结与资源
12.1 VectorBT 的优缺点回顾
优点:
- 速度快: 向量化和参数优化极速。
- 易于参数扫描: 非常适合寻找最优参数。
- 集成度高: 从数据到分析的完整流程。
- 与 Pandas/NumPy 生态兼容: 易于扩展和集成。
- 可视化强大: Plotly 交互式图表。
缺点:
- 向量化限制: 不适合高度依赖路径(path-dependent)或复杂状态管理的策略(事件驱动回测库可能更合适)。
- 内存消耗: 处理大数据集或大量参数组合时可能消耗较多内存。
- 学习曲线: 需要理解向量化思维和 Pandas 操作。
- 对订单簿细节模拟有限: 主要基于 K 线数据,对高频策略的微观结构模拟不够精细。
12.2 学习建议
- 动手实践: 跟着教程和示例代码敲一遍。
- 阅读官方文档: 文档非常详细,包含大量示例。
- 从小处着手: 先实现简单的策略,再逐步增加复杂度。
- 理解数据结构: 熟悉 VectorBT 中 DataFrame 和 Series 的用法,特别是 MultiIndex。
- 尝试参数优化: 体验 VectorBT 的核心优势。
12.3 官方文档与社区
- 官方文档: https://vectorbt.dev/ (非常重要!)
- GitHub 仓库: https://github.com/polakowo/vectorbt (可以查看源码、提交 issue)
- 社区/论坛 (可能): 检查 GitHub Discussions 或相关 Python 量化社区。
希望这份深度解析和教程能帮助你入门并精通 VectorBT!祝你在量化交易的道路上取得成功!