当前位置:首页>python>用 Python 实现量化交易策略回测:从压力测试到前向优化

用 Python 实现量化交易策略回测:从压力测试到前向优化

  • 2026-09-16 13:34:43
用 Python 实现量化交易策略回测:从压力测试到前向优化

2026年重磅升级已全面落地!欢迎加入专注财经数据与量化投研的【数据科学实战】知识星球!您将获取持续更新的《财经数据宝典》与《量化投研宝典》,双典协同提供系统化指引;星球内含300篇以上独有高质量文章,深度覆盖策略开发、因子分析、风险管理等核心领域,内容基本每日更新;同步推出的「量化因子专题教程」系列(含完整可运行代码与实战案例),系统详解因子构建、回测与优化全流程,并实现日更迭代。我们持续扩充独家内容资源,全方位赋能您的投研效率与专业成长。无论您是量化新手还是资深研究者,这里都是助您少走弯路、事半功倍的理想伙伴,携手共探数据驱动的投资未来!

引言

你是否曾经好奇,那些华尔街的量化交易员是如何验证他们的交易策略的?今天我们来拆解一个完整的量化交易案例——基于 Keltner 通道和 SuperTrend 指标的交易系统,看看如何用 Python 对策略进行压力测试和前向优化(Walk-Forward Optimization)。

这篇文章基于美国银行(BAC)从 2000 年到 2026 年的历史数据,涵盖了互联网泡沫、2008 年金融危机、以及多次市场震荡,是一个非常严苛的测试环境。

核心思路

这个交易系统的逻辑并不复杂:

  • • 入场条件:当收盘价突破 Keltner 通道下轨时买入
  • • 出场条件:当价格回到 SuperTrend 指标的上下轨之间时卖出

关键在于,作者没有只测试一组参数,而是进行了数百种参数组合的压力测试,并采用前向优化来验证策略的稳健性。

第一步:获取历史数据

import pandas as pd
import
 numpy as np
import
 yfinance as yf

# 设置股票代码和时间范围

symbol = "BAC"  # 美国银行
start_date = "2000-01-01"
end_date = "2026-01-01"
interval = "1d"  # 日线数据

# 使用 yfinance 下载数据

df_raw = yf.download(
    symbol,
    start=start_date,
    end=end_date,
    interval=interval,
    multi_level_index=False
)

print
(f"数据量:{len(df_raw)} 条")
print
(df_raw.head())

第二步:计算技术指标

Keltner 通道

Keltner 通道是一种基于波动率的指标,由中轨(EMA)和上下轨(ATR 的倍数)组成。

def calculate_keltner_channel(df, period, multiplier):
    """
    计算 Keltner 通道

    参数:
        df: 包含 OHLC 数据的 DataFrame
        period: 计算周期
        multiplier: ATR 倍数

    返回:
        添加了 KC_Lower 列的 DataFrame
    """

    df = df.copy()

    # 计算典型价格(Typical Price)

    tp = (df['High'] + df['Low'] + df['Close']) / 3

    # 计算 EMA

    ema = tp.ewm(span=period, adjust=False).mean()

    # 计算真实波幅(True Range)

    tr = pd.concat([
        df['High'] - df['Low'],
        (df['High'] - df['Close'].shift()).abs(),
        (df['Low'] - df['Close'].shift()).abs()
    ], axis=1).max(axis=1)

    # 计算 ATR(平均真实波幅)

    atr = tr.rolling(period).mean()

    # 计算下轨

    df['KC_Lower'] = ema - multiplier * atr

    return
 df

SuperTrend 指标

SuperTrend 是一种趋势跟踪指标,能够帮助识别趋势方向。

def calculate_supertrend(df, period, multiplier):
    """
    计算 SuperTrend 指标

    参数:
        df: 包含 OHLC 数据的 DataFrame
        period: 计算周期
        multiplier: ATR 倍数

    返回:
        添加了 Upper_Band 和 Lower_Band 列的 DataFrame
    """

    df = df.copy()

    # 计算中间价

    hl2 = (df['High'] + df['Low']) / 2

    # 计算真实波幅

    tr = pd.concat([
        df['High'] - df['Low'],
        (df['High'] - df['Close'].shift()).abs(),
        (df['Low'] - df['Close'].shift()).abs()
    ], axis=1).max(axis=1)

    # 计算 ATR

    atr = tr.rolling(period).mean()

    # 计算基础上下轨

    upper_basic = hl2 + multiplier * atr
    lower_basic = hl2 - multiplier * atr

    upper_band = upper_basic.copy()
    lower_band = lower_basic.copy()

    # 动态调整上下轨

    for
 i in range(1, len(df)):
        # 如果前一日收盘价低于上轨,取较小值

        if
 df['Close'].iloc[i-1] <= upper_band.iloc[i-1]:
            upper_band.iloc[i] = min(upper_basic.iloc[i], upper_band.iloc[i-1])

        # 如果前一日收盘价高于下轨,取较大值

        if
 df['Close'].iloc[i-1] >= lower_band.iloc[i-1]:
            lower_band.iloc[i] = max(lower_basic.iloc[i], lower_band.iloc[i-1])

    df['Upper_Band'] = upper_band
    df['Lower_Band'] = lower_band

    return
 df

