双均线策略

ZaynPei Lv6

均线的“前世今生”

移动平均线(Moving Average, MA),一个进行形态分析时总也绕不过去的指标。

均线最早由美国投资专家Joseph E.Granville(格兰威尔)于20世纪中期提出,现在仍然广泛为人们使用,成为判断买卖信号的一大重要指标。从统计角度来说,均线就是历史价格的平均值,可以代表过去N日股价的平均走势。

一般来说, 均线最常使用的是收盘价, 但也可以使用开盘价、最高价、最低价等价格来计算均线。

1962年7月,Joseph E.Granville在他的书中提出了著名的Granville八大买卖法则。只利用股价和均线即可进行择时,方法简单有效,一经提出,迅速受到市场追捧。尤其是其中的金叉和死叉信号,更是沿用至今。

Granville 八大法则其中有四条是用于判断买进时机,另外四条是用于判断卖出时机。买进和卖出法则一一对应,分布在高点的左右两侧(除买4和卖4以外)。法则内容如下所示:

买1:均线整体上行,股价由下至上上穿均线,此为黄金交叉,形成第一个买点。 买2:股价出现下跌迹象,但尚未跌破均线,此时均线变成支撑线,形成第二个买点。 买3:股价仍处于均线上方,但呈现急剧下跌趋势。当跌破均线时,出现第三个买点。 买4:(右侧)股价和均线都处于下降通道,且股价处于均线下方,严重远离均线,出现第四个买点。

卖1:均线由上升状态变为缓慢下降的状态,股价也开始下降。当股价跌破均线时,此为死亡交叉,形成第一个卖点。 卖2:股价仍处于均线之下,但股价开始呈现上涨趋势,当股价无限接近均线但尚未突破时,此时均线变成阻力线,形成第二个卖点。 卖3:股价终于突破均线,处于均线上方。但持续时间不长,股价开始下跌,直至再一次跌破均线,此为第三个卖点。 卖4:(左侧)股价和均线都在上涨,股价上涨的速度远快于均线上涨的速度。当股价严重偏离均线时,出现第四个卖点。

alt text > 图中的买3和卖3描述相反

均线理论为什么有效?

Shiller(1981)在研究中发现,资产的长期价格呈现均值回复的特征,即从长期来看,资产的价格会回归均值。这也是均线理论被广泛应用的前提。

均线理论的缺陷

均线归根到底是一种平均值,平均值在应用过程中存在最大的问题就是其滞后性。当出现买入卖出信号时,最佳时机早已过去。举例来说,如果A股票最新价格出现了较大的涨幅,股价和均线都上涨,但均线的速度慢于股价上涨速度。此时,从形态上来看,金叉出现,为买入信号。次日,股价回调,股价下降的速度快于均线下降的速度,形成死叉,为卖点。这样一买一卖不仅没有盈利,反而出现亏损。

均线理论的改进

针对均线的缺点,市场上提出了各种各样的改进方法。

1.对均线的计算方法进行改正。

加权移动平均线是在移动平均线的基础上按照时间进行加权。越靠近当前日期的价格对未来价格的影响越大,赋予更大的权重;越远离当前日期价格,赋予越小的权重。

例如, 5日加权移动平均线的计算公式为:

其中,P1、P2、P3、P4、P5分别为第1天、第2天、第3天、第4天和第5天的收盘价。

指数平滑移动平均线(Exponential Moving Average, EMA)是对加权移动平均线的进一步改进。它通过指数衰减的方式赋予最近的价格更大的权重,从而使得均线对最新价格变化更加敏感。 EMA的计算公式为: EMAt = (Pt ⋅ α) + (EMAt − 1 ⋅ (1 − α))

其中,Pt 是当前价格,EMAt − 1 是前一时期的EMA值,α 是平滑系数,通常计算为 ,其中N是选定的时间周期。

2.调整均线周期

利用不同周期均线得到的结果也不同。许多有经验的投资者发现,在不同的市场中,有些均线的效果显著优于其他周期均线。有些长线投资者还会将股价替换成短周期均线进行趋势判断。

通用双均线(DMA)交易策略代码示例

下面的代码示例展示了如何实现通用双均线(DMA)交易策略:

标的:任意金融产品(例如股票、期货等)。

周期:1分钟 K线数据(或其他您选择的周期)。

参数:短周期均线(Short MA)设置为 20,长周期均线(Long MA)设置为 60。

  • 交易信号:

    • 做多信号:短期均线由下向上穿越长期均线(金叉)。
    • 做空信号:短期均线由上向下穿越长期均线(死叉)。
    • 持仓管理:每次开仓前,先平掉所有现有仓位(反向开仓)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# coding=utf-8
