• 【量化交易笔记】12.海龟交易策略


    引言

    海龟交易法则是一种著名的趋势跟踪交易策略,适用于中长线投资者。

    海龟交易策略(Turtle Trading)起源于美国,由著名的交易员理查德·丹尼斯(Richard Dennis)创立。这种交易策略属于趋势跟踪策略,适用于中长线投资者。其核心思想是捕捉市场的主要趋势,并在趋势开始时买入,趋势结束时卖出。海龟交易法则以严格的纪律和长期的跟踪为特点,旨在实现稳定的长期盈利。

    原理:

    海龟交易策略一套完整的交易系统,包括市场,入市,头寸规模、止损/止盈,出场,买卖策略等。

    1. 市场:海龟交易者关注全球市场,主要观察股票、债券、商品、货币等市场的趋势。
    2. 入市:海龟交易者使用一定的技术指标来判断市场趋势,并在市场价格突破一定阻力位时买入。例如,他们可能会使用移动平均线或趋势线来确定趋势。
      头寸规模:海龟交易者会根据他们的风险承受能力和市场状况来决定头寸规模。他们通常会使用一定比例的账户资金来投资,以控制风险。
    3. 止损/止盈:海龟交易者会设定止损点和止盈点,以限制损失并锁定盈利。止损点通常设置在离当前价格一定距离的位置,以减少损失。止盈点则根据市场状况和投资者的目标来设定。
    4. 出场:海龟交易者在市场价格达到止盈点或止损点时平仓。他们也会在市场趋势逆转时出场,以避免损失。
    5. 买卖策略:海龟交易者既可以做多,也可以做空。他们通常会观察市场趋势,并在趋势开始时买入,趋势结束时卖出。他们也会在市场波动较大时进行反趋势交易,以获取短线利润。

    总的来说,海龟交易法则是一套以风险控制为核心的交易系统,旨在捕捉市场趋势并最大化盈利。

    趋势追踪——唐奇安通道

    海龟交易法则利用唐奇安通道的突破点作为买卖信号指导交易,简单而言唐奇安通道是由一条上轨线、中线和下线组成,上轨线由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')
    
    • 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

    为了与前面的文章统一,仍选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()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    datecloseopenhighlowvolumeupdownATR
    2023-10-137.10007.11007.15007.0800196504107.27.00.096226
    2023-10-167.07007.12007.13007.0400249077337.27.00.095914
    2023-10-177.09007.09007.10007.0500190291437.27.00.093619
    2023-10-187.05007.07007.11007.0500214857217.27.00.091938
    2023-10-196.84007.04007.05006.8300616797717.27.00.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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    以下代码实现了回测,并加上了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/n1

    这里的alpha和beta 是指量化投资理论的 α \alpha α β \beta β

    贝塔(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)

    非系统性风险,Alpha是投资者获得与市场波动无关的回报,一般用来度量投资者的投资技艺。例如,投资者获得了12%的回报,其基准获得了10%的回报,那么Alpha或者价值增值的部分就是2%。

    α = p r − r f − β ( B r − r f ) \alpha=p_r-r_f-\beta(B_r-r_f) α=prrfβ(Brrf)
    p r p_r pr:策略的年化收益率
    r f r_f rf:无风险收益率
    B r B_r Br:基准年化收益率

    收益波动率(Volatility)

    用来测量资产的风险性,波动越大代表策略风险越高。
    σ 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=n1250t=1n(ptpt)2

    夏普比率(Sharpe Ratio)

    表示每承受一单位总风险,会产生多少的超额报酬,可以同时对策略的收益与风险进行综合考虑。
    夏普比率为(算法交易收益-基准收益)/算法交易收益的标准差
    S h a r p R a t i o = p r − r f σ p SharpRatio=\frac{p_r-r_f}{\sigma_p} SharpRatio=σpprrf
    p r p_r pr:策略的年化收益率
    r f r_f rf:无风险收益率
    σ p \sigma_p σp策略收益率波动率

    信息比率(Information Ratio)

    衡量单位超额风险带来的超额收益。

    信息比率越大,说明该策略单位跟踪误差所获得的超额收益越高,因此,信息比率较大的策略的表现要优于信息比率较小的策略。合理的投资目标应该是在承担适度风险下,尽可能追求高信息比率。
    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=σtprBr

    最大回撤(Max Drawdown)

    描述策略可能出现的最糟糕的情况。

    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(1PiPj)

    
    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','当天仓位','策略净值','指数净值']]
    
    • 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

    直接调用

    strategy("sh.600000","2023-01-01","2023-10-19")
    
    • 1

    其他其他策略的收益情况

    strategy('sz.300576','2023-01-01','2023-10-19')
    
    • 1

    在这里插入图片描述

    结论

    经过上述步骤,我们就实现了一个简单的海龟交易策略。交易次数并不多,另外可通过对上述代码做进一步的扩展,使其更加完善和复杂。

    原始的海龟交易采用唐奇安通道来追踪趋势,在趋势比较明显的行情表现不错,但是在震荡的行情中效果不佳,当然这是所有趋势型策略的通病。下面着重使用Python对唐奇安通道进行可视化,并利用简化版的海龟交易法则进行简单的历史回测。

  • 相关阅读:
    TensorRTX 生成Engine笔记 Alexnet
    每日4道算法题——第005天
    Delphi XE2 新控件 布局Panel TGridPanel TFlowPanel
    说说HTTP 和 HTTPS 有什么区别?
    什么是Jmeter?Jmeter使用的原理步骤是什么?
    Copilot 初体验
    Codigger的项目代码检测工具:特性和优势
    数字签名与数字证书
    C++之STL(十)
    MySQL安装及初始密码设置
  • 原文地址:https://blog.csdn.net/cndrip/article/details/134014062