【手把手教你】用Python量化海龜交易法則

1 引言

對于純多頭或空頭的方向性策略而言奠骄,只有當證券價格是均值回歸或趨勢的迁匠,交易策略才能盈利剩瓶。否則,如果價格是隨機游走的城丧,交易將無利可圖(法瑪有效市場假說)延曙。換句話說,目前各種紛繁復雜的所謂量化策略大都可以歸結為均值回歸或趨勢追蹤策略亡哄。趨勢追蹤策略認為價格會沿著一定的趨勢繼續(xù)走枝缔,也常稱為“慣性”或“動量”策略,很多技術指標就是基于動量的思想來設定的蚊惯。今天為大家介紹著名的趨勢交易策略——“海龜交易法則”魂仍,著重介紹如何使用Python對海龜?shù)慕灰滓?guī)則進行量化回測拐辽,尤其是對Pandas的綜合運用。關于海龜原理的詳細介紹和相關軼事感興趣的可閱讀原書和網(wǎng)上相關資料擦酌,在微信公眾號后臺回復“海龜交易”可下載《海龜交易法則》高清中文PDF。


2 海龜交易法則簡介

海龜交易法則可以認為是一個完整的交易系統(tǒng)菠劝,具備一個完整的交易系統(tǒng)所應該有的所有成分赊舶,包括市場、入市赶诊、頭寸規(guī)模笼平、止損/止盈、退出舔痪、買賣策略等:

市場:買賣什么寓调?

頭寸規(guī)模:買賣多少?

入市:什么時候買賣锄码?

止損:什么時候放棄一個虧損的頭寸夺英?

離市:什么時候退出一個盈利的頭寸?

策略:如何買賣滋捶?

趨勢追蹤——唐奇安通道

海龜交易法則利用唐奇安通道的突破點作為買賣信號指導交易痛悯,簡單而言唐奇安通道是由一條上軌線、中線和下線組成重窟,上軌線由N1日內(nèi)最高價構成载萌,下軌線由N2日內(nèi)最低價計算,當價格沖破上軌是可能的買入信號巡扇,反之扭仁,沖破下軌時是可能的賣出信號。??

買賣單位及首次建倉

海龜交易系統(tǒng)本質(zhì)上是一個趨勢跟隨的系統(tǒng)厅翔,但是最值得學習的是資金管理尤其是分批建倉及動態(tài)止損的部分乖坠。書中提到了N值倉位管理法,其中N值與技術指標平均真實波幅 ATR計算類似知给。ATR是真實波幅TR的20日平均值瓤帚,而TR是當前交易日最高價和最低價之差 、前一交易日收盤價與當前交易日最高價之差涩赢、前一交易日收盤價與當前交易日最低價之差三者中的最大值戈次,用公式表示為:

TR=Max(High?Low,abs(High?PreClose),abs(PreClose?Low)),技術指標庫TA-Lib提供了直接計算ATR的函數(shù)筒扒。

建倉單位:

Unit=(1%?賬戶總資金)/N ?
首次建倉的時候怯邪,當捕捉到趨勢,即價格突破唐奇安上軌時花墩,買入1個unit悬秉。其意義就是澄步,讓一個N值的波動與你總資金1%的波動對應,如果買入1unit單位的資產(chǎn)和泌,當天震幅使得總資產(chǎn)的變化不超過1%村缸。??

例如:
現(xiàn)在你有1萬元資金,1%波動就是100元武氓。假如某股票的N(ATR)值為0.1元梯皿,100÷0.1元=1000股。也就是說县恕,你的第一筆倉位應該是在其突破上軌(假設為3元)時立刻買入1000股东羹,耗資3000元。

動態(tài)止損或清倉條件

當股價跌破10日唐奇安通道下沿忠烛,清空頭寸結束本次交易属提。當價格比最后一次買入價格下跌2N時,則賣出全部頭寸止損美尸。
接上面的例子冤议,最后一次加倉價格為3.2。假如此時N值0.2元火惊。當價格下跌到 3.2 - 2*0.2 = 2.8元時求类,清倉。持倉成本為 (3+3.1+3.2)*1000/3000 = 3.1元屹耐。此時虧損 (3.1-2.8)*3000 = 900元尸疆, 對于1萬來說 這波虧損9%。

