海龟法则本质上是一种趋势性研究,主要依赖于唐奇安通道,利用唐奇安通道的突破点作为买卖信号指导交易。唐奇安通道主要是一个突破型趋势跟踪指标,可以提供两种不同的突破信号。
唐奇安通道指标计算:
唐奇安上阻力线 = 过去N天的当日最高价的最大值。
唐奇安下支撑线 = 过去N天的当日最低价的最小值。
中心线 = (上线 + 下线)/ 2
唐奇安通道所提供的两种突破信号分别为:上阻力线或下支撑线,突破信号是中心线交叉。中心线有几个用途。首先,它可以用作突破信号以进入新的位置。当价格越过中心线向上时,可以买入;当价格越过中心线向下时,可以卖出。激进的交易者从中心线进入,开始下单,而不是等待支撑/阻力线突破。
我们通常认为越好的越好,越差的越差,这在趋势比较明显的行情表现不错,但是在震荡的行情中效果不佳,当然这是所有趋势型策略的通病。举一个简单的例子来讲就是,当一只股票的收盘价持续10天在涨的时候或出于涨的状态我们就进行买入操作,当它的收盘价持续10天在跌的时候就进行卖出操作。(这里我感觉可以应用到换手率或者涨跌幅的问题上)
实现过程:我用的是python3.7,win10
import pandas as pd
import numpy as np
import talib as ta
from datetime import datetime,timedelta
import matplotlib.pyplot as plt
#正常显示画图时出现的中文和负号
from pylab import mpl
mpl.rcParams['font.sans-serif']=['SimHei']
mpl.rcParams['axes.unicode_minus']=False
#使用tushare获取交易数据
#设置token
import tushare as ts
from pyecharts import Kline,Grid,Bar,Line,EffectScatter,Overlap
这个轮子的问题折腾了半天,在这里罗列出来我出现的问题,防止出错
一、第三行的 import talib as ta这个需要安装的是TA-Lib库,直接用pip install 安装因为墙的原因不好安装,建议采用镜像
pip install -i https://pypi.doubanio.com/simple/ --trusted-host pypi.doubanio.com TA-Lib
二、tushare这个是我之前安装过的包一个很好的连接数据的包https://blog.csdn.net/holal/article/details/107243306具体安装使用可以参考这个帖子。
三、pyecharts也是我耗时最长的一个包,他直接安装的话pip以后是1.0的版本,但是目前网上能搜到的代码,貌似都是0.5的版本
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pyecharts==0.5.10
可以利用上面这个来装就可以了。
token='输入你的token获取方式参考tushare那里'
pro=ts.pro_api(token)
index={'上证综指': '000001.SH',
'深证成指': '399001.SZ',
'沪深300': '000300.SH',
'创业板指': '399006.SZ',
'上证50': '000016.SH',
'中证500': '000905.SH',
'中小板指': '399005.SZ',
'上证180': '000010.SH'}
#获取当前交易的股票代码和名称
def get_code():
df = pro.stock_basic(exchange='', list_status='L')
codes=df.ts_code.values
names=df.name.values
stock=dict(zip(names,codes))
#合并指数和个股成一个字典
stocks=dict(stock,**index)
return stocks
#获取行情数据
def get_daily_data(stock,start,end):
#如果代码在字典index里,则取的是指数数据
code=get_code()[stock]
if code in index.values():
df=pro.index_daily(ts_code=code,start_date=start, end_date=end)
#否则取的是个股数据
else:
df=pro.daily(ts_code=code, adj='qfq',start_date=start, end_date=end)
#将交易日期设置为索引值
df.index=pd.to_datetime(df.trade_date)
df=df.sort_index()
#计算收益率
df['ret']=df.close/df.close.shift(1)-1
return df
hs=get_daily_data('沪深300','20180101','')[['close','open','high','low','vol']]
#最近N1个交易日最高价
hs['up']=ta.MAX(hs.high,timeperiod=20).shift(1)
#最近N2个交易日最低价
hs['down']=ta.MIN(hs.low,timeperiod=10).shift(1)
#每日真实波动幅度
hs['ATR']=ta.ATR(hs.high,hs.low,hs.close,timeperiod=20)
print(hs.tail())
close open high ... up down ATR
trade_date ...
2020-07-17 4544.7007 4524.7717 4601.3733 ... 4878.0773 4348.4535 92.796266
2020-07-20 4680.3046 4597.2038 4681.9431 ... 4878.0773 4465.8081 95.542083
2020-07-21 4691.0425 4697.5026 4714.2925 ... 4878.0773 4485.8214 93.407404
2020-07-22 4714.4454 4682.6575 4790.4491 ... 4878.0773 4485.8214 94.492049
2020-07-23 4712.4357 4668.7401 4731.4004 ... 4878.0773 4485.8214 95.954541
[5 rows x 8 columns]
'''
(1)当今天的收盘价,大于过去20个交易日中的最高价时,以收盘价买入;
(2)买入后,当收盘价小于过去10个交易日中的最低价时,以收盘价卖出。
'''
def my_strategy(data):
x1=data.close>data.up
x2=data.close.shift(1)<data.up.shift(1)
x=x1&x2
y1=data.close<data.down
y2=data.close.shift(1)>data.down.shift(1)
y=y1&y2
data.loc[x,'signal']='buy'
data.loc[y,'signal']='sell'
buy_date=(data[data.signal=='buy'].index).strftime('%Y%m%d')
sell_date=(data[data.signal=='sell'].index).strftime('%Y%m%d')
buy_close=data[data.signal=='buy'].close.round(2).tolist()
sell_close=data[data.signal=='sell'].close.round(2).tolist()
return (buy_date,buy_close,sell_date,sell_close)
#对K线图和唐奇安通道进行可视化
import pyecharts
# from pyecharts import *
from pyecharts import Kline,Grid,Bar,Line,EffectScatter,Overlap
grid = Grid()
attr=[str(t) for t in hs.index.strftime('%Y%m%d')]
v1=np.array(hs.loc[:,['open','close','low','high']])
v2=np.array(hs.up)
v3=np.array(hs.down)
kline = Kline("沪深300唐奇安通道",title_text_size=15)
kline.add("K线图", attr, v1.round(1),is_datazoom_show=True,)
# 成交量
bar = Bar()
bar.add("成交量", attr, hs['vol'],tooltip_tragger="axis", is_legend_show=False,
is_yaxis_show=False, yaxis_max=5*max(hs["vol"]))
line = Line()
line.add("上轨线", attr, v2.round(1),is_datazoom_show=True,
is_smooth=True,is_symbol_show=False,line_width=1.5)
line.add("下轨线", attr, v3.round(1),is_datazoom_show=True,
is_smooth=True,is_symbol_show=False,line_width=1.5)
#添加买卖信号
bd,bc,sd,sc=my_strategy(hs)
es = EffectScatter("buy")
es.add( "sell", sd, sc, )
es.add("buy", bd, bc,symbol="triangle",)
overlap = Overlap(width=2000, height=600)
overlap.add(kline)
overlap.add(line)
overlap.add(bar,yaxis_index=1, is_add_yaxis=True)
overlap.add(es)
grid.add(overlap, grid_right="10%")
grid.render()
进行该操作后可生产一个采取海龟法则的html文件
grid = Grid()
attr=[str(t) for t in hs.index.strftime('%Y%m%d')]
v1=np.array(hs.loc[:,['open','close','low','high']])
v2=np.array(hs.up)
v3=np.array(hs.down)
kline = Kline("沪深300唐奇安通道",title_text_size=15)
kline.add("K线图", attr, v1.round(1),is_datazoom_show=True,)
# 成交量
bar = Bar()
bar.add("成交量", attr, hs['vol'],tooltip_tragger="axis", is_legend_show=False,
is_yaxis_show=False, yaxis_max=5*max(hs["vol"]))
line = Line()
line.add("上轨线", attr, v2.round(1),is_datazoom_show=True,
is_smooth=True,is_symbol_show=False,line_width=1.5)
line.add("下轨线", attr, v3.round(1),is_datazoom_show=True,
is_smooth=True,is_symbol_show=False,line_width=1.5)
#添加买卖信号
bd,bc,sd,sc=my_strategy(hs)
es = EffectScatter("buy")
es.add( "sell", sd, sc, )
es.add("buy", bd, bc,symbol="triangle",)
overlap = Overlap(width=2000, height=600)
overlap.add(kline)
overlap.add(line)
overlap.add(bar,yaxis_index=1, is_add_yaxis=True)
overlap.add(es)
grid.add(overlap, grid_right="10%")
# grid.render()
#关掉pandas的warnings
pd.options.mode.chained_assignment = None
def strategy(stock,start,end,N1=20,N2=10):
df=get_daily_data(stock,start,end)
#最近N1个交易日最高价
df['H_N1']=ta.MAX(df.high,timeperiod=N1)
#最近N2个交易日最低价
df['L_N2']=ta.MIN(df.low,timeperiod=N2)
#当日收盘价>昨天最近N1个交易日最高点时发出信号设置为1
buy_index=df[df.close>df['H_N1'].shift(1)].index
df.loc[buy_index,'收盘信号']=1
#将当日收盘价<昨天最近N2个交易日的最低点时收盘信号设置为0
sell_index=df[df.close<df['L_N2'].shift(1)].index
df.loc[sell_index,'收盘信号']=0
df['当天仓位']=df['收盘信号'].shift(1)
df['当天仓位'].fillna(method='ffill',inplace=True)
d=df[df['当天仓位']==1].index[0]-timedelta(days=1)
df1=df.loc[d:].copy()
df1['ret'][0]=0
df1['当天仓位'][0]=0
#当仓位为1时,买入持仓,当仓位为0时,空仓,计算资金净值
df1['策略净值']=(df1.ret.values*df1['当天仓位'].values+1.0).cumprod()
df1['指数净值']=(df1.ret.values+1.0).cumprod()
df1['策略收益率']=df1['策略净值']/df1['策略净值'].shift(1)-1
df1['指数收益率']=df1.ret
total_ret=df1[['策略净值','指数净值']].iloc[-1]-1
annual_ret=pow(1+total_ret,250/len(df1))-1
dd=(df1[['策略净值','指数净值']].cummax()-df1[['策略净值','指数净值']])/df1[['策略净值','指数净值']].cummax()
d=dd.max()
beta=df1[['策略收益率','指数收益率']].cov().iat[0,1]/df1['指数收益率'].var()
alpha=(annual_ret['策略净值']-annual_ret['指数净值']*beta)
exReturn=df1['策略收益率']-0.03/250
sharper_atio=np.sqrt(len(exReturn))*exReturn.mean()/exReturn.std()
TA1=round(total_ret['策略净值']*100,2)
TA2=round(total_ret['指数净值']*100,2)
AR1=round(annual_ret['策略净值']*100,2)
AR2=round(annual_ret['指数净值']*100,2)
MD1=round(d['策略净值']*100,2)
MD2=round(d['指数净值']*100,2)
S=round(sharper_atio,2)
df1[['策略净值','指数净值']].plot(figsize=(15,7))
plt.title('海龟交易策略简单回测',size=15)
bbox = dict(boxstyle="round", fc="w", ec="0.5", alpha=0.9)
plt.text(df1.index[int(len(df1)/5)], df1['指数净值'].max()/1.5, f'累计收益率:\
策略{TA1}%,指数{TA2}%;\n年化收益率:策略{AR1}%,指数{AR2}%;\n最大回撤: 策略{MD1}%,指数{MD2}%;\n\
策略alpha: {round(alpha,2)},策略beta:{round(beta,2)}; \n夏普比率: {S}',size=13,bbox=bbox)
plt.xlabel('')
ax=plt.gca()
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
plt.show()
#return df1.loc[:,['close','ret','H_N1','L_N2','当天仓位','策略净值','指数净值']]
strategy('上证综指','20050101','')