4. 金叉死叉:MA金叉/死叉策略的实战实现与回测

金叉死叉,这名字听着挺玄乎,对吧?

其实说白了,就是两根移动平均线(MA)互相交叉。一根短期均线,一根长期均线。短期线上穿长期线,叫「金叉」,看涨信号。反过来,短期线下穿长期线,叫「死叉」,看跌信号。

嗯,就是这么简单。但简单不代表没用。我入行那会儿,第一个实盘策略就是基于金叉死叉的。虽然现在看很粗糙,但当时确实帮我赚到了第一桶金。

4.1 核心逻辑:金叉与死叉的定义

我们先明确一下数学定义。

  • 金叉(Golden Cross):短期MA 上穿 长期MA。比如 MA5 上穿 MA20。
  • 死叉(Death Cross):短期MA 下穿 长期MA。比如 MA5 下穿 MA20。

这里有个细节:什么叫「上穿」?

不是当前时刻的值大于长期线就算。而是 前一时刻短期线 ≤ 长期线,且当前时刻短期线 > 长期线。这才是真正的交叉。

核心公式:

金叉条件:MA_short[t-1] ≤ MA_long[t-1]MA_short[t] > MA_long[t]

死叉条件:MA_short[t-1] ≥ MA_long[t-1]MA_short[t] < MA_long[t]

为什么强调这个?因为很多人直接用当前值比较,结果信号满天飞,回测曲线漂亮得不行,一实盘就崩。我踩过这个坑,后来才明白——交叉信号必须用前后两个时刻判断

4.2 实战实现:从数据到信号

我们直接用 Python 实现。假设你已经有 OHLC 数据,存在 DataFrame 里。

import pandas as pd
import numpy as np

def generate_ma_cross_signals(df, short_window=5, long_window=20):
    """
    生成金叉死叉信号
    df: 包含 'close' 列的 DataFrame
    short_window: 短期均线窗口,默认5
    long_window: 长期均线窗口,默认20
    """
    # 计算均线
    df['MA_short'] = df['close'].rolling(window=short_window).mean()
    df['MA_long'] = df['close'].rolling(window=long_window).mean()
    
    # 初始化信号列
    df['signal'] = 0
    
    # 金叉:短期上穿长期
    # 条件:前一刻短期 ≤ 长期,且当前短期 > 长期
    cross_up = (df['MA_short'].shift(1) <= df['MA_long'].shift(1)) & \
               (df['MA_short'] > df['MA_long'])
    
    # 死叉:短期下穿长期
    cross_down = (df['MA_short'].shift(1) >= df['MA_long'].shift(1)) & \
                 (df['MA_short'] < df['MA_long'])
    
    # 赋值信号
    df.loc[cross_up, 'signal'] = 1   # 买入
    df.loc[cross_down, 'signal'] = -1 # 卖出
    
    # 生成持仓信号(1表示持仓,0表示空仓)
    df['position'] = df['signal'].replace(to_replace=0, method='ffill').fillna(0)
    
    return df

代码很简单,但有几个点要注意:

  • shift(1) 取前一个时刻的值,这是判断交叉的关键。
  • rolling().mean() 计算均线,窗口大小自己调。
  • position 列用前向填充,因为信号只在交叉时出现,但持仓是连续的。

我的习惯:

我个人习惯把 short_windowlong_window 设成参数,方便后面做参数优化。比如 MA10/MA30、MA20/MA60,不同品种适合不同参数。

4.3 回测框架:验证策略有效性

光有信号不行,得回测。我们写一个简单的回测函数。

def backtest_ma_cross(df, initial_capital=100000, commission=0.001):
    """
    回测金叉死叉策略
    """
    df = df.copy()
    
    # 计算每日收益率
    df['daily_return'] = df['close'].pct_change()
    
    # 策略收益率 = 持仓信号 * 每日收益率
    df['strategy_return'] = df['position'].shift(1) * df['daily_return']
    
    # 扣除手续费(双边)
    df.loc[df['signal'] != 0, 'strategy_return'] -= commission
    
    # 计算净值曲线
    df['nav'] = initial_capital * (1 + df['strategy_return']).cumprod()
    
    # 计算绩效指标
    total_return = df['nav'].iloc[-1] / initial_capital - 1
    sharpe_ratio = np.sqrt(252) * df['strategy_return'].mean() / df['strategy_return'].std()
    max_drawdown = (df['nav'] / df['nav'].cummax() - 1).min()
    
    return df, {
        'total_return': total_return,
        'sharpe_ratio': sharpe_ratio,
        'max_drawdown': max_drawdown
    }

回测结果怎么看?我一般关注三个指标:

指标 含义 合格线
总收益率 策略最终赚了多少 跑赢基准即可
夏普比率 风险调整后收益 大于1算不错
最大回撤 最惨的时候亏多少 最好控制在20%以内

避坑指南:

我曾经在回测时忘记处理 shift(1),导致信号和收益率错位。结果回测曲线漂亮得不像话,年化收益50%+。后来发现是用了未来数据。记住:交易信号必须用前一个时刻的持仓,去乘当前时刻的收益率。否则就是作弊。

4.4 核心逻辑流程图

下面这张图,把整个流程串起来了。从原始数据到最终绩效,一步不落。

金叉死叉策略核心流程 OHLC 数据输入 计算 MA5 / MA20 判断金叉 / 死叉 金叉 → 买入 死叉 → 卖出 回测计算绩效

你看,整个流程其实就四步:拿数据 → 算均线 → 找交叉 → 跑回测。但每一步都有坑。数据要干净,均线参数要调,交叉判断要严谨,回测不能有未来函数。

4.5 实战中的注意事项

最后,分享几个实战经验:

  • 参数不是万能的:别想着找到一个万能参数组合。不同品种、不同周期,参数差异很大。我习惯先用默认参数跑一遍,再根据回测结果微调。
  • 震荡行情别用:金叉死叉在趋势行情里好用,但在震荡市里会被反复打脸。我一般会加一个过滤器,比如只在大盘趋势向上时做多。
  • 别迷信历史回测:回测漂亮不代表实盘能赚钱。我见过太多人拿着回测曲线去募资,结果实盘三个月就亏光了。回测只是参考,不是圣杯。

一句话总结:

金叉死叉策略,简单但不简陋。理解其核心逻辑,做好回测验证,控制好风险,它就能成为你交易工具箱里的一把好刀。


公众号:蓝海资料掘金营,微信deep3321