原始的海龜交易采用唐奇安通道來追蹤趨勢惶岭,在趨勢比較明顯的行情表現(xiàn)不錯寿弱,但是在震蕩的行情中效果不佳,當然這是所有趨勢型策略的通病按灶。下面著重使用Python對唐奇安通道進行可視化症革,并利用簡化版的海龜交易法則進行簡單的歷史回測。

3 海龜交易規(guī)則Python實現(xiàn)


#先引入后面可能用到的包(package)
import?pandas?as?pd??
import?numpy?as?np
import?talib?as?ta
from?datetime?import?datetime,timedelta
import?matplotlib.pyplot?as?plt
%matplotlib?inline???
#正常顯示畫圖時出現(xiàn)的中文和負號
from?pylab?import?mpl
mpl.rcParams['font.sans-serif']=['SimHei']
mpl.rcParams['axes.unicode_minus']=False
#使用tushare獲取交易數(shù)據(jù)
#設置token
import?tushare?as?ts?
#注意token更換為你在tushare網(wǎng)站上獲取的
token='輸入你的token'
pro=ts.pro_api(token)
index={'上證綜指':?'000001.SH',
????????'深證成指':?'399001.SZ',
????????'滬深300':?'000300.SH',
????????'創(chuàng)業(yè)板指':?'399006.SZ',
????????'上證50':?'000016.SH',
????????'中證500':?'000905.SH',
????????'中小板指':?'399005.SZ',
????????'上證180':?'000010.SH'}
#獲取當前交易的股票代碼和名稱
def?get_code():
????df?=?pro.stock_basic(exchange='',?list_status='L')
????codes=df.ts_code.values
????names=df.name.values
????stock=dict(zip(names,codes))
????#合并指數(shù)和個股成一個字典
????stocks=dict(stock,**index)
????return?stocks????
#獲取行情數(shù)據(jù)
def?get_daily_data(stock,start,end):
????#如果代碼在字典index里鸯旁,則取的是指數(shù)數(shù)據(jù)
????code=get_code()[stock]
????if?code?in?index.values():
????????df=pro.index_daily(ts_code=code,start_date=start,?end_date=end)
????#否則取的是個股數(shù)據(jù)
????else:
????????df=pro.daily(ts_code=code,?adj='qfq',start_date=start,?end_date=end)
????#將交易日期設置為索引值
????df.index=pd.to_datetime(df.trade_date)
????df=df.sort_index()
????#計算收益率
????df['ret']=df.close/df.close.shift(1)-1
????return?df

下面以滬深300指數(shù)為例噪矛,對唐奇安通道和買賣突破信號進行可視化。

hs=get_daily_data('滬深300','20180101','')[['close','open','high','low','vol']]
#最近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()

下面使用簡化版的海龜交易法則進行歷史回測铺罢,即不考慮倉位管理和動態(tài)止損/止盈條件艇挨,以唐奇安通道突破作為買入賣出信號。

交易規(guī)則為:

(1)當今天的收盤價韭赘,大于過去20個交易日中的最高價時缩滨,以收盤價買入; ?

