'''
策略原理:
在下跌过程中,当某一日出现锤子线,意味着当天行情先继续下跌后出现大幅反弹,行情可能由此反转;
由此以观察期均线识别趋势下跌,以下跌趋势中出现锤子线作为开仓信号;
采用移动止损方式进行止损构建此策略;
择时策略:
'''
import numpy as np
import pandas as pd
import tushare as ts
from matplotlib import pyplot as plt
from matplotlib import font_manager
my_font = font_manager.FontProperties(fname='C:/Windows/Fonts/simhei.ttf')
# 参数设置
code = 'hs300' # 股票代码
start = '2018-01-01' # 回测开始时间
end = '2020-04-24' # 回测结束时间
body_size = 0.3 # k线实体长度与开盘价之比的 上限,即当日k线涨幅
head_size = 0.5 # k线上影线长度与k线下影线长度之比的 上限
tail_size = 2 # k线下影线长度与k线实体长度之比的 下限
length = 10 # 观察期时间长短
stoplose_trigger = 1 # 止损触发的几倍标准差
# 获取数据
stock = ts.get_k_data(code, start=start, end=end)[['date', 'open', 'close', 'high', 'low']]
# 数据准备
stock['ma'] = stock['close'].rolling(length).mean()
stock['std'] = stock['close'].rolling(length).std()
stock['pct_change'] = stock['close'].pct_change()
stock['yes_ma'] = stock['ma'].shift(1)
stock['yes_std'] = stock['std'].shift(1)
# 判断k线是否为锤子线
stock['head'] = stock['high'] - stock[['open', 'close']].max(axis=1) # 上影线长度
stock['body'] = abs(stock['open'] - stock['close']) # 实体长度
stock['tail'] = stock[['open', 'close']].min(axis=1) - stock['low'] # 下影线长度
stock['head_cond'] = np.where(stock['tail'] == 0, False, stock['head'] / stock['tail'] < head_size)
stock['body_cond'] = np.where(stock['body'] / stock['open'] < body_size, 1, 0)
stock['tail_cond'] = np.where(stock['tail'] == 0, True, stock['tail'] / stock['body'] > tail_size)
stock['hammer'] = stock[['head_cond', 'body_cond', 'tail_cond']].all(axis=1) # 锤子信号
stock['yes_hammer'] = stock['hammer'].shift(1) # 昨天的锤子信号
# 交易策略***************************************
signal = 0 # 持仓记录,1代码有仓位,0代表空仓;
# 遍历每一天,前20天数据无效
for i in range(2*length, len(stock)):
# 1. 持仓中
if signal == 1:
stoplose_price = max(stock.loc[i, 'yes_ma'] - stoplose_trigger * stock.loc[i, 'yes_std'],
long_open_price - long_open_delta)
# 判断是否满足止损
if stock.loc[i, 'low'] < stoplose_price:
signal = 0
# 当天收益计算
stock.loc[i,'return'] = min(stock.loc[i, 'open'], stoplose_price) / stock.loc[i, 'open'] - 1
stock.loc[i, 'signal'] = -1
# 不满足止损条件,继续持仓的收益
else:
stock.loc[i, 'return'] = stock.loc[i, 'close'] / stock.loc[i-1, 'close'] - 1
stock.loc[i, 'signal'] = 1
# 2. 开仓触发条件
else:
# 判断前期是否下降趋势中
if stock.loc[i-length, 'ma'] > stock.loc[i, 'ma']:
# 判断是否有锤子信号
if stock.loc[i, 'yes_hammer']:
signal = 1
# 开仓价格&标准差
long_open_price = stock.loc[i, 'open']
long_open_delta = stock.loc[i, 'yes_std']
# 当天收益计算
stock.loc[i, 'return'] = stock.loc[i, 'close'] / stock.loc[i, 'open'] - 1
stock.loc[i, 'signal'] = 2
stock['signal'] = stock['signal'].fillna(0)
# 收益计算
stock['return'] = stock['return'].fillna(0)
stock['stock_return'] = (1 + stock['pct_change']).cumprod()
stock['strategy_return'] = (1 + stock['return']).cumprod()
print(stock)
# 画图
stock[['stock_return', 'strategy_return']].plot(figsize=(16,8))
plt.legend(loc=2)
plt.xticks(list(range(len(stock)))[::30], stock['date'][::30], rotation=45)
plt.show()