刺猬教你量化投资(二十五):如何用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= 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日

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