双均线策略
双均线策略是量化投资中最经典、最基础的策略,也是入门量化投资的第一个实战项目。
策略原理
当短期均线上穿长期均线时买入(金叉),当短期均线下穿长期均线时卖出(死叉)。
为什么有效?
- 金叉意味着短期趋势走强,可能开启一波上涨
- 死叉意味着短期趋势走弱,可能开启一波下跌
- 本质是趋势跟踪——不去预测趋势,而是跟随趋势
策略实现
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
策略注意事项
- 震荡市不适用:横盘整理时会产生大量假信号
- 交易成本不可忽视:频繁交易的手续费会蚕食利润
- 滑点:实际成交价与信号价之间的差异
- 避免过度优化:参数调得太精致反而在实盘中表现差
下一步:因子选股策略 →