
2026年重磅升级已全面落地!欢迎加入专注财经数据与量化投研的【数据科学实战】知识星球!您将获取持续更新的《财经数据宝典》与《量化投研宝典》,双典协同提供系统化指引;星球内含 350 篇以上独有高质量文章,深度覆盖策略开发、因子分析、风险管理等核心领域,内容基本每日更新;同步推出的「量化因子专题教程」系列(含完整可运行代码与实战案例),系统详解因子构建、回测与优化全流程,并实现日更迭代。我们持续扩充独家内容资源,全方位赋能您的投研效率与专业成长。无论您是量化新手还是资深研究者,这里都是助您少走弯路、事半功倍的理想伙伴,携手共探数据驱动的投资未来!
你有没有经历过这样的时刻:辛辛苦苦写了一个量化交易策略,回测结果漂亮得不像话——收益率高达 22957%,胜率 78%,利润因子 3.39……然后你开始幻想人生巅峰。
冷静。
今天分享的这篇文章,来自量化交易博主 Kryptera,他用一个真实案例,完整演示了一个"看起来很美"的交易策略是如何在严格测试下现出原形的。更重要的是,文章中的每一步都用 Python 代码实现,非常适合正在学习 Python 量化分析的你。
这个策略的核心逻辑非常简洁:
标的是 SMCI(Super Micro Computer)的日线数据,回测时间跨度从 2007 年到 2026 年。
关键参数如下:
使用 Python 的 vectorbt 库进行回测,结果如下:
而同期买入并持有 SMCI 的收益率仅为 2409%。策略看似跑赢了大盘近 10 倍。
核心代码示例:
import pandas as pd
import numpy as np
import yfinance as yf
import vectorbt as vbt
# -------------------------
# 下载数据
# -------------------------
symbol = "SMCI"
start_date = "1960-01-01"
end_date = "2030-01-01"
interval = "1d"
df = yf.download(symbol, start=start_date, end=end_date, interval=interval)
# -------------------------
# 核心参数
# -------------------------
MOMENTUM_LENGTH = 3 # 动量连续下降的 K 线数量
MOMENTUM_PERIOD = 10 # 动量计算的回看周期
VIDYA_ALPHA = 0.2 # VIDYA 平滑系数
VIDYA_PERIOD = 14 # VIDYA 计算周期
VIDYA_SHIFT = 5 # VIDYA 比较的偏移量
# -------------------------
# 计算动量指标
# -------------------------
def calculate_momentum(df, period=MOMENTUM_PERIOD):
"""经典动量:当前收盘价 / N 周期前收盘价 * 100"""
df = df.copy()
df["Momentum"] = df["Close"] / df["Close"].shift(period) * 100
return df
def momentum_trend_down(df, length=MOMENTUM_LENGTH, period=MOMENTUM_PERIOD):
"""判断动量是否连续 length 根 K 线下降"""
df = calculate_momentum(df, period)
return df["Momentum"].diff().rolling(length).apply(lambda x: (x < 0).all(), raw=True).fillna(False)
# -------------------------
# 计算 VIDYA 指标
# -------------------------
def calculate_vidya(df, period=VIDYA_PERIOD, alpha=VIDYA_ALPHA):
"""计算变量指数动态平均线(VIDYA)"""
df = df.copy()
close = df["Close"].to_numpy()
vidya = np.full(len(close), np.nan)
vidya[0] = close[0]
# 用全局平均波动率作为基准
base_vol = np.mean(np.abs(np.diff(close)))
for i in range(1, len(close)):
window = close[max(0, i - period):i + 1]
vol = np.mean(np.abs(np.diff(window)))
# 波动率比率,限制在 0.2 到 2.0 之间
vol_ratio = np.clip(vol / base_vol if base_vol != 0 else 1, 0.2, 2.0)
a = alpha * vol_ratio
vidya[i] = a * close[i] + (1 - a) * vidya[i - 1]
df["VIDYA"] = vidya
return df
def vidya_rising(df, shift=VIDYA_SHIFT, period=VIDYA_PERIOD, alpha=VIDYA_ALPHA):
"""判断 VIDYA 是否正在上升"""
df = calculate_vidya(df, period, alpha)
return df['VIDYA'] > df['VIDYA'].shift(shift)
# -------------------------
# 生成交易信号
# -------------------------
df["entry_signal"] = momentum_trend_down(df)
df["exit_signal"] = vidya_rising(df)
# -------------------------
# 执行回测(信号延迟 1 根 K 线,避免未来函数)
# -------------------------
shift_entries = df['entry_signal'].shift(1).astype(bool).fillna(False).to_numpy()
shift_exits = df['exit_signal'].shift(1).astype(bool).fillna(False).to_numpy()
pf = vbt.Portfolio.from_signals(
close=df['Open'], # 用开盘价成交
entries=shift_entries,
exits=shift_exits,
init_cash=100_000, # 初始资金 10 万
fees=0.001, # 手续费 0.1%
slippage=0.002, # 滑点 0.2%
freq='1d'
)
# 打印策略统计结果
print(pf.stats())作者提出了一个关键观点:一个真正有效的策略,不应该只在某个精确的参数组合上表现优异,而应该在一个"参数邻域"内都表现良好。
于是他对 MOMENTUM_LENGTH(2、3、4)和 MOMENTUM_PERIOD(8~12)做了参数扫描,生成了一个 15 格的热力图。
结果触目惊心:
这不是一座平缓的山丘,而是一根针。往左偏一格(LENGTH=2)收益暴跌至 1670%,往右一格(LENGTH=4)只有 3145%。
这种现象叫参数敏感性(Parameter Sensitivity)——当策略的表现完全依赖于某个精确参数时,它捕捉的不是市场规律,而是历史巧合。
热力图展示的不是一个"优势",而是一块"化石"。
作者继续对 VIDYA 的三个参数(VIDYA_ALPHA、VIDYA_PERIOD、VIDYA_SHIFT)做了 45 种组合的 3D 参数扫描。
结果是:几乎全是红色和橙色的点,只有两三个绿色的孤立点散落在空间中。最优组合(ALPHA=0.15、PERIOD=15、SHIFT=4)收益 24971%,但它的邻居全是低收益。
一个稳健的策略在 3D 参数空间中应该呈现为一团绿色区域,而不是几个孤零零的绿点。
走步优化(Walk-Forward Optimization)是量化研究中最接近"公平审判"的测试方法。它的流程是:
核心代码片段:
def run_walk_forward(df, train_years=4, test_years=1):
"""
走步优化:
- 在 train_years 年数据上寻找最优参数
- 在接下来的 test_years 年数据上测试
- 滚动重复,拼接所有样本外结果
"""
results = []
start_year = df.index[0].year
end_year = df.index[-1].year
for train_start in range(start_year, end_year - train_years - test_years + 1):
train_end = train_start + train_years - 1
test_start = train_end + 1
test_end = test_start + test_years - 1
# ... 在训练集上遍历所有参数组合 ...
best_perf = -np.inf
best_params = None
for mom_len, mom_per, v_alpha, v_per, v_shift in product(
[2, 3, 4], # MOMENTUM_LENGTH 候选值
[8, 10, 12], # MOMENTUM_PERIOD 候选值
[0.15, 0.2, 0.25], # VIDYA_ALPHA 候选值
[12, 14, 16], # VIDYA_PERIOD 候选值
[4, 5, 6] # VIDYA_SHIFT 候选值
):
# 在训练集上回测,选出收益最高的参数
pf_train = vbt.Portfolio.from_signals(...)
if pf_train.total_return() > best_perf:
best_perf = pf_train.total_return()
best_params = (mom_len, mom_per, v_alpha, v_per, v_shift)
# 用训练集选出的最优参数,在测试集上执行交易
results.append({
"train_period": f"{train_start}-{train_end}",
"test_period": f"{test_start}-{test_end}",
"params": best_params,
})
return resultsWFO 的结果彻底戳破了幻觉:
| 1805% | ||
经过 15 年的真实样本外测试,执行了 126 笔交易、支付了近 10 万美元的手续费和滑点后,策略的收益甚至不如"什么都不做"的买入持有。
作者总结了这个策略失败的原因——三层幻觉:
幻觉 1——耀眼的回测结果。 22957% 的收益率是在已知所有历史数据后"找到"的最优参数,属于事后诸葛亮。
幻觉 2——表面的稳健性。 78% 的胜率和 3.39 的利润因子看起来像是"优势"的证据,实际上是过度拟合的证据。
幻觉 3——热力图上的孤峰。 一个被红色包围的唯一绿色格子,不是"发现了宝藏",而是"优化器找到了一个幸运数字"。
现实——走步优化。 当被迫在不知道未来的前提下选择参数,并在从未见过的数据上测试时,策略连买入持有都跑不赢。
简单回测告诉你的是:如果你是上帝,策略本可以做到什么。走步优化告诉你的是:作为一个普通人,策略实际会做到什么。两者之间约 13000 个百分点的差距,就是幻觉的代价。
这篇文章给所有学习 Python 量化交易的同学上了重要一课:
学会用 Python 跑回测只是第一步,学会用正确的方法验证回测结果,才是真正的核心能力。
先画热力图,再跑走步优化,然后再做决定。
2026年全面升级已落地!【数据科学实战】知识星球核心权益如下:
星球已沉淀丰富内容生态——涵盖量化文章专题教程库、因子日更系列、高频数据集、PyBroker实战课程、专家深度分享与实时答疑服务。无论您是初探量化的学习者,还是深耕领域的从业者,这里都是助您少走弯路、高效成长的理想平台。诚邀加入,共探数据驱动的投资未来!
好文推荐
1. 用 Python 打造股票预测系统:Transformer 模型教程(一)
2. 用 Python 打造股票预测系统:Transformer 模型教程(二)
3. 用 Python 打造股票预测系统:Transformer 模型教程(三)
4. 用 Python 打造股票预测系统:Transformer 模型教程(完结)
6. YOLO 也能预测股市涨跌?计算机视觉在股票市场预测中的应用
9. Python 量化投资利器:Ridge、Lasso 和 Elastic Net 回归详解
好书推荐