第三步:参数压力测试

作者的核心思想是:一个只在完美参数下才能工作的策略,本质上是脆弱的。

因此,他测试了数百种参数组合:

import itertools
import
 vectorbt as vbt
import
 matplotlib.pyplot as plt

# 定义参数范围

KC_MULTIPLIER = [2, 3, 4]           # Keltner 通道倍数
KC_PERIOD = list(range(16, 25))     # Keltner 通道周期:16-24
SUPERTREND_MULTIPLIER = [2, 3, 4, 5]  # SuperTrend 倍数
SUPERTREND_PERIOD = list(range(10, 21))  # SuperTrend 周期:10-20

# 生成所有参数组合

param_sets = list(itertools.product(
    KC_MULTIPLIER,
    KC_PERIOD,
    SUPERTREND_MULTIPLIER,
    SUPERTREND_PERIOD
))

print
(f"总共 {len(param_sets)} 种参数组合")

# 存储每种参数组合的资金曲线

equity_curves = {}

for
 kc_mult, kc_period, st_mult, st_period in param_sets:
    df = df_raw.copy()

    # 计算指标

    df = calculate_keltner_channel(df, kc_period, kc_mult)
    df = calculate_supertrend(df, st_period, st_mult)

    # 定义入场和出场信号

    df['entry_signal'] = df['Close'] > df['KC_Lower']  # 价格突破下轨
    df['exit_signal'] = (
        (df['Close'] <= df['Upper_Band']) &
        (df['Close'] >= df['Lower_Band'])
    )  # 价格回到通道内

    # 信号延迟一天执行(模拟真实交易)

    entries = df['entry_signal'].shift(1).astype(bool).fillna(False).to_numpy()
    exits = df['exit_signal'].shift(1).astype(bool).fillna(False).to_numpy()

    # 使用 vectorbt 进行回测

    pf = vbt.Portfolio.from_signals(
        close=df['Open'],     # 以开盘价成交
        entries=entries,
        exits=exits,
        init_cash=100,        # 初始资金
        fees=0.001,           # 手续费 0.1%
        slippage=0.002,       # 滑点 0.2%
        freq='1d'
    )

    # 保存资金曲线

    label = f"KC({kc_period},{kc_mult}) ST({st_period},{st_mult})"
    equity_curves[label] = pf.value()

第四步:与买入持有策略对比

任何策略都需要一个基准,最简单的基准就是买入持有(Buy & Hold)。

# 计算买入持有的收益
pf_holding = vbt.Portfolio.from_holding(
    df_raw['Open'], 
    init_cash=100, 
    freq='1d'
)
buy_hold_curve = pf_holding.value()

# 计算所有策略的平均资金曲线

equity_df = pd.DataFrame(equity_curves)
average_equity = equity_df.mean(axis=1)

# 绘制对比图

plt.figure(figsize=(14, 7))

# 绘制所有策略曲线(淡色)

for
 curve in equity_curves.values():
    plt.plot(curve.index, curve.values, alpha=0.25, linewidth=1, color='gray')

# 绘制平均策略曲线(蓝色粗线)

plt.plot(average_equity.index, average_equity.values, 
         color='blue', linewidth=2, label='策略平均')

# 绘制买入持有曲线(红色虚线)

plt.plot(buy_hold_curve.index, buy_hold_curve.values, 
         color='red', linewidth=2, linestyle='--', label='买入持有')

plt.title("压力测试:策略 vs 买入持有")
plt.xlabel("日期")
plt.ylabel("资金曲线")
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()

测试结果显示,蓝色的策略平均曲线在 25 年间跑赢了红色的买入持有曲线。

第五步:前向优化(Walk-Forward Optimization)

压力测试回答了"策略能否经受参数变化",而前向优化回答了一个更难的问题:

"策略能否在不知道未来的情况下自适应?"

前向优化的框架:

  • • 训练窗口:4 年
  • • 测试窗口:1 年
  • • 每一步只用过去的数据优化参数,然后在未来的数据上测试
from itertools import product

