3. 向量化回测实现
任务目标
用 pandas 向量化操作替代逐行循环,完成策略信号生成与持仓计算。以双均线策略为例:快线上穿慢线时全仓买入,下穿时清仓,最终输出每日持仓状态与策略净值曲线。
环境依赖
仅需 pandas 与 numpy,无需其他第三方库。数据使用内置生成的模拟行情,保证可独立复现。
关键完整代码
import pandas as pd
import numpy as np
# 生成模拟行情(100个交易日)
np.random.seed(42)
dates = pd.date_range('2024-01-01', periods=100, freq='D')
price = 100 + np.cumsum(np.random.normal(0, 1, 100))
df = pd.DataFrame({'close': price}, index=dates)
# 计算快慢均线
df['fast_ma'] = df['close'].rolling(5).mean()
df['slow_ma'] = df['close'].rolling(20).mean()
# 向量化信号生成:快线在慢线上方为1,否则为0
df['signal'] = (df['fast_ma'] > df['slow_ma']).astype(int)
# 向量化持仓计算:信号变化时调仓(前一行信号作为当前持仓)
df['position'] = df['signal'].shift(1).fillna(0)
# 向量化策略收益与净值
df['market_return'] = df['close'].pct_change().fillna(0)
df['strategy_return'] = df['position'] * df['market_return']
df['strategy_nav'] = (1 + df['strategy_return']).cumprod()
df['buy_hold_nav'] = (1 + df['market_return']).cumprod()
# 输出关键结果
print(df[['close', 'fast_ma', 'slow_ma', 'signal', 'position', 'strategy_nav']].tail(5))
print(f"\n策略最终净值: {df['strategy_nav'].iloc[-1]:.4f}")
print(f"买入持有最终净值: {df['buy_hold_nav'].iloc[-1]:.4f}")
运行方式
将上述代码保存为 vectorized_backtest.py,在安装有 pandas 和 numpy 的 Python 环境中执行:
python vectorized_backtest.py
验证方法与判定标准
- 无循环验证:代码中不存在任何
for或while遍历行数据的逻辑,全部通过 pandas 的 rolling、shift、pct_change 等向量化方法完成。 - 持仓逻辑正确性:检查输出中
position列,应满足:当signal从 0 变为 1 的次日,position变为 1;当signal从 1 变为 0 的次日,position变为 0。这是由shift(1)实现的 T+1 日生效规则。 - 净值计算验证:策略净值应等于每日
(1 + position * market_return)的连乘结果。若某日持仓为 0,当日策略收益为 0,净值保持不变。 - 预期输出特征:由于使用固定随机种子,最终净值结果可复现。策略净值曲线应整体跟随价格趋势,但在均线交叉时点出现调仓导致的净值变化。
核心逻辑说明
信号生成使用布尔比较直接转为 0/1 整数,完全避免逐行 if-else。持仓计算通过 shift(1) 将当日信号延迟一日生效,模拟收盘后产生信号、次日开盘执行的真实交易场景。收益计算中 pct_change() 天然向量化,持仓与收益逐元素相乘后累乘即得净值序列。整个过程无任何 Python 循环,数据量增大时性能仅受 pandas 底层 C 实现约束。