刺猬教你量化投资(二十五):如何用Python实现海龟交易法则

交易史上最著名的实验

使用海龟交易法则之前,需要先了解注明的海龟实验。1983年年中,著名的商品投机家理查德.丹尼斯与他的老友比尔.埃克哈特进行了一场辩论,这场辩论是关于伟大的交易员是天生造就还是后天培养的。理查德相信,他可以教会人们成为伟大的交易员。比尔则认为遗传和天性才是决定因素。

为了解决这一问题,理查德建议招募并培训一些交易员,给他们提供真实的帐户进行交易,看看两个人中谁是正确的。理查德或许是当时世界上最著名的交易员,所以,有1000多位申请人前来投奔他。他会见了其中的80位,并在这群人精选出10个人。后来这个名单变成13个人,所增加的3个人理查德以前就认识。

理查德在开始这项计划之前,刚从亚洲回来。他在新加坡的海龟农场曾说过:“我们要培养交易者,就像新加坡人养海龟一样。”于是,这13名学员日后便被称作‘海龟’。最终,这项计划成为交易史上最著名的实验,在随后的四年中,海龟们取得了年均复利80%的收益。

理查德也证明了交易可以被传授。他用一套简单的法则,就可以使仅有很少或根本没有交易经验的人成为优秀的交易员。这套法则便是著名的海龟交易法则。

海龟交易法则的原理

海龟交易法则有四大模块,分别是入市、资金管理、止损及退出。这四大模块分别解决了在哪个市场买卖、买卖什么、如何设定头寸规模、什么时候买卖、什么时候止损、什么时候退出市场等问题,是一套完整的交易体系。

入市部分

我们需要构建唐奇安通道来生成交易的指令。唐奇安通道的使用方式类似于布林线,但构建时并不使用标准差,而是捕捉趋势信号。上线为过去20天的最高价,下线为过去10天的最低价,中线是上线加下线除以2。当股价上破时买入,当股价下坡时卖出。

资金管理部分

需要计算N值。N值与平均真实波幅ATR相似,我们通过N值来确定仓位的大小。原则是波动大的市场买少一点,波动小的市场买多一点,最终使得在不同市场中承受的风险相似。

N值的含义是,过去20天股价最大波动范围的平均幅度。TrueRange=Max(High−Low,abs(High−PreClose),abs(PreClose−Low))
N=(PreN∗19+TrueRange20) / 20
Unit = 1/N

计算Unit公式的背后含义是,UnitN=Capital1%。在股票市场中计算的Unit就是多少股。当股价在上一次买入的基础上上涨了0.5N,则加仓一个Unit。

止损部分

当股价比最后一次买入的价格下跌2N,则立即清仓止损。

用Python实现

下面我们以中国平安的个股为例,根据海龟交易法则进行操作:

import numpy as np
import pandas as pd
from CAL.PyCAL import *

start = '2013-01-01'                       # 回测起始时间
end = '2019-01-01'                         # 回测结束时间
benchmark = '601318.XSHG'                        
universe = ['601318.XSHG']
capital_base = 100000                      # 起始资金
freq = 'd'                              
refresh_rate = 1        

accounts = {
    'fantasy_account': AccountConfig(account_type='security', capital_base=100000)
}

def initialize(context):       #初始化环境
    context.last_buy_prcie = 0  #上一次买入价
    context.hold_flag = False   # 是否持有头寸标志
    context.limit_unit = 10      # 限制最多买入的单元数 
    context.unit = 0
    context.add_time = 0        # 买入次数

def CalcATR(data):
    TR_List = []
    for i in range(1,21):
        TR = max(data['highPrice'].iloc[i]-data['lowPrice'].iloc[i],abs(data['highPrice'].iloc[i]-data['closePrice'].iloc[i-1]),abs(data['closePrice'].iloc[i-1]-data['lowPrice'].iloc[i]))
        TR_List.append(TR)
    ATR = np.array(TR_List).mean()
    return ATR

def CalcUnit(perValue,ATR):
    return round((perValue/ATR)/100)*100

def SellComplete(hold_flag,security_position):
    if len(security_position)>0 and hold_flag==False:
        return True
    else:
        return False

def IN_OR_OUT(data,price,T):
    up = max(data['highPrice'].iloc[-T:-1])
    down = min(data['lowPrice'].iloc[-int(T/2):-1])  # 这里是10日唐奇安下沿
    if price>up:
        return 1
    elif price<down:
        return -1
    else:
        return 0 

def Add_OR_Stop(price,lastprice,ATR):
    if price >= lastprice + 0.5*ATR:
        return 1
    elif price <= lastprice - 2*ATR:
        return -1
    else:
        return 0


def handle_data(context): 
    T = 20
    stock_account = context.get_account('fantasy_account')
    data = context.history(['601318.XSHG'], attribute=['openPrice', 'highPrice', 'lowPrice', 'closePrice'], time_range=T+1, freq='1d', style='sat', rtype='frame')
    stk = universe[0]
    data = data[stk]
    data = pd.DataFrame(data)
    prices = context.current_price(stk) #获得最近价格
    today = Date.fromDateTime(context.current_date) 
    today = today.toISO() #转换为标准日期格式

    #剔除停牌的股票
    if np.isnan(prices) or prices == 0:  # 停牌或是还没有上市等原因不能交易
        return 

    # 计算ATR和unit
    ATR = CalcATR(data)
    value = stock_account.portfolio_value * 0.01
    context.unit = CalcUnit(value,ATR)

    #判断是否处于空仓的状态
    if SellComplete(context.hold_flag,stock_account.get_positions()): 
        for stk in stock_account.get_positions():
            stock_account.order_to(stk,0)

    #判断买卖时机
    out = IN_OR_OUT(data,prices,T)
    if out ==1 and context.hold_flag==False:  
        stock_account.order_to(stk,context.unit)
        context.add_time = 1 #入场之后持仓就+1
        context.hold_flag = True
        context.last_buy_prcie = prices             
    elif out==-1 and context.hold_flag ==True: #离场
        stock_account.order_to(stk,0)
        initialize(context)   # 重新初始化参数
     
    #判断加仓或止损
    if context.hold_flag==True and len(stock_account.get_positions())>0:   
        temp = Add_OR_Stop(prices,context.last_buy_prcie,ATR)
        if temp ==1 and context.add_time<=context.limit_unit:  # 加仓
            order_num = min(context.unit,int((stock_account.cash/prices)/100)*100)      
            stock_account.order(stk,order_num)
            context.last_buy_prcie = prices
            context.add_time += 1
        elif temp== -1:      # 止损
            stock_account.order_to(stk,0)
            initialize(context)  

    return

写完代码后运行,结果如下:

image.png

综上所述,海龟交易法则的应用效果还不错。



刺猬偷腥
2019年1月26日

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,099评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,473评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,229评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,570评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,427评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,335评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,737评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,392评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,693评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,730评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,512评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,349评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,750评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,017评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,290评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,706评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,904评论 2 335

推荐阅读更多精彩内容