1.基本原理
股票价格多围绕某一价格上下波动。当股价远离基准价格超过一定幅度则可能形成长期趋势,而短期股价偏离基准价格过多则可能因过度偏离均值而形成大幅回撤甚至趋势终止。
由此以观察期内均线作为基准价格,以观察期内标准差的一定倍数作为开仓价或止盈价,采用移动止损方式进行止损构建此策略。
开仓条件
当日最高价 > 均价 + 开仓触发倍数 × 观察期内标准差最大值
止盈条件
当天最高价 > 均价 + 止盈触发倍数 × 观察期内标准差最大值
止损条件
同样结合了移动止损和固定止损两种止损模式;
当天最低价 < max(均价, 开仓价 - 止损触发倍数 × 开仓时观察期内标准差最大值)
均价:移动止损;开仓价 - 止损触发倍数 × 开仓时观察期内标准差最大值:固定止损
注意:
考虑了开仓当天也触发了平仓信号的近似处理;
用观察期内标准差的最大值开仓的原因是:在震荡行情的时候,避免频繁开仓;更加稳定
2.策略实现
2.1 数据获取
import pandas as pd
import numpy as np
import tushare as ts
import matplotlib
import matplotlib.pyplot as plt
%matplotlib inline
code = '002397'
length = 10 # 参考周期长度,用于确定计算标准差及移动平均的周期
open_trigger = 0.5 # 价格向上偏离均线0.5倍观察期内标准差的最大值开仓;
stopwin_trigger = 3 # 价格向上偏离均线3倍观察期内标准差的最大值止盈;
stoplose_trigger = 1 # 移动止损;跌破均值移动止损;固定止损:开仓价向下偏离观察期内标准差的最大值;
data = ts.get_k_data(code, '2012-01-01', '2019-05-01')
2.2 策略数据处理
data['pct_change'] = data['close'].pct_change()
data['ma'] = data['close'].rolling(window=length, min_periods=3).mean()
data['std'] = data['close'].rolling(window=length, min_periods=3).std()
以观察期内标准差最大值作为标准差限制指标
data['std_limit'] = data['std'].rolling(window=length).max() #观察期内标准差的最大值
由于实盘中当天的日线级别参考指标未实现,例如当日ma计算时使用当日收盘价,因此应使用昨日参考指标指导当日交易
data['yes_ma'] = data['ma'].shift(1)
data['yes_std_limit'] = data['std_limit'].shift(1)
计算当日开仓价和止盈价
data['long_open_price'] = data['yes_ma'] + data['yes_std_limit']*open_trigger #计算每一天满足条件的开仓价;
data['long_stopwin_price'] = data['yes_ma'] + data['yes_std_limit']*stopwin_trigger #计算每一天满足条件的止盈价;价格高于3倍观察期内标准差最大值止盈;
2.3 计算开仓、止盈信号
data['long_open_signal'] = np.where(data['high'] > data['long_open_price'], 1, 0)
data['long_stopwin_signal'] = np.where(data['high'] > data['long_stopwin_price'], 1, 0)
3 策略逻辑
策略要点:
1. 当天有持仓,满足平仓条件进行平仓后,当天不再开仓;
2. 当天无持仓,满足开仓条件则进行开仓。开仓当日如果同时满足平仓条件,以第二日开盘价平仓;
flag = 0 # 记录持仓情况,0代表空仓,1代表持仓;
# 前12个数据因均值计算无效所以不作为待处理数据
# 终止数据选择倒数第二个以防止当天止盈情况会以第二天开盘价平仓导致无数据情况发生
# 最后一天不再进行操作;可能会面临最后一天开仓之后当天触发平仓,要用下一天开盘价卖出,无法得到;
for i in range(12, (len(data)-1)):
# 有持仓进行平仓
if flag == 1:
# 计算止损价格,取均线和开仓价下移一定倍数标准差,两者的最大值作为止损价
stoplose_price = max(data.loc[i,'yes_ma'], long_open_price - long_open_delta * stoplose_trigger)
# 多头止盈并计算当日收益率
if data.loc[i, 'long_stopwin_signal']:
data.loc[i, 'return'] = data.loc[i, 'long_stopwin_price']/data.loc[i-1, 'close'] - 1
flag = 0
# 多头移动止损并计算当日收益率
elif data.loc[i, 'low'] < stoplose_price:
# 考虑到当天开盘价就低于止损价,无法止损的情况;
# 谨慎起见,在计算收益时,取止损价和开盘价的最小值;
data.loc[i, 'return'] = min(data.loc[i, 'open'], stoplose_price)/data.loc[i-1, 'close'] - 1
flag = 0
# 多头持仓并计算收益率
else:
data.loc[i, 'return'] = data.loc[i, 'close']/data.loc[i-1, 'close'] - 1
# 无持仓进行开仓
else:
if data.loc[i, 'long_open_signal']:
#开仓时标记flag=1
flag = 1
# 需要比较当天的开盘价和开仓价,当开盘价高于开仓价时,只能以开盘价进行开仓,不能用开仓价;
# 否则对导致策略收益高估;
# 记录开仓价
long_open_price = max(data.loc[i, 'open'], data.loc[i, 'long_open_price'])
# long_open_price = data.loc[i, 'long_open_price'] #存在问题;
# 记录开仓时的10天内的标准差的最大值;是为了计算固定止损的价格;
long_open_delta = data.loc[i, 'yes_std_limit']
# 记录当天盈利情况
data.loc[i, 'return'] = data.loc[i, 'close']/long_open_price - 1
# 计算止损价:多头移动止损,以均线和开仓价减一定倍数标准差,两者的最大值作为止损点
stoplose_price = max(data.loc[i,'yes_ma'], long_open_price - long_open_delta * stoplose_trigger)
# 如果开仓当天同时满足平仓条件,则以第二天开盘价平仓
# 这里做了一定的近似处理;
if (data.loc[i, 'low'] < stoplose_price # 满足止损条件
or data.loc[i, 'long_stopwin_signal']): # 满足止盈条件
# 记录此次操作盈利情况并将收益记录在开仓日
data.loc[i, 'return'] = data.loc[i+1, 'open']/long_open_price - 1
flag = 0
4 计算策略收益率并可视化
data['return'].fillna(0, inplace=True)
data['strategy_return'] = (data['return'] + 1).cumprod()
data['stock_return'] = (data['pct_change'] + 1).cumprod()
matplotlib.style.use('ggplot')
fig = plt.figure(figsize=(10,5))
ax = fig.add_subplot(1,1,1)
ax.plot(data.stock_return)
ax.plot(data.strategy_return)
plt.title(code)
plt.legend()
plt.show()
网友评论