Skip to main content

双均线策略

双均线策略是量化投资中最经典、最基础的策略,也是入门量化投资的第一个实战项目。


策略原理

当短期均线上穿长期均线时买入(金叉),当短期均线下穿长期均线时卖出(死叉)。

为什么有效?

  • 金叉意味着短期趋势走强,可能开启一波上涨
  • 死叉意味着短期趋势走弱,可能开启一波下跌
  • 本质是趋势跟踪——不去预测趋势,而是跟随趋势

策略实现

import pandas as pd
import numpy as np

def dual_ma_strategy(df, short_window=5, long_window=20):
"""
双均线策略

参数:
df: 包含 close 列的 DataFrame
short_window: 短期均线窗口
long_window: 长期均线窗口

返回:
增加了 signal 和 position 列的 DataFrame
"""
# 1. 计算均线
df = df.copy()
df['ma_short'] = df['close'].rolling(window=short_window).mean()
df['ma_long'] = df['close'].rolling(window=long_window).mean()

# 2. 生成交易信号
df['signal'] = 0
df.loc[df['ma_short'] > df['ma_long'], 'signal'] = 1 # 持仓
df.loc[df['ma_short'] <= df['ma_long'], 'signal'] = 0 # 空仓

# 3. 计算持仓变化(交易点)
df['position'] = df['signal'].diff()
# position = 1: 买入信号, position = -1: 卖出信号

return df

信号可视化

import matplotlib.pyplot as plt

def plot_dual_ma(df, short_window=5, long_window=20):
"""可视化双均线策略信号"""
df = dual_ma_strategy(df, short_window, long_window)

fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(14, 10),
gridspec_kw={'height_ratios': [3, 1]})

# 上图:价格和均线
ax1.plot(df.index, df['close'], label='收盘价', alpha=0.6)
ax1.plot(df.index, df['ma_short'], label=f'MA{short_window}', linewidth=1)
ax1.plot(df.index, df['ma_long'], label=f'MA{long_window}', linewidth=1)

# 标记买卖点
buy_signals = df[df['position'] == 1]
sell_signals = df[df['position'] == -1]
ax1.scatter(buy_signals.index, buy_signals['close'],
color='red', marker='^', s=100, label='买入', zorder=5)
ax1.scatter(sell_signals.index, sell_signals['close'],
color='green', marker='v', s=100, label='卖出', zorder=5)

ax1.set_title(f'双均线策略 (MA{short_window} / MA{long_window})')
ax1.legend(loc='upper left')
ax1.grid(True, alpha=0.3)

# 下图:持仓信号
ax2.fill_between(df.index, 0, df['signal'], alpha=0.3, color='blue')
ax2.set_ylabel('持仓')
ax2.set_ylim(-0.1, 1.1)
ax2.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

策略回测

def backtest_dual_ma(df, short_window=5, long_window=20,
initial_capital=100000, commission=0.0003):
"""
双均线策略回测

参数:
df: 包含 close 列的 DataFrame
short_window: 短期均线窗口
long_window: 长期均线窗口
initial_capital: 初始资金
commission: 手续费率(默认万三)
"""
df = dual_ma_strategy(df, short_window, long_window)

# 计算每日收益率
df['market_return'] = df['close'].pct_change()

# 策略收益 = 持仓信号 * 市场收益(前一天的信号后一天生效)
df['strategy_return'] = df['signal'].shift(1) * df['market_return']

# 扣除交易手续费
trades = df['position'].abs() # 交易发生时
df['strategy_return'] = df['strategy_return'] - trades * commission

# 累计收益
df['cum_market'] = (1 + df['market_return']).cumprod()
df['cum_strategy'] = (1 + df['strategy_return']).cumprod()

# 绩效指标
total_return = df['cum_strategy'].iloc[-1] - 1
annual_return = (1 + total_return) ** (252 / len(df)) - 1
sharpe = np.sqrt(252) * df['strategy_return'].mean() / df['strategy_return'].std()
max_drawdown = (df['cum_strategy'] / df['cum_strategy'].cummax() - 1).min()
win_rate = (df['strategy_return'] > 0).sum() / (df['strategy_return'] != 0).sum()

print(f"=== 双均线策略回测 (MA{short_window}/MA{long_window}) ===")
print(f"累计收益率: {total_return*100:.2f}%")
print(f"年化收益率: {annual_return*100:.2f}%")
print(f"夏普比率: {sharpe:.2f}")
print(f"最大回撤: {max_drawdown*100:.2f}%")
print(f"胜率: {win_rate*100:.2f}%")
print(f"交易次数: {int(df['position'].abs().sum())}")

return df

参数优化

不同股票和周期适合不同的均线参数,可以通过网格搜索来优化:

def optimize_ma_params(df, short_range, long_range):
"""寻找最优均线参数组合"""
results = []

for short in short_range:
for long in long_range:
if short >= long:
continue
df_bt = backtest_dual_ma(df.copy(), short, long, verbose=False)
results.append({
'short': short,
'long': long,
'sharpe': sharpe,
'total_return': total_return
})

results_df = pd.DataFrame(results)
best = results_df.loc[results_df['sharpe'].idxmax()]

print(f"最优参数: MA{int(best['short'])} / MA{int(best['long'])}")
print(f"最佳夏普比率: {best['sharpe']:.2f}")

return results_df

策略注意事项

  1. 震荡市不适用:横盘整理时会产生大量假信号
  2. 交易成本不可忽视:频繁交易的手续费会蚕食利润
  3. 滑点:实际成交价与信号价之间的差异
  4. 避免过度优化:参数调得太精致反而在实盘中表现差

下一步因子选股策略 →