第四章:经典量化策略集锦(第六篇:交易系统终结者—海龟交易法则)

导语:作为策略锦集第六篇,再向大家介绍非常著名的交易系统—海龟交易法则。 






一、策略阐述 






1.海龟交易法则的由来 






    Riachard Dennis 是七八十年代著名的期货投机商,是一位具有传奇色彩的人物,在多年 


的投机生涯中,Dennis 出尽风头,给人的感觉是常常可以在最低点买进,然后在最高峰反手 


卖空。 






    他相信优秀的交易员是后天培养而非天生的。在1983 年12 月,他招聘了23 名新人,昵 


称为海龟,并对这些交易员进行了一个趋势跟踪交易策略培训。随后给予每个新人 100 万美 


元的初始资金。经5 年的运作,大部分“海龟”的业绩非常惊人,其中最好的业绩达到1.72 


亿美元。多年后,海龟交易法则公布于世,我们才有幸看到曾名噪一时的完整的海龟交易法 


则。 






2.海龟交易法则介绍 






A.市场与标的 






    海龟交易法则应用于流动性高的市场中,选择交易量较大的标的进行交易。本文以沪深 


300 指数 ETF 为标的,构建海龟交易法则。 






B 、仓位:仓位是海龟交易系统最核心的部分,通过ATR 真实波幅指标来管理仓位。 






第一步:计算True Range ,简称TR 。 






    TR = Max ( H−L , H−P , P−L )  ,其中H 为当日日内最高价,L 为当日日内最低价,P 为 


前一日收盘价。 






第二步:计算ATR 






    ATR = mean ( TR , 20 ) ,  即计算过去 20 天的TR 的平均值,ATR 是 TR 的移动平均值 






第三步:计算unit  (法则的交易单位) 






    Unit = (value * 1% ) / ATR  ,其中value*1% 即为总资产的 1%,考虑到国内最小变化量是 


0.01 元,1 手是 100 股,所以1ATR 即为持1 股股票的资产最大变动,那么买入 1Unit 单位的 


股票,使得总资产当天震幅不超过 1%  。 




----------------------- Page 95-----------------------


C.开仓入市 






    海龟交易法则分两个交易系统,两者都为分钟回测,即盘中交易: 






系统一: 






I 、若当前价格高于过去 20  日的最高价,则买入一个Unit 


II 、加仓:若股价在上一次买入的基础上上涨了0.5ATR,则加仓一个 Unit 。 






系统二: 






I 、若当前价格高于过去 55 日的最高价,则买入一个Unit 


II 、加仓:若股价在上一次买入的基础上上涨了0.5N ,则加仓一个 Unit 。 






举例:若某只股票 A 的ATR 为 1,20  日最高价为40 。 


则当股价突破40 时买入一个Unit ,当股价突破40+0.5×1=40.5 时加仓一个 Unit 。 


当股价突破40.5+0.5×1=41 时加仓一个 Unit 。 






D.止损:海龟交易法则规定,当价格比最后一次买入价格下跌2ATR 时,则卖出全部头寸止 


损。 






E.止盈:两个系统分别采用不同参数来止盈。 






系统一:当股价跌破10 日内最低价格时,清仓结束交易 






系统二、当股价跌破20  日内最低价格时,清仓结束交易 






    考虑到系统一与系统二的差异性集中在参数上,本篇内容实现系统二,来向大家展示海 


龟交易法则。 






    以下为策略实现的基本信息: 






    策略实现难度:2 


    实现过程中所需要用到的API 函数,ps:通过 MindGo 量化交易平台 API 文档快速掌握: 






需要用到的API 函数            功能 






account.portfolio_value 获取账户总资产 






set_benchmark()        设置基准指数 




----------------------- Page 96-----------------------


二、代码示意图 




----------------------- Page 97-----------------------


三、编写释义 






    本策略的编写难点在于理解海龟交易法则的运行逻辑,以下是海龟法则运行逻辑的梳理: 


    编写海龟交易法则的时候,建议采用主干+枝干的思路。 




----------------------- Page 98-----------------------


四、最终结果 






策略回测区间:2014.01.01-2018.01.29 


回测资金:1000000 


回测频率:分钟 


回测结果:红色曲线为策略收益率曲线,蓝色曲线为对应的基准指数收益率曲线 




----------------------- Page 99-----------------------


策略源代码: 






import pandas as pd  


import numpy as np  


#===========================初始化账户================================== 


==  


def initialize(account): 


    account.security = '159919.OF'#确定交易标的 


    set_benchmark(account.security) 


    account.ART = 0#储存ATR 的值,每个交易 日更新一次 


    account.unit = 0# 买卖单位的储存变量 


    account.steam = False#交易系统 


    account.price = 0 #记录系统的买入价,以便加仓和离市 


#======================盘前运行============================ 


