
双均线策略到底在赚什么钱
股票和期货市场里,大部分人亏钱是因为总想预测明天涨跌。双均线策略不猜明天,它只做一件事:趋势来了就跟上,趋势走了就离开。短期均线代表最近价格的平均成本,长期均线代表更长时间的平均成本。短期均线从下往上穿过长期均线,说明近期买入的人愿意用更高的价格接手,趋势可能向上。短期均线从上往下穿过长期均线,说明近期卖出的人愿意用更低的价格抛售,趋势可能向下。
这个策略赚的是趋势的钱。震荡市里它会连续亏损,因为价格来回穿越均线。趋势市里它能抓住一大段行情。股票T+1制度下,当天买入不能卖出,双均线策略的信号频率不能太高。期货T+0且带杠杆,信号执行更灵活,但杠杆会放大亏损。

用Python准备股票期货数据
回测第一步是拿到干净的数据。股票数据可以从免费接口获取,期货数据需要主力连续合约。这里用pandas和yfinance演示股票,期货数据可以用akshare或者本地CSV。
import pandas as pd
import yfinance as yf
import numpy as np
import matplotlib.pyplot as plt
# 获取股票数据,以苹果公司为例
df = yf.download('AAPL', start='2020-01-01', end='2024-12-31')
df = df[['Close']].copy()
df.columns = ['close']
# 获取期货数据,以国内螺纹钢主力连续为例
# import akshare as ak
# df = ak.futures_main_sina(symbol="RB0", start_date="2020-01-01", end_date="2024-12-31")
# df = df[['收盘价']].rename(columns={'收盘价':'close'})
# df.index = pd.to_datetime(df['日期'])
print(df.head())
print(df.tail())
数据必须按时间升序排列,缺失值用前值填充或者删除。股票有分红拆股,yfinance默认返回调整后价格。期货主力连续合约在换月时会有跳空,需要做后复权处理,否则均线会被假信号干扰。
计算双均线并生成交易信号
短期均线参数常用5到20,长期均线常用20到60。股票波动小,可以用10和30。期货波动大,可以用5和20。参数不是固定的,不同品种需要测试。
short_window = 10
long_window = 30
df['short_ma'] = df['close'].rolling(window=short_window).mean()
df['long_ma'] = df['close'].rolling(window=long_window).mean()
df['signal'] = 0
df.loc[df['short_ma'] > df['long_ma'], 'signal'] = 1
df.loc[df['short_ma'] <= df['long_ma'], 'signal'] = 0
df['position'] = df['signal'].diff()
print(df[['close','short_ma','long_ma','signal','position']].dropna().head(20))
position等于1是买入信号,等于-1是卖出信号。信号在收盘后产生,第二天开盘执行。股票不能做空,signal为0时只能空仓。期货可以做空,signal为0时可以开空仓。
回测收益与风险指标
回测要算清楚每笔交易的成本。股票佣金万分之二点五,卖出印花税千分之一。期货手续费按手数或者成交额比例。滑点按一个最小变动价位计算。
df['market_return'] = df['close'].pct_change()
df['strategy_return'] = df['signal'].shift(1) * df['market_return']
# 扣除交易成本,假设每次换仓成本为0.1%
cost = 0.001
df['trade'] = df['position'].abs()
df['strategy_return_net'] = df['strategy_return'] - df['trade'].shift(1) * cost
df['cum_market'] = (1 + df['market_return']).cumprod()
df['cum_strategy'] = (1 + df['strategy_return_net']).cumprod()
# 计算年化收益、最大回撤、夏普比率
total_days = len(df)
annual_return = df['cum_strategy'].iloc[-1] ** (252/total_days) - 1
drawdown = (df['cum_strategy'] / df['cum_strategy'].cummax() - 1).min()
sharpe = df['strategy_return_net'].mean() / df['strategy_return_net'].std() * np.sqrt(252)
print(f'年化收益: {annual_return:.2%}')
print(f'最大回撤: {drawdown:.2%}')
print(f'夏普比率: {sharpe:.2f}')
plt.figure(figsize=(12,6))
plt.plot(df['cum_market'], label='买入持有')
plt.plot(df['cum_strategy'], label='双均线策略')
plt.legend()
plt.title('双均线策略回测')
plt.show()
年化收益高不代表策略好。最大回撤太大,实盘时心理压力会让人提前离场。夏普比率低于1,说明承担的风险没有换来足够收益。股票买入持有在牛市中可能跑赢双均线,因为均线信号滞后。期货带杠杆,双均线能控制单笔亏损,但连续亏损会吃掉本金。
参数怎么选才不坑自己
参数优化最容易犯的错是拿全部历史数据找最优参数。这样找到的是历史最幸运的参数,未来一定失效。正确做法是把数据分成训练集和测试集。训练集找参数,测试集验证。
股票和期货的均线参数不一样。股票趋势慢,长期均线可以到60。期货趋势快,长期均线20到40足够。不同品种的波动率不同,参数需要单独测试。螺纹钢和原油的节奏完全不同,用同一套参数一定有一个亏钱。
双均线策略在震荡市会反复止损。加一个过滤条件能减少假信号。价格在长期均线上方才允许做多,价格在长期均线下方才允许做空。或者用ATR指标过滤,波动太小的时候不交易。
实盘前必须知道的坑
回测用收盘价计算均线,实盘收盘前几分钟均线还没固定。用盘中价格计算均线,信号会闪烁。解决办法是收盘前5分钟用当前价格预估均线,或者等收盘后第二天开盘执行。
股票T+1,当天买入不能卖出。双均线信号如果当天买入又当天卖出,股票无法执行。期货T+0没有这个限制,但期货有涨跌停板,信号触发时可能无法成交。
滑点对高频信号影响很大。双均线策略信号不多,一年几次到几十次。每次滑点按一个最小变动价位算,期货一个点对应10元或者100元。滑点吃掉利润,参数越短信号越多,滑点成本越高。
双均线策略不是圣杯。它在大趋势里赚钱,在震荡里亏钱。股票市场牛短熊长,双均线能躲过大跌。期货市场杠杆高,双均线能控制单笔风险。用Python回测只是第一步,理解策略的赚钱逻辑和亏钱场景,才能决定要不要实盘。