导语:作为策略锦集第六篇,再向大家介绍非常著名的交易系统—海龟交易法则。
一、策略阐述
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-----------------------