(2)買入后,當收盤價小于過去10個交易日中的最低價時脉漏,以收盤價賣出苞冯。

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)
#對K線圖和唐奇安通道進行可視化
from?pyecharts?import?*
grid?=?Grid()
attr=[str(t)?for?t?in?hs.index.strftime('%Y%m%d')]
v1=np.array(hs.loc[:,['open','close','low','high']])
v2=np.array(hs.up)
v3=np.array(hs.down)
kline?=?Kline("滬深300唐奇安通道",title_text_size=15)
kline.add("K線圖",?attr,?v1.round(1),is_datazoom_show=True,)
#?成交量
bar?=?Bar()
bar.add("成交量",?attr,?hs['vol'],tooltip_tragger="axis",?is_legend_show=False,?
????????is_yaxis_show=False,?yaxis_max=5*max(hs["vol"]))
line?=?Line()
line.add("上軌線",?attr,?v2.round(1),is_datazoom_show=True,
?????????is_smooth=True,is_symbol_show=False,line_width=1.5)
line.add("下軌線",?attr,?v3.round(1),is_datazoom_show=True,
?????????is_smooth=True,is_symbol_show=False,line_width=1.5)
#添加買賣信號
bd,bc,sd,sc=my_strategy(hs)
es?=?EffectScatter("buy")
es.add(?"sell",?sd,?sc,?)
es.add("buy",?bd,?bc,symbol="triangle",)
overlap?=?Overlap(width=2000,?height=600)
overlap.add(kline)
overlap.add(line)
overlap.add(bar,yaxis_index=1,?is_add_yaxis=True)
overlap.add(es)
grid.add(overlap,?grid_right="10%")
grid

(注:運行上述代碼得到的是動態(tài)交互圖,可調(diào)整時間區(qū)間)

#關掉pandas的warnings
pd.options.mode.chained_assignment?=?None
def?strategy(stock,start,end,N1=20,N2=10):
????df=get_daily_data(stock,start,end)
????#最近N1個交易日最高價
????df['H_N1']=ta.MAX(df.high,timeperiod=N1)
????#最近N2個交易日最低價
????df['L_N2']=ta.MIN(df.low,timeperiod=N2)
????#當日收盤價>昨天最近N1個交易日最高點時發(fā)出信號設置為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['指數(shù)凈值']=(df1.ret.values+1.0).cumprod()
????df1['策略收益率']=df1['策略凈值']/df1['策略凈值'].shift(1)-1
????df1['指數(shù)收益率']=df1.ret
????total_ret=df1[['策略凈值','指數(shù)凈值']].iloc[-1]-1
????annual_ret=pow(1+total_ret,250/len(df1))-1
????dd=(df1[['策略凈值','指數(shù)凈值']].cummax()-df1[['策略凈值','指數(shù)凈值']])/df1[['策略凈值','指數(shù)凈值']].cummax()
????d=dd.max()
????beta=df1[['策略收益率','指數(shù)收益率']].cov().iat[0,1]/df1['指數(shù)收益率'].var()
????alpha=(annual_ret['策略凈值']-annual_ret['指數(shù)凈值']*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['指數(shù)凈值']*100,2)
????AR1=round(annual_ret['策略凈值']*100,2)
????AR2=round(annual_ret['指數(shù)凈值']*100,2)
????MD1=round(d['策略凈值']*100,2)
????MD2=round(d['指數(shù)凈值']*100,2)
????S=round(sharper_atio,2)
????df1[['策略凈值','指數(shù)凈值']].plot(figsize=(15,7))
????plt.title('海龜交易策略簡單回測',size=15)
????bbox?=?dict(boxstyle="round",?fc="w",?ec="0.5",?alpha=0.9)
????plt.text(df1.index[int(len(df1)/5)],?df1['指數(shù)凈值'].max()/1.5,?f'累計收益率:\
策略{TA1}%巧娱,指數(shù){TA2}%;\n年化收益率:策略{AR1}%,指數(shù){AR2}%烘贴;\n最大回撤:??策略{MD1}%,指數(shù){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','當天倉位','策略凈值','指數(shù)凈值']]
下面對上證綜指桨踪、滬深300、創(chuàng)業(yè)板指數(shù)芹啥、中國平安锻离、東方通信和貴州茅臺進行簡單回測,看看海龜交易規(guī)則唐奇安的擇時效果如何墓怀,具體指標看圖汽纠。
strategy('上證綜指','20050101','')
strategy('滬深300','','')
strategy('創(chuàng)業(yè)板指','','')
strategy('滬深300','20180101','')
strategy('中國平安','20050101','',N1=20,N2=10)
strategy('東方通信','20130101','',N1=20,N2=10)
strategy('貴州茅臺','20050101','',N1=20,N2=10)
上述回測沒有考慮使用N值的倉位管理和動態(tài)止損,下面是在萬礦平臺上加入了倉位管理進行回測傀履,與上面簡單使用Pandas的回測框架相比(圖形比較丑陋)虱朵,貴州茅臺的各項回測指標看上去更理想了,最大回撤也只有21%钓账。具體實現(xiàn)代碼可參考萬礦平臺社區(qū)上面的分享碴犬。此外,聚寬梆暮、優(yōu)礦等量化平臺上也提供了相應了策略回測模板服协,實現(xiàn)代碼大同小異,感興趣的可以進一步了解啦粹。