def before_trading_start(account,data):   


    #更新n 值 


    ATR=get_ATR(account,data) 


    #获取账户总资产 


    value=account.portfolio_value 


    # 依本策略,计算一个单位的unit 为多少股, 以便后续下单交易,注意一共两个系统,因此 


需要除以2 


    account.unit = (value*0.01)/account.ATR 


    if account.unit<100: 


        log.info('一个unit 单位的股数不满1 手,无法下单!') 


     


#======================设置买卖条件,分钟回测============================ 


def handle_data(account,data): 


      #====================系统 1================================ 


    #系统是否需要开启运作 


    if account.steam == False: 


        #获取开启系统的结果 


        account.steam = steam(account,data,55) 


        if account.steam == False: 


            pass 


        else: 


            order(account.security,account.unit) 


            #买入后记录当前价位,以便加仓和离市 


            log.info('系统开启') 


            nowclose = history(account.security, ['close'], 1, '1m', False, 'pre', is_panel=1)['close'] 


            account.price = nowclose[0] 


    #系统已经开启运作 


    if account.steam == True: 


        #获取进行加仓结果 


        signal=addtrade(account,data) 


        #执行结果加仓 


        if signal=='buy': 


            log.info('系统加仓') 


            nowclose = history(account.security, ['close'], 1, '1m', False, 'pre', is_panel=1)['close'] 


            order(account.security,account.unit) 




----------------------- Page 100-----------------------


            #买入后记录当前价位,以便加仓和离市 


            account.price1 = nowclose[0] 


        else: 


            pass 


        #获取止盈结果 


        signal=down(account,data) 


        #执行止盈,关闭系统 


        if signal=='sell': 


            log.info('系统 1 止盈') 


            order_target(account.security,0) 


            #止盈后情况价位记录 


            account.price = 0 


            #关闭系统 


            account.steam = False 


    #离场结果判断 


    if account.steam==True: 


        #获取离场结果 


        signal=giveuptrade(account,data,20) 


        #执行离场,关闭系统 


        if signal=='sell': 


            log.info('系统 1 离场') 


            order_target(account.security,0) 


            #离场后情况价位记录 


            account.price=0 


            #关闭系统 


            account.steam=False 


#=================判断是否离场函数============================ 


def giveuptrade(account,data,n): 


    #根据系统来获取相应数据长度 


    close = history(account.security, ['low'], n, '1d', False, 'pre', is_panel=1)['low'] 


    close_min=min(close) 


    nowclose = history(account.security, ['close'], 1, '1m', False, 'pre', is_panel=1)['close'] 


    #最新价格突破过去N  日最大收盘价,即为突破,开启系统 


    if nowclose[0]<close_min: 


        return 'sell' 


    else: 


        return None 


#==================判断是否止盈函数================================= 


def down(account,data): 


    nowclose = history(account.security, ['close'], 1, '1m', False, 'pre', is_panel=1)['close'] 


    n=account.ATR 


    TP=account.price-2*n 


    if nowclose[0]<TP: 


        return 'sell' 


    else: 


        return None 






#==================判断是否加仓函数================================= 


def addtrade(account,data): 


    nowclose = history(account.security, ['close'], 1, '1m', False, 'pre', is_panel=1)['close'] 




----------------------- Page 101-----------------------


    n=account.ATR 


    TP=account.price+n/2 


    if nowclose[0]>TP: 


        return 'buy' 


    else: 


        return None 


     


#==================判断系统开启函数================================= 


def steam(account,data,n): 


    close = history(account.security, ['close'], n, '1d', False, 'pre', is_panel=1)['close'] 


    close_max=max(close) 


    nowclose = history(account.security, ['close'], 1, '1m', False, 'pre', is_panel=1)['close'] 


    #最新价格突破过去N  日最大收盘价,即为突破,开启系统 


    if nowclose[0]>close_max: 


        return True 


    else: 


        return False 


#==================计算 n 值的函数================================= 


def get_ATR(account,data): 


    # 由于用到前20 个交易 日的n 值,ATR 计为过去 20  日的TR 均值 


    price = history(account.security, ['close','high','low'], 21, '1d', False, 'pre', is_panel=1) 


    h = price['high'].iloc[1:] #最高价,获取21 个需弃掉第一个 


    l= price['low'].iloc[1:]#最低价,获取21 个需弃掉第一个 


    rc = price['close'].shift().iloc[1:]# 昨日收盘价,获取21 个需弃掉第一个 


    #shift()操作专门是用于获取前收盘价数据的 


    tr_list = [] 


    for i in range(0,20,1): 


        h = price['high'].iloc[i] 


        l = price['low'].iloc[i] 


        rc = price['close'].iloc[i] 


        TR = max(h-l, h-rc, rc-l) 


        tr_list.append(TR) 


    ATR=np.mean(tr_list) 


    account.ATR=ATR 


    return account.ATR 




----------------------- Page 102-----------------------
阅读更多

更多精彩内容