def
 run_walk_forward(df, train_years=4, test_years=1):
    """
    执行前向优化

    参数:
        df: 完整的历史数据
        train_years: 训练窗口年数
        test_years: 测试窗口年数

    返回:
        每个测试期的结果列表
    """

    results = []
    max_lb = max(max(KC_PERIOD), max(SUPERTREND_PERIOD)) + 5  # 预留指标计算所需数据

    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

        # 获取训练和测试数据的索引

        train_data = df[(df.index.year >= train_start) & (df.index.year <= train_end)]
        test_data = df[(df.index.year >= test_start) & (df.index.year <= test_end)]

        best_perf = -np.inf
        best_params = None

        # 在训练集上寻找最优参数

        for
 kc_p, kc_m, st_p, st_m in product(KC_PERIOD, KC_MULTIPLIER, 
                                               SUPERTREND_PERIOD, SUPERTREND_MULTIPLIER):
            # 计算信号并回测

            # ...(回测逻辑)


            # 记录最佳参数

            if
 performance > best_perf:
                best_perf = performance
                best_params = (kc_p, kc_m, st_p, st_m)

        results.append({
            "train_period"
: f"{train_start}-{train_end}",
            "test_period"
: f"{test_start}-{test_end}",
            "params"
: best_params,
            "test_data"
: test_data
        })

        print
(f"训练期:{train_start}-{train_end} | 测试期:{test_start}-{test_end}")
        print
(f"最优参数:KC({best_params[0]},{best_params[1]}) ST({best_params[2]},{best_params[3]})")

    return
 results

前向优化结果

经过前向优化测试,从 2004 年到 2025 年的样本外结果如下:

指标
数值
总收益率
148%
最大回撤
79%
总交易次数
29
胜率
34%
盈亏比
1.31

乍一看,34% 的胜率和 79% 的最大回撤似乎令人担忧。但需要理解这是一个趋势跟踪系统:

  • • 盈亏比 1.31 说明赢的时候赚得比输的时候多
  • • 交易次数少,专注于捕捉大趋势
  • • 在样本外数据上仍然跑赢买入持有

总结

这篇文章展示了一个完整的量化策略验证流程:

  1. 1. 数据获取:使用 yfinance 获取 25 年的历史数据
  2. 2. 指标计算:实现 Keltner 通道和 SuperTrend 指标
  3. 3. 压力测试:测试数百种参数组合,验证策略的稳健性
  4. 4. 基准对比:与买入持有策略对比
  5. 5. 前向优化:模拟真实交易环境,避免过拟合

作者最后总结道:

压力测试证明了稳健性,前向优化证明了真实性。这个系统并不完美,但它是诚实的。

对于学习量化交易的同学来说,这个案例提供了一个很好的模板。记住,一个好的策略不是因为它被优化得很好,而是因为它的结构是合理的。

参考文章

加入专注于财经数据与量化投研的知识星球【数据科学实战】,获取本文完整研究解析、代码实现细节。

财经数据与量化投研知识社区

2026年全面升级已落地!【数据科学实战】知识星球核心权益如下:

  1. 1. 双典系统赋能:获赠《财经数据宝典》与《量化投研宝典》完整文档,凝练多年实战经验,构建系统化知识框架;
  2. 2. 量化因子日更教程(2026重磅新增):每日更新「量化因子专题教程」,配套完整可运行代码与实战案例,深度拆解因子构建、回测与优化全流程;
  3. 3. 量化文章专题教程库:300+篇星球独有高质量教程式文章,系统覆盖策略开发、因子研究、风险管理等核心领域,内容基本每日更新,并配套精选学习资料与实战参考;
  4. 4. PyBroker实战课程:赠送《PyBroker-入门及实战》视频课程,手把手教学,快速掌握量化策略开发技能;
  5. 5. 财经数据支持:定期更新国内外财经数据,为策略研发提供精准、可靠的数据基础;
  6. 6. 顶尖学者与行业专家分享:年度邀请学术界博士与业界资深专家开展前沿论文精讲与实战案例分享,不少于4场,直击研究前沿与产业实践;
    专家直连答疑:与核心开发者及领域专家实时互动,高效解决投研实战难题;
  7. 7. 专业社群与专属福利:加入高质量交流社群,获取课程折扣及更多独家资源。

星球已沉淀丰富内容生态——涵盖量化文章专题教程库、因子日更系列、高频数据集、PyBroker实战课程、专家深度分享与实时答疑服务。无论您是初探量化的学习者,还是深耕领域的从业者,这里都是助您少走弯路、高效成长的理想平台。诚邀加入,共探数据驱动的投资未来!

好文推荐

1. 用 Python 打造股票预测系统:Transformer 模型教程(一)

2. 用 Python 打造股票预测系统:Transformer 模型教程(二)

3. 用 Python 打造股票预测系统:Transformer 模型教程(三)

4. 用 Python 打造股票预测系统:Transformer 模型教程(完结)

5. 揭秘隐马尔可夫模型:因子投资的制胜武器

6. YOLO 也能预测股市涨跌?计算机视觉在股票市场预测中的应用

7. 金融 AI 助手:FinGPT 让你轻松掌握市场分析

8. 量化交易秘籍:为什么专业交易员都在用对数收益率?

9. Python 量化投资利器:Ridge、Lasso 和 Elastic Net 回归详解

10. 掌握金融波动率模型:完整 Python 实现指南

好书推荐

最新文章

随机文章