4 結語
本文簡要介紹了海龜交易法則的基本原理偿荷,使用Python對其買賣信號進行了可視化分析,并利用Pandas對相關指數(shù)和個股運用簡化版的海龜交易規(guī)則進行了歷史回測唠椭。由回測結果可看出跳纳,該簡化的趨勢追蹤策略對于某些標的在某些區(qū)間效果表現(xiàn)不錯,但對于某些標的或某些時期則效果不佳泪蔫。當然棒旗,本文旨在回顧經(jīng)典策略,展示Pandas在金融量化分析的綜合運用,為Python在金融量化中的運用起到拋磚引玉的效果铣揉,不作出任何選股或策略推薦饶深。值得注意的是,任何策略都具有一定的局限性逛拱,尤其是知道和使用該策略的交易者多了敌厘,其作用自然比該理念剛出現(xiàn)的的效果差得多。正如技術分析指標朽合,剛出現(xiàn)的時候很有效俱两,但被大家所熟知或應用后,自然效用就大打折扣(相對于多因子模型中的Alpha被大家挖掘后漸漸成了risk factor)曹步。但所謂新理念宪彩、新策略一定是站在前人的肩膀上,因此不能因為經(jīng)典策略回測效果不佳而全盤否定讲婚,如何改進尿孔、細化和升級,使之更適合當下的市場才是我們要面對的問題筹麸。
關于Python金融量化


專注于分享Python在金融量化領域的應用活合。加入知識星球,可以免費獲取30多g的量化投資視頻資料物赶、量化金融相關PDF資料白指、公眾號文章Python完整源碼、量化投資前沿分析框架酵紫,與博主直接交流告嘲、結識圈內(nèi)朋友等。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末憨闰,一起剝皮案震驚了整個濱河市状蜗,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌鹉动,老刑警劉巖轧坎,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異泽示,居然都是意外死亡缸血,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門械筛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來捎泻,“玉大人,你說我怎么就攤上這事埋哟“驶恚” “怎么了郎汪?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長闯狱。 經(jīng)常有香客問我煞赢,道長,這世上最難降的妖魔是什么哄孤? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任照筑,我火速辦了婚禮,結果婚禮上瘦陈,老公的妹妹穿的比我還像新娘凝危。我一直安慰自己,他們只是感情好晨逝,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布蛾默。 她就那樣靜靜地躺著,像睡著了一般捉貌。 火紅的嫁衣襯著肌膚如雪趴生。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天昏翰,我揣著相機與錄音,去河邊找鬼刘急。 笑死棚菊,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的叔汁。 我是一名探鬼主播统求,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼据块!你這毒婦竟也來了码邻?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤另假,失蹤者是張志新(化名)和其女友劉穎像屋,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體边篮,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡己莺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了戈轿。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凌受。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖思杯,靈堂內(nèi)的尸體忽然破棺而出胜蛉,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布誊册,位于F島的核電站领突,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏解虱。R本人自食惡果不足惜攘须,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望殴泰。 院中可真熱鬧于宙,春花似錦、人聲如沸悍汛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽离咐。三九已至谱俭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間宵蛀,已是汗流浹背昆著。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留术陶,地道東北人凑懂。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像梧宫,于是被迫代替她去往敵國和親接谨。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容