
2026年重磅升级已全面落地!欢迎加入专注财经数据与量化投研的【数据科学实战】知识星球!您将获取持续更新的《财经数据宝典》与《量化投研宝典》,双典协同提供系统化指引;星球内含 350 篇以上独有高质量文章,深度覆盖策略开发、因子分析、风险管理等核心领域,内容基本每日更新;同步推出的「量化因子专题教程」系列(含完整可运行代码与实战案例),系统详解因子构建、回测与优化全流程,并实现日更迭代。我们持续扩充独家内容资源,全方位赋能您的投研效率与专业成长。无论您是量化新手还是资深研究者,这里都是助您少走弯路、事半功倍的理想伙伴,携手共探数据驱动的投资未来!
你是否想过,如何用 Python 构建一个真正经得起市场考验的量化交易策略?
很多初学者在学习量化交易时,往往会陷入一个误区:过度优化参数,追求历史回测中的"完美曲线"。然而,这种做法往往导致策略在实盘中表现惨淡——这就是所谓的"过拟合"问题。
今天,我们将通过一个完整的案例,学习如何使用 Python 构建、压力测试并优化一个基于 MACD 和 QQE 指标的量化交易策略。这个案例来自对 CH Robinson(股票代码:CHRW)长达 26 年的历史数据分析,最终实现了 1846% 的总回报率,同时将最大回撤控制在 30% 以内。
这个策略的思路非常简洁:
入场条件:当 MACD 低于零轴时入场做多。这意味着价格处于空头或调整阶段,我们在"逆势"中寻找机会。
出场条件:当 QQE(Quantitative Qualitative Estimation,量化定性估计)指标向上穿越设定的 RSI 阈值时出场,表明动量可能正在恢复或已经耗尽。
MACD 是最经典的趋势跟踪指标之一。以下是计算 MACD 的 Python 代码:
import pandas as pdimport numpy as npdef calculate_macd(df, fast, slow, signal): """ 计算 MACD 指标 参数: df: 包含 Close 列的 DataFrame fast: 快线周期(如 12) slow: 慢线周期(如 26) signal: 信号线周期(如 9) 返回: 包含 MACD 和 Signal 列的 DataFrame """ df = df.copy() # 计算快速 EMA df["EMA_fast"] = df["Close"].ewm(span=fast, adjust=False).mean() # 计算慢速 EMA df["EMA_slow"] = df["Close"].ewm(span=slow, adjust=False).mean() # MACD 线 = 快速 EMA - 慢速 EMA df["MACD"] = df["EMA_fast"] - df["EMA_slow"] # 信号线 = MACD 的 EMA df["Signal"] = df["MACD"].ewm(span=signal, adjust=False).mean() return dfQQE 是一个基于 RSI 的动量指标,它通过平滑处理来减少噪音:
def calculate_qqe(df, rsi_period, smooth, factor): """ 计算 QQE 指标 参数: df: 包含 Close 列的 DataFrame rsi_period: RSI 周期 smooth: 平滑周期 factor: ATR 因子 返回: 包含 QQE_Value1 和 QQE_Value2 列的 DataFrame """ df = df.copy() # 计算 RSI delta = df["Close"].diff() up = delta.clip(lower=0) # 上涨幅度 down = -delta.clip(upper=0) # 下跌幅度 roll_up = up.ewm(alpha=1 / rsi_period, adjust=False).mean() roll_down = down.ewm(alpha=1 / rsi_period, adjust=False).mean() rsi = 100 - (100 / (1 + roll_up / roll_down)) # 平滑 RSI rsi_ma = rsi.rolling(smooth).mean().bfill() # 计算类似 ATR 的平滑值 rsi_delta = rsi_ma.diff().abs().fillna(0) atr_rsi = rsi_delta.ewm(alpha=1 / smooth, adjust=False).mean() # 计算 QQE 线 value1 = rsi_ma.copy() value2 = pd.Series(index=df.index, dtype=float) value2.iloc[0] = value1.iloc[0] for i in range(1, len(df)): # 根据前一周期的方向确定当前方向 direction = 1 if value1.iloc[i-1] > value2.iloc[i-1] else -1 value2.iloc[i] = value2.iloc[i-1] + direction * factor * atr_rsi.iloc[i] df["QQE_Value1"] = value1 df["QQE_Value2"] = value2 return df压力测试的核心思想是:不寻找"最佳"参数,而是观察策略在不同参数组合下的整体表现。如果大多数参数组合都能盈利,说明策略本身具有结构性优势,而非依赖特定参数。
import yfinance as yfimport itertoolsimport vectorbt as vbtimport matplotlib.pyplot as plt# 参数网格定义macd_fast_list = list(range(10, 21, 2)) # 快线周期:10、12、14、16、18、20macd_slow_list = list(range(20, 31, 2)) # 慢线周期:20、22、24、26、28、30macd_signal_list = list(range(9, 16, 2)) # 信号线周期:9、11、13、15qqe_period_list = [12, 14, 16] # QQE RSI 周期qqe_smooth_list = [3, 5, 7] # QQE 平滑周期qqe_factor_list = [1.618, 2.618, 4.236] # QQE 因子(黄金比例相关)qqe_level_list = [50, 55, 60] # QQE 触发阈值# 下载历史数据symbol = "CHRW"df_raw = yf.download( symbol, start="2000-01-01", end="2026-01-01", interval="1d", multi_level_index=False)# 存储所有权益曲线equity_curves = {}# 生成所有参数组合param_sets = list(itertools.product( macd_fast_list, macd_slow_list, macd_signal_list, qqe_period_list, qqe_smooth_list, qqe_factor_list, qqe_level_list))# 遍历每个参数组合进行回测for (fast, slow, signal, qqe_period, qqe_smooth, qqe_factor, qqe_level) in param_sets: # 确保快线周期小于慢线周期 if fast >= slow: continue df = df_raw.copy() # 计算指标 df = calculate_macd(df, fast, slow, signal) df["MACD_lower_0"] = df["MACD"] < 0 # 入场信号 df = calculate_qqe(df, qqe_period, qqe_smooth, qqe_factor) # 出场信号:QQE 向上穿越阈值 df["QQE_cross"] = ( (df["QQE_Value1"] > qqe_level) & (df["QQE_Value1"].shift(1) <= qqe_level) ) # 生成交易信号(延迟一天执行,模拟真实交易) entries = df["MACD_lower_0"].shift(1).astype(bool).fillna(False).to_numpy() exits = df["QQE_cross"].shift(1).astype(bool).fillna(False).to_numpy() # 使用 vectorbt 进行回测 pf = vbt.Portfolio.from_signals( close=df["Open"], # 使用开盘价成交 entries=entries, exits=exits, init_cash=100, # 初始资金 100 fees=0.001, # 手续费 0.1% slippage=0.002, # 滑点 0.2% freq="1d" ) # 保存权益曲线 label = f"MACD({fast},{slow},{signal}) QQE({qqe_period},{qqe_smooth},{qqe_factor},{qqe_level})" equity_curves[label] = pf.value()压力测试证明了策略的稳健性后,我们需要使用前向优化来模拟真实的交易场景。具体做法是:
这种方法可以有效避免"未来信息"的使用,更接近实盘交易的情况。
经过前向优化后,策略在 2004 年至 2025 年期间的表现如下:
策略不仅获得了更高的收益,还显著降低了风险(最大回撤从 43% 降至 30%)。这说明策略的优势来自于其结构设计,而非单纯的参数优化。
通过这个案例,我们学到了以下几个重要的量化交易原则:
如果你正在学习 Python 量化交易,建议从这个框架开始,逐步完善自己的策略体系。记住:真正的优势来自于结构设计,而非参数调优。
2026年全面升级已落地!【数据科学实战】知识星球核心权益如下:
星球已沉淀丰富内容生态——涵盖量化文章专题教程库、因子日更系列、高频数据集、PyBroker实战课程、专家深度分享与实时答疑服务。无论您是初探量化的学习者,还是深耕领域的从业者,这里都是助您少走弯路、高效成长的理想平台。诚邀加入,共探数据驱动的投资未来!
好文推荐
1. 用 Python 打造股票预测系统:Transformer 模型教程(一)
2. 用 Python 打造股票预测系统:Transformer 模型教程(二)
3. 用 Python 打造股票预测系统:Transformer 模型教程(三)
4. 用 Python 打造股票预测系统:Transformer 模型教程(完结)
6. YOLO 也能预测股市涨跌?计算机视觉在股票市场预测中的应用
9. Python 量化投资利器:Ridge、Lasso 和 Elastic Net 回归详解
好书推荐