from __future__ import print_function, absolute_import
from gm.api import * # 引入掘进量化交易API
import talib
'''
本策略以SHFE.rb2101为交易标的,根据其一分钟(即60s频度)bar数据建立双均线模型,
短周期为20,长周期为60,当短期均线由上向下穿越长期均线时做空,
当短期均线由下向上穿越长期均线时做多,每次开仓前先平掉所持仓位,再开仓。
注:为了适用于仿真和实盘,在策略中增加了一个“先判断是否平仓成功再开仓”的判断逻辑,以避免出现未平仓成功,可用资金不足的情况。
回测数据为:SHFE.rb2101的60s频度bar数据
回测时间为:2020-04-01 09:00:00到2020-05-31 15:00:00
'''
def init(context):
context.short = 20 # 短周期均线
context.long = 60 # 长周期均线
context.symbol = 'SHFE.rb2101' # 订阅交易标的
context.period = context.long + 1 # 订阅数据滑窗长度
context.open_long = False # 开多单标记
context.open_short = False # 开空单标记
subscribe(context.symbol, '60s', count=context.period) # 订阅行情
def on_bar(context, bars):
# 获取通过subscribe订阅的数据
prices = context.data(context.symbol, '60s', context.period, fields='close')
# 利用talib库计算长短周期均线
short_avg = talib.SMA(prices.values.reshape(context.period), context.short)
long_avg = talib.SMA(prices.values.reshape(context.period), context.long)
# 查询持仓
position_long = context.account().position(symbol=context.symbol, side=1)
position_short = context.account().position(symbol=context.symbol, side=2)
# 短均线下穿长均线,做空(即当前时间点短均线处于长均线下方,前一时间点短均线处于长均线上方)
if long_avg[-2] < short_avg[-2] and long_avg[-1] >= short_avg[-1]:
# 无多仓情况下,直接开空
if not position_long:
order_volume(symbol=context.symbol, volume=1, side=OrderSide_Sell, position_effect=PositionEffect_Open,
order_type=OrderType_Market)
print(context.symbol, '以市价单调空仓到仓位')
# 有多仓情况下,先平多,再开空(开空命令放在on_order_status里面)
else:
context.open_short = True
# 以市价平多仓
order_volume(symbol=context.symbol, volume=1, side=OrderSide_Sell, position_effect=PositionEffect_Close,
order_type=OrderType_Market)
print(context.symbol, '以市价单平多仓')
# 短均线上穿长均线,做多(即当前时间点短均线处于长均线上方,前一时间点短均线处于长均线下方)
if short_avg[-2] < long_avg[-2] and short_avg[-1] >= long_avg[-1]:
# 无空仓情况下,直接开多
if not position_short:
order_volume(symbol=context.symbol, volume=1, side=OrderSide_Buy, position_effect=PositionEffect_Open,
order_type=OrderType_Market)
print(context.symbol, '以市价单调多仓到仓位')
# 有空仓的情况下,先平空,再开多(开多命令放在on_order_status里面)
else:
context.open_long = True
# 以市价平空仓
order_volume(symbol=context.symbol, volume=1, side=OrderSide_Buy,
position_effect=PositionEffect_Close, order_type=OrderType_Market)
print(context.symbol, '以市价单平空仓')
def on_order_status(context, order):
# 查看下单后的委托状态
status = order['status']
# 成交命令的方向
side = order['side']
# 交易类型
effect = order['position_effect']
# 当平仓委托全成后,再开仓
if status == 3:
# 以市价开空仓,需等到平仓成功无仓位后再开仓
# 如果无多仓且side=2(说明平多仓成功),开空仓
if effect == 2 and side == 2 and context.open_short:
context.open_short = False
order_volume(symbol=context.symbol, volume=1, side=OrderSide_Sell, position_effect=PositionEffect_Open,
order_type=OrderType_Market)
print(context.symbol, '以市价单调空仓到仓位')
# 以市价开多仓,需等到平仓成功无仓位后再开仓
# 如果无空仓且side=1(说明平空仓成功),开多仓
if effect == 2 and side == 1 and context.open_long:
context.open_long = False
order_volume(symbol=context.symbol, volume=1, side=OrderSide_Buy, position_effect=PositionEffect_Open,
order_type=OrderType_Market)
print(context.symbol, '以市价单调多仓到仓位')
if __name__ == '__main__':
'''
strategy_id策略ID,由系统生成
filename文件名,请与本文件名保持一致
mode实时模式:MODE_LIVE回测模式:MODE_BACKTEST
token绑定计算机的ID,可在系统设置-密钥管理中生成
backtest_start_time回测开始时间
backtest_end_time回测结束时间
backtest_adjust股票复权方式不复权:ADJUST_NONE前复权:ADJUST_PREV后复权:ADJUST_POST
backtest_initial_cash回测初始资金
backtest_commission_ratio回测佣金比例
backtest_slippage_ratio回测滑点比例
'''
run(strategy_id='strategy_id',
filename='main.py',
mode=MODE_BACKTEST,
token='token_id',
backtest_start_time='2020-04-01 09:00:00',
backtest_end_time='2020-05-31 15:00:00',
backtest_adjust=ADJUST_NONE,
backtest_initial_cash=10000000,
backtest_commission_ratio=0.0001,
backtest_slippage_ratio=0.0001)