2. 环境搭建与快速上手
上一章我们明确了向量化回测的价值:用数组运算替代逐笔循环,把策略验证从“分钟级”压缩到“毫秒级”。这一章的目标很具体——把环境跑通,并亲手完成一次完整的向量化回测,同时看懂 vectorbt 返回的对象到底在说什么。只有先建立“输入—计算—输出”的完整直觉,后续的参数扫描和组合优化才有落脚点。
一、核心原理:vectorbt 的三层结构
vectorbt 的设计哲学可以概括为三层:数据层、信号层、组合层。数据层是价格、成交量等时间序列,通常以 pandas 的 DataFrame 或 Series 承载;信号层是把策略逻辑翻译成布尔数组(入场 True/False、出场 True/False);组合层则根据信号和价格,模拟持仓、计算收益、统计绩效。理解这三层,就理解了 vectorbt 的调用顺序。
与传统事件驱动框架最大的区别在于:vectorbt 不关心“第几根 K 线发生了什么”,而是把整段历史一次性交给 NumPy 广播运算。因此它的输入必须是规整的二维结构(行=时间,列=标的或参数),输出也是同样规整的结构。这种“矩阵化”思维是本章最需要建立的认知。
二、方法流程:从安装到第一次回测
建议使用独立的虚拟环境,避免与系统 Python 冲突。vectorbt 依赖 NumPy、pandas、Numba 等,安装命令如下:
# 创建并激活虚拟环境(以 conda 为例)
conda create -n vbt python=3.11 -y
conda activate vbt
# 安装 vectorbt(开源版)
pip install vectorbt
# 验证安装
python -c "import vectorbt as vbt; print(vbt.__version__)"
数据导入方面,vectorbt 内置了便捷的数据接口,可以直接拉取常见行情数据,也可以传入自己的 DataFrame。下面用内置数据快速演示:
import vectorbt as vbt
import numpy as np
import pandas as pd
# 方式一:使用内置数据接口(需联网)
price = vbt.YFData.download(
"BTC-USD",
start="2022-01-01",
end="2023-01-01"
).get("Close")
# 方式二:使用本地 CSV
# price = pd.read_csv("btc.csv", index_col=0, parse_dates=True)["close"]
print(price.head())
print(price.shape)
接下来定义最简单的均线交叉策略,并运行第一次向量化回测:
fast_ma = vbt.MA.run(price, 10)
slow_ma = vbt.MA.run(price, 30)
entries = fast_ma.ma_crossed_above(slow_ma)
exits = fast_ma.ma_crossed_below(slow_ma)
pf = vbt.Portfolio.from_signals(
close=price,
entries=entries,
exits=exits,
init_cash=10000,
fees=0.001,
freq="1D"
)
print(pf.stats())
print(pf.total_return())
print(pf.sharpe_ratio())
这段代码没有一行 for 循环,却完成了完整的回测:生成信号、模拟成交、计算净值、输出绩效。这就是向量化的直观体验。
三、核心对象与输出结果解读
回测结束后,最常打交道的对象是 Portfolio。它内部保存了订单、持仓、现金、净值等完整记录。常用的访问方式包括:
pf.stats():返回一张汇总表,包含总收益、夏普、最大回撤、胜率等关键指标。pf.value():返回组合净值曲线,是 Series 或 DataFrame。pf.orders:返回订单记录,可查看每笔成交的时间、价格、数量。pf.trades:返回按交易配对的盈亏记录,便于分析单笔交易质量。pf.drawdown():返回回撤序列,用于观察风险暴露。
下面这张表可以帮助你快速定位常用输出:
| 对象/方法 | 含义 | 典型用途 |
|---|---|---|
pf.stats() | 绩效汇总表 | 快速评估策略整体表现 |
pf.value() | 净值曲线 | 绘制资金曲线、计算收益率 |
pf.orders | 订单明细 | 核对成交价格与手续费 |
pf.trades | 交易配对记录 | 分析盈亏比、持仓周期 |
pf.drawdown() | 回撤序列 | 评估最大回撤与恢复时间 |
需要特别注意的是,pf.stats() 返回的指标默认基于日频年化。如果你的数据频率不是日线,务必在 from_signals 中正确设置 freq 参数,否则夏普比率和年化收益会被严重扭曲。
四、案例场景与常见误区复盘
假设我们想验证“10 日均线上穿 30 日均线买入,下穿卖出”这一经典策略。运行上面的代码后,你可能会发现总收益并不理想,甚至为负。这很正常,因为向量化回测的价值不在于“找到赚钱策略”,而在于快速排除无效假设。你可以立刻修改参数,比如把 10/30 改成 5/20,再跑一次,整个过程不到一秒。
1. 信号错位:用当日收盘价生成信号,又用当日收盘价成交,会产生未来函数。正确做法是信号在 T 日生成,T+1 日开盘或收盘成交,可通过
from_signals 的 shift 或延迟参数控制。2. 频率设置错误:日线数据不设
freq="1D",年化指标会失真。3. 忽略手续费与滑点:高频策略中,手续费足以吞噬全部利润,务必在回测中显式设置
fees 和 slippage。复盘时建议按以下清单逐项检查:
- 数据是否连续?有无缺失值或停牌日?
- 信号是否使用了未来数据?
- 成交价格是否合理?是否考虑了滑点?
- 手续费是否与真实交易环境一致?
- 绩效指标是否基于正确的频率年化?
完成这一章后,你应该已经能够独立安装 vectorbt、导入数据、运行一次均线交叉回测,并读懂 Portfolio 对象的主要输出。更重要的是,你建立了“数据—信号—组合”的向量化思维。接下来,我们将在这个基础上引入多参数并行计算,让一次回测变成成百上千次参数扫描。