海龟交易法则是一种著名的趋势跟踪交易策略,适用于中长线投资者。
海龟交易策略(Turtle Trading)起源于美国,由著名的交易员理查德·丹尼斯(Richard Dennis)创立。这种交易策略属于趋势跟踪策略,适用于中长线投资者。其核心思想是捕捉市场的主要趋势,并在趋势开始时买入,趋势结束时卖出。海龟交易法则以严格的纪律和长期的跟踪为特点,旨在实现稳定的长期盈利。
海龟交易策略一套完整的交易系统,包括市场,入市,头寸规模、止损/止盈,出场,买卖策略等。
总的来说,海龟交易法则是一套以风险控制为核心的交易系统,旨在捕捉市场趋势并最大化盈利。
趋势追踪——唐奇安通道
海龟交易法则利用唐奇安通道的突破点作为买卖信号指导交易,简单而言唐奇安通道是由一条上轨线、中线和下线组成,上轨线由N1日内最高价构成,下轨线由N2日内最低价计算,当价格冲破上轨是可能的买入信号,反之,冲破下轨时是可能的卖出信号。
海龟交易系统本质上是一个趋势跟随的系统,但是最值得学习的是资金管理尤其是分批建仓及动态止损的部分。书中提到了N值仓位管理法,其中N值与技术指标平均真实波幅 ATR计算类似。ATR是真实波幅TR的20日平均值,而TR是当前交易日最高价和最低价之差 、前一交易日收盘价与当前交易日最高价之差、前一交易日收盘价与当前交易日最低价之差三者中的最大值,用公式表示为:
TR=Max(High−Low,abs(High−PreClose),abs(PreClose−Low)),技术指标库TA-Lib提供了直接计算ATR的函数。
下面使用简化版的海龟交易法则进行历史回测,即不考虑仓位管理和动态止损/止盈条件,以唐奇安通道突破作为买入卖出信号。
交易规则为:
(1)当今天的收盘价,大于过去20个交易日中的最高价时,以收盘价买入;
(2)买入后,当收盘价小于过去10个交易日中的最低价时,以收盘价卖出。
import pandas as pd
import numpy as np
import talib as ta
from datetime import datetime,timedelta
import matplotlib.pyplot as plt
%matplotlib inline
#正常显示画图时出现的中文和负号
import warnings
warnings.filterwarnings("ignore")
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus']=False
import baostock as bs
#### 登陆系统 ####
def get_k_data(code,start,end):
lg = bs.login()
rs = bs.query_history_k_data_plus(code,
"date,code,open,high,low,close,preclose,volume,amount,adjustflag,turn,tradestatus,pctChg,peTTM,pbMRQ,psTTM,pcfNcfTTM,isST",
start_date=start, end_date=end,
frequency="d", adjustflag="3")
data=rs.get_data()
data['date'] = pd.to_datetime(data['date'])
bs.logout()
return data.set_index('date')
为了与前面的文章统一,仍选sh.600000,时间区间为从2023-1-1 到 2023-10-19
hs=get_k_data('sh.600000','2023-1-1','2023-10-19')[['close','open','high','low','volume']]
#最近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)
hs.tail()
date | close | open | high | low | volume | up | down | ATR |
---|---|---|---|---|---|---|---|---|
2023-10-13 | 7.1000 | 7.1100 | 7.1500 | 7.0800 | 19650410 | 7.2 | 7.0 | 0.096226 |
2023-10-16 | 7.0700 | 7.1200 | 7.1300 | 7.0400 | 24907733 | 7.2 | 7.0 | 0.095914 |
2023-10-17 | 7.0900 | 7.0900 | 7.1000 | 7.0500 | 19029143 | 7.2 | 7.0 | 0.093619 |
2023-10-18 | 7.0500 | 7.0700 | 7.1100 | 7.0500 | 21485721 | 7.2 | 7.0 | 0.091938 |
2023-10-19 | 6.8400 | 7.0400 | 7.0500 | 6.8300 | 61679771 | 7.2 | 7.0 | 0.098341 |
设置买卖策略及相关信号
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)
以下代码实现了回测,并加上了alpha 、 beta、年化收益、夏普比率等
年化收益率=[(投资内收益 / 本金)/ 投资天数] * 365×100%
而实际上采用以下公式计算,考虑到一年的交易日 约为250日
p r = ( p e n d p s t a r ) 250 / n − 1 p_r=(\frac{p_{end}}{p_{star}})^{250/n}-1 pr=(pstarpend)250/n−1
这里的alpha和beta 是指量化投资理论的 α \alpha α 和 β \beta β 。
表示投资的系统性风险,反映了策略对大盘变化的敏感性。
例如,一个策略的Beta为1.3,则大盘涨1%的时候,策略可能涨1.3%,反之亦然;
如果一个策略的Beta为-1.3,说明大盘涨1%的时候,策略可能跌1.3%,反之亦然。
β = C o v ( p n , B n ) σ B 2 \beta=\frac{Cov(p_n,B_n)}{\sigma^2_B} β=σB2Cov(pn,Bn)
p n p_n pn:策略每日收益率
B n B_n Bn:基准每日收益率
σ B 2 \sigma^2_B σB2:基准每日收益方差
C o v ( p n , B n ) Cov(p_n,B_n) Cov(pn,Bn):策略和基准每日收益率的协方差
非系统性风险,Alpha是投资者获得与市场波动无关的回报,一般用来度量投资者的投资技艺。例如,投资者获得了12%的回报,其基准获得了10%的回报,那么Alpha或者价值增值的部分就是2%。
α = p r − r f − β ( B r − r f ) \alpha=p_r-r_f-\beta(B_r-r_f) α=pr−rf−β(Br−rf)
p r p_r pr:策略的年化收益率
r f r_f rf:无风险收益率
B r B_r Br:基准年化收益率
用来测量资产的风险性,波动越大代表策略风险越高。
σ p = 250 n − 1 ∑ t = 1 n ( p t − p t ‾ ) 2 \sigma_p=\sqrt{\frac{250}{n-1} \sum^n_{t=1}(p_t- \overline{p_t})^2} σp=n−1250∑t=1n(pt−pt)2
表示每承受一单位总风险,会产生多少的超额报酬,可以同时对策略的收益与风险进行综合考虑。
夏普比率为(算法交易收益-基准收益)/算法交易收益的标准差
S h a r p R a t i o = p r − r f σ p SharpRatio=\frac{p_r-r_f}{\sigma_p} SharpRatio=σppr−rf
p r p_r pr:策略的年化收益率
r f r_f rf:无风险收益率
σ p \sigma_p σp策略收益率波动率
衡量单位超额风险带来的超额收益。
信息比率越大,说明该策略单位跟踪误差所获得的超额收益越高,因此,信息比率较大的策略的表现要优于信息比率较小的策略。合理的投资目标应该是在承担适度风险下,尽可能追求高信息比率。
I n f o r m a t i o n R a t i o = p r − B r σ t InformationRatio=\frac{p_r-B_r}{\sigma_t} InformationRatio=σtpr−Br
描述策略可能出现的最糟糕的情况。
M a x D r a w D o w n t = max ( 1 − P j P i ) MaxDrawDown_t=\max(1-\frac{P_j}{P_i}) MaxDrawDownt=max(1−PiPj)
def strategy(stock,start,end,N1=20,N2=10):
df=get_k_data(stock,start,end)[['close','open','high','low','volume']]
df=df.astype("float")
df['ret']=df.close/df.close.shift(1)-1
#最近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(stock+'海龟交易策略简单回测',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.08, 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("sh.600000","2023-01-01","2023-10-19")
其他其他策略的收益情况
strategy('sz.300576','2023-01-01','2023-10-19')
经过上述步骤,我们就实现了一个简单的海龟交易策略。交易次数并不多,另外可通过对上述代码做进一步的扩展,使其更加完善和复杂。
原始的海龟交易采用唐奇安通道来追踪趋势,在趋势比较明显的行情表现不错,但是在震荡的行情中效果不佳,当然这是所有趋势型策略的通病。下面着重使用Python对唐奇安通道进行可视化,并利用简化版的海龟交易法则进行简单的历史回测。