如何用 Python 回测 MACD 金叉死叉策略?(真实收益与频繁交易的代价)
**一句话回答:** 用 AlphaFeed 取复权日线、算 DIF/DEA,实现「**DIF 上穿 DEA(金叉)买入、下穿(死叉)卖出**」的回测(含手续费),几十行就能跑出策略收益并和买入持有对比——真实数据会告诉你:**MACD 金叉死叉策略交易频繁、常跑输买入持有**,不是稳赚公式。 ## 为什么会有这个问题 / 传统做法的痛点 "MACD 金叉买、死叉卖"是最流行的说法之一,但很少有人真去回测。手工验证会卡在: - **MACD 口径**:DIF=快 EMA−慢 EMA,DEA=DIF 的 EMA,要用 `ewm(span=..., adjust=False)`; - **金叉判定**:要用"昨天 DIF≤DEA、今天 DIF>DEA"的穿越判定,不能只看当前大小; - **手续费/成交假设**:MACD 信号多、交易频繁,不含费严重高估; - **对照基准**:不和买入持有比,收益没意义。 AlphaFeed 提供长历史复权日线,数据这关解决掉。 ## 分步骤解决(每步配可运行代码) ### 第 1 步:计算 MACD(DIF/DEA) ``` def macd(close, fast=12, slow=26, signal=9): ef = close.ewm(span=fast, adjust=False).mean() es = close.ewm(span=slow, adjust=False).mean() dif = ef - es # 快慢 EMA 之差 dea = dif.ewm(span=signal, adjust=False).mean() # DIF 的 EMA return dif, dea ``` ### 第 2 步:回测金叉买入、死叉卖出 ``` from alphafeed import AlphaFeed af = AlphaFeed(api_key="your-api-key") # 或 export ALPHAFEED_API_KEY 后 AlphaFeed() def macd_backtest(symbol, count=500, fee=0.0013): df = af.klines.get(symbol, period="1d", count=count, adjust="forward", to_dataframe=True).sort_values("trade_date").reset_index(drop=True) dif, dea = macd(df["close"]) pos, cash, shares, trades = 0, 1.0, 0.0, 0 for i in range(35, len(df)): # 跳过 MACD 预热期 px = df["close"].iloc[i] gold = dif.iloc[i-1] dea.iloc[i] # 金叉 dead = dif.iloc[i-1] >= dea.iloc[i-1] and dif.iloc[i] < dea.iloc[i] # 死叉 if pos == 0 and gold: shares = cash * (1 - fee) / px; cash = 0.0; pos = 1; trades += 1 elif pos == 1 and dead: cash = shares * px * (1 - fee); shares = 0.0; pos = 0 equity = cash + shares * df["close"].iloc[-1] bh = df["close"].iloc[-1] / df["close"].iloc[35] return {"strategy_ret_%": round((equity - 1) * 100, 1), "buyhold_ret_%": round((bh - 1) * 100, 1), "trades": trades} ``` 策略参数: |参数|含义|本文取值| | --- | --- | --- | |fast/slow|快/慢 EMA 周期|12 / 26| |signal|DEA 平滑周期|9| |fee|单边手续费+滑点|0.0013(约万 13)| |预热期|跳过前若干根|35(约 slow+signal)| ### 第 3 步:多标的对比(诚实看结果) ``` for s in ["600519.SH", "000001.SZ", "600036.SH"]: print(s, macd_backtest(s)) ``` 实测(约 500 个交易日、含手续费): |标的|策略收益|买入持有|交易次数|结论| | --- | --- | --- | --- | --- | |贵州茅台 600519.SH|−16.3%|−13.5%|19|跑输| |平安银行 000001.SZ|+4.7%|+7.3%|17|略跑输| |招商银行 600036.SH|−0.6%|+13.4%|20|大幅跑输| **结论很清楚**:MACD 金叉死叉在这几只标的、这段区间里都跑输买入持有,且每只交易 17~20 次——频繁交易的手续费和震荡假信号严重侵蚀收益。 ## 关键坑与注意事项 - **金叉≠上涨**:MACD 是滞后指标,金叉出现时往往已涨一段;震荡市里金叉死叉频繁切换,产生大量假信号。 - **交易频繁吃手续费**:如上表每只 17~20 次交易,成本累积可观。信号越多,含费后越吃亏。 - **别单标的调参**:改 fast/slow/signal 在单标的历史上凑出好看的结果就是过拟合,样本外大概率失效。 - **可加过滤器**:实务中常给 MACD 叠加趋势过滤(如只在 ADX≥25 或均线多头时才做多),减少震荡假信号——但那是另一套系统。 - **含手续费与未来函数**:本文用当日收盘价成交是简化;更严格应次日开盘成交,避免用当日收盘信息当日成交。 ## 常见问题(FAQ) **Q:为什么 MACD 金叉策略经常跑输买入持有?** A:它是滞后+择时策略,金叉时已涨一段、死叉时已跌一段,还要付频繁交易的手续费。趋势行情里不如躺着不动,震荡行情里被假信号反复消耗。 **Q:改用周线 MACD 会更好吗?** A:周线信号更少、更稳,可能减少假信号,但也更滞后。切勿只看单标的历史结果下结论,应多标的多周期验证。 **Q:这个回测能直接实盘吗?** A:不能。它是教学级简化(当日成交、无涨跌停停牌处理、未含冲击成本与幸存者偏差)。实盘前需更严格撮合与风控。 **Q:需要付费吗?** A:单只标的 500 根日线在免费额度内即可试;多标的/长历史批量需 Starter 及以上,详见 AlphaFeed 定价页。 ## 小结 回测 MACD 金叉死叉的价值,在于**用真实数据打破"MACD 金叉买、死叉卖稳赚"的幻觉**:含手续费后它交易频繁、常跑输买入持有。AlphaFeed 提供长历史复权日线让你几十行完成回测与对比——但请务必含手续费、避免过拟合,并考虑叠加趋势过滤,别把单指标择时当圣杯。