用Python量化海龟交易法则
1、引言
對(duì)于純多頭或空頭的方向性策略而言,只有當(dāng)證券價(jià)格是均值回歸或趨勢(shì)的,交易策略才能盈利。否則,如果價(jià)格是隨機(jī)游走的,交易將無(wú)利可圖(法瑪有效市場(chǎng)假說(shuō))。換句話說(shuō),目前各種紛繁復(fù)雜的所謂量化策略大都可以歸結(jié)為均值回歸或趨勢(shì)追蹤策略。趨勢(shì)追蹤策略認(rèn)為價(jià)格會(huì)沿著一定的趨勢(shì)繼續(xù)走,也常稱(chēng)為“慣性”或“動(dòng)量”策略,很多技術(shù)指標(biāo)就是基于動(dòng)量的思想來(lái)設(shè)定的。今天為大家介紹著名的趨勢(shì)交易策略——“海龜交易法則”,著重介紹如何使用Python對(duì)海龜?shù)慕灰滓?guī)則進(jìn)行量化回測(cè),尤其是對(duì)Pandas的綜合運(yùn)用。關(guān)于海龜原理的詳細(xì)介紹和相關(guān)軼事感興趣的可閱讀原書(shū)和網(wǎng)上相關(guān)資料。
2、海龜交易法則簡(jiǎn)介
?
海龜交易法則可以認(rèn)為是一個(gè)完整的交易系統(tǒng),具備一個(gè)完整的交易系統(tǒng)所應(yīng)該有的所有成分,包括市場(chǎng)、入市、頭寸規(guī)模、止損/止盈、退出、買(mǎi)賣(mài)策略等:
市場(chǎng):買(mǎi)賣(mài)什么?
頭寸規(guī)模:買(mǎi)賣(mài)多少?
入市:什么時(shí)候買(mǎi)賣(mài)?
止損:什么時(shí)候放棄一個(gè)虧損的頭寸?
離市:什么時(shí)候退出一個(gè)盈利的頭寸?
策略:如何買(mǎi)賣(mài)?
趨勢(shì)追蹤——唐奇安通道
海龜交易法則利用唐奇安通道的突破點(diǎn)作為買(mǎi)賣(mài)信號(hào)指導(dǎo)交易,簡(jiǎn)單而言唐奇安通道是由一條上軌線、中線和下線組成,上軌線由N1日內(nèi)最高價(jià)構(gòu)成,下軌線由N2日內(nèi)最低價(jià)計(jì)算,當(dāng)價(jià)格沖破上軌是可能的買(mǎi)入信號(hào),反之,沖破下軌時(shí)是可能的賣(mài)出信號(hào)。??
買(mǎi)賣(mài)單位及首次建倉(cāng)
海龜交易系統(tǒng)本質(zhì)上是一個(gè)趨勢(shì)跟隨的系統(tǒng),但是最值得學(xué)習(xí)的是資金管理尤其是分批建倉(cāng)及動(dòng)態(tài)止損的部分。書(shū)中提到了N值倉(cāng)位管理法,其中N值與技術(shù)指標(biāo)平均真實(shí)波幅 ATR計(jì)算類(lèi)似。ATR是真實(shí)波幅TR的20日平均值,而TR是當(dāng)前交易日最高價(jià)和最低價(jià)之差 、前一交易日收盤(pán)價(jià)與當(dāng)前交易日最高價(jià)之差、前一交易日收盤(pán)價(jià)與當(dāng)前交易日最低價(jià)之差三者中的最大值,用公式表示為:
TR=Max(High?Low,abs(High?PreClose),abs(PreClose?Low)),技術(shù)指標(biāo)庫(kù)TA-Lib提供了直接計(jì)算ATR的函數(shù)。
建倉(cāng)單位:
Unit=(1%?賬戶(hù)總資金)/N ?
首次建倉(cāng)的時(shí)候,當(dāng)捕捉到趨勢(shì),即價(jià)格突破唐奇安上軌時(shí),買(mǎi)入1個(gè)unit。其意義就是,讓一個(gè)N值的波動(dòng)與你總資金1%的波動(dòng)對(duì)應(yīng),如果買(mǎi)入1unit單位的資產(chǎn),當(dāng)天震幅使得總資產(chǎn)的變化不超過(guò)1%。??
例如:
現(xiàn)在你有1萬(wàn)元資金,1%波動(dòng)就是100元。假如某股票的N(ATR)值為0.1元,100÷0.1元=1000股。也就是說(shuō),你的第一筆倉(cāng)位應(yīng)該是在其突破上軌(假設(shè)為3元)時(shí)立刻買(mǎi)入1000股,耗資3000元。
動(dòng)態(tài)止損或清倉(cāng)條件
當(dāng)股價(jià)跌破10日唐奇安通道下沿,清空頭寸結(jié)束本次交易。當(dāng)價(jià)格比最后一次買(mǎi)入價(jià)格下跌2N時(shí),則賣(mài)出全部頭寸止損。
接上面的例子,最后一次加倉(cāng)價(jià)格為3.2。假如此時(shí)N值0.2元。當(dāng)價(jià)格下跌到 3.2 - 2*0.2 = 2.8元時(shí),清倉(cāng)。持倉(cāng)成本為 (3+3.1+3.2)*1000/3000 = 3.1元。此時(shí)虧損 (3.1-2.8)*3000 = 900元, 對(duì)于1萬(wàn)來(lái)說(shuō) 這波虧損9%。
原始的海龜交易采用唐奇安通道來(lái)追蹤趨勢(shì),在趨勢(shì)比較明顯的行情表現(xiàn)不錯(cuò),但是在震蕩的行情中效果不佳,當(dāng)然這是所有趨勢(shì)型策略的通病。下面著重使用Python對(duì)唐奇安通道進(jìn)行可視化,并利用簡(jiǎn)化版的海龜交易法則進(jìn)行簡(jiǎn)單的歷史回測(cè)。
3、海龜交易規(guī)則Python實(shí)現(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 #正常顯示畫(huà)圖時(shí)出現(xiàn)的中文和負(fù)號(hào) from pylab import mpl mpl.rcParams['font.sans-serif']=['SimHei'] mpl.rcParams['axes.unicode_minus']=False #使用tushare獲取交易數(shù)據(jù) #設(shè)置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'} #獲取當(dāng)前交易的股票代碼和名稱(chēng) def get_code():df = pro.stock_basic(exchange='', list_status='L')codes=df.ts_code.valuesnames=df.name.valuesstock=dict(zip(names,codes))#合并指數(shù)和個(gè)股成一個(gè)字典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)#否則取的是個(gè)股數(shù)據(jù)else:df=pro.daily(ts_code=code, adj='qfq',start_date=start, end_date=end)#將交易日期設(shè)置為索引值df.index=pd.to_datetime(df.trade_date)df=df.sort_index()#計(jì)算收益率df['ret']=df.close/df.close.shift(1)-1return df下面以滬深300指數(shù)為例,對(duì)唐奇安通道和買(mǎi)賣(mài)突破信號(hào)進(jìn)行可視化。
hs=get_daily_data('滬深300','20180101','')[['close','open','high','low','vol']] #最近N1個(gè)交易日最高價(jià) hs['up']=ta.MAX(hs.high,timeperiod=20).shift(1) #最近N2個(gè)交易日最低價(jià) hs['down']=ta.MIN(hs.low,timeperiod=10).shift(1) #每日真實(shí)波動(dòng)幅度 hs['ATR']=ta.ATR(hs.high,hs.low,hs.close,timeperiod=20) hs.tail()下面使用簡(jiǎn)化版的海龜交易法則進(jìn)行歷史回測(cè),即不考慮倉(cāng)位管理和動(dòng)態(tài)止損/止盈條件,以唐奇安通道突破作為買(mǎi)入賣(mài)出信號(hào)。
交易規(guī)則為:
(1)當(dāng)今天的收盤(pán)價(jià),大于過(guò)去20個(gè)交易日中的最高價(jià)時(shí),以收盤(pán)價(jià)買(mǎi)入;??
(2)買(mǎi)入后,當(dāng)收盤(pán)價(jià)小于過(guò)去10個(gè)交易日中的最低價(jià)時(shí),以收盤(pán)價(jià)賣(mài)出。
def my_strategy(data):x1=data.close>data.upx2=data.close.shift(1)<data.up.shift(1)x=x1&x2y1=data.close<data.downy2=data.close.shift(1)>data.down.shift(1)y=y1&y2data.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) #對(duì)K線圖和唐奇安通道進(jìn)行可視化 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) #添加買(mǎi)賣(mài)信號(hào) 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(注:運(yùn)行上述代碼得到的是動(dòng)態(tài)交互圖,可調(diào)整時(shí)間區(qū)間)
#關(guān)掉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個(gè)交易日最高價(jià)df['H_N1']=ta.MAX(df.high,timeperiod=N1)#最近N2個(gè)交易日最低價(jià)df['L_N2']=ta.MIN(df.low,timeperiod=N2)#當(dāng)日收盤(pán)價(jià)>昨天最近N1個(gè)交易日最高點(diǎn)時(shí)發(fā)出信號(hào)設(shè)置為1buy_index=df[df.close>df['H_N1'].shift(1)].indexdf.loc[buy_index,'收盤(pán)信號(hào)']=1#將當(dāng)日收盤(pán)價(jià)<昨天最近N2個(gè)交易日的最低點(diǎn)時(shí)收盤(pán)信號(hào)設(shè)置為0sell_index=df[df.close<df['L_N2'].shift(1)].indexdf.loc[sell_index,'收盤(pán)信號(hào)']=0df['當(dāng)天倉(cāng)位']=df['收盤(pán)信號(hào)'].shift(1)df['當(dāng)天倉(cāng)位'].fillna(method='ffill',inplace=True)d=df[df['當(dāng)天倉(cāng)位']==1].index[0]-timedelta(days=1)df1=df.loc[d:].copy()df1['ret'][0]=0df1['當(dāng)天倉(cāng)位'][0]=0#當(dāng)倉(cāng)位為1時(shí),買(mǎi)入持倉(cāng),當(dāng)倉(cāng)位為0時(shí),空倉(cāng),計(jì)算資金凈值df1['策略?xún)糁?#39;]=(df1.ret.values*df1['當(dāng)天倉(cāng)位'].values+1.0).cumprod()df1['指數(shù)凈值']=(df1.ret.values+1.0).cumprod()df1['策略收益率']=df1['策略?xún)糁?#39;]/df1['策略?xún)糁?#39;].shift(1)-1df1['指數(shù)收益率']=df1.rettotal_ret=df1[['策略?xún)糁?#39;,'指數(shù)凈值']].iloc[-1]-1annual_ret=pow(1+total_ret,250/len(df1))-1dd=(df1[['策略?xún)糁?#39;,'指數(shù)凈值']].cummax()-df1[['策略?xún)糁?#39;,'指數(shù)凈值']])/df1[['策略?xún)糁?#39;,'指數(shù)凈值']].cummax()d=dd.max()beta=df1[['策略收益率','指數(shù)收益率']].cov().iat[0,1]/df1['指數(shù)收益率'].var()alpha=(annual_ret['策略?xún)糁?#39;]-annual_ret['指數(shù)凈值']*beta)exReturn=df1['策略收益率']-0.03/250sharper_atio=np.sqrt(len(exReturn))*exReturn.mean()/exReturn.std()TA1=round(total_ret['策略?xún)糁?#39;]*100,2)TA2=round(total_ret['指數(shù)凈值']*100,2)AR1=round(annual_ret['策略?xún)糁?#39;]*100,2)AR2=round(annual_ret['指數(shù)凈值']*100,2)MD1=round(d['策略?xún)糁?#39;]*100,2)MD2=round(d['指數(shù)凈值']*100,2)S=round(sharper_atio,2)df1[['策略?xún)糁?#39;,'指數(shù)凈值']].plot(figsize=(15,7))plt.title('海龜交易策略簡(jiǎn)單回測(cè)',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'累計(jì)收益率:\ 策略{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','當(dāng)天倉(cāng)位','策略?xún)糁?#39;,'指數(shù)凈值']]下面對(duì)上證綜指、滬深300、創(chuàng)業(yè)板指數(shù)、中國(guó)平安、東方通信和貴州茅臺(tái)進(jìn)行簡(jiǎn)單回測(cè),看看海龜交易規(guī)則唐奇安的擇時(shí)效果如何,具體指標(biāo)看圖。
strategy('上證綜指','20050101','') strategy('滬深300','','') strategy('創(chuàng)業(yè)板指','','')?
strategy('滬深300','20180101','') strategy('中國(guó)平安','20050101','',N1=20,N2=10) strategy('東方通信','20130101','',N1=20,N2=10) strategy('貴州茅臺(tái)','20050101','',N1=20,N2=10)上述回測(cè)沒(méi)有考慮使用N值的倉(cāng)位管理和動(dòng)態(tài)止損,下面是在萬(wàn)礦平臺(tái)上加入了倉(cāng)位管理進(jìn)行回測(cè),與上面簡(jiǎn)單使用Pandas的回測(cè)框架相比(圖形比較丑陋),貴州茅臺(tái)的各項(xiàng)回測(cè)指標(biāo)看上去更理想了,最大回撤也只有21%。具體實(shí)現(xiàn)代碼可參考萬(wàn)礦平臺(tái)社區(qū)上面的分享。此外,聚寬、優(yōu)礦等量化平臺(tái)上也提供了相應(yīng)了策略回測(cè)模板,實(shí)現(xiàn)代碼大同小異,感興趣的可以進(jìn)一步了解。
4、結(jié)語(yǔ)
本文簡(jiǎn)要介紹了海龜交易法則的基本原理,使用Python對(duì)其買(mǎi)賣(mài)信號(hào)進(jìn)行了可視化分析,并利用Pandas對(duì)相關(guān)指數(shù)和個(gè)股運(yùn)用簡(jiǎn)化版的海龜交易規(guī)則進(jìn)行了歷史回測(cè)。由回測(cè)結(jié)果可看出,該簡(jiǎn)化的趨勢(shì)追蹤策略對(duì)于某些標(biāo)的在某些區(qū)間效果表現(xiàn)不錯(cuò),但對(duì)于某些標(biāo)的或某些時(shí)期則效果不佳。當(dāng)然,本文旨在回顧經(jīng)典策略,展示Pandas在金融量化分析的綜合運(yùn)用,為Python在金融量化中的運(yùn)用起到拋磚引玉的效果,不作出任何選股或策略推薦。值得注意的是,任何策略都具有一定的局限性,尤其是知道和使用該策略的交易者多了,其作用自然比該理念剛出現(xiàn)的的效果差得多。正如技術(shù)分析指標(biāo),剛出現(xiàn)的時(shí)候很有效,但被大家所熟知或應(yīng)用后,自然效用就大打折扣(相對(duì)于多因子模型中的Alpha被大家挖掘后漸漸成了risk factor)。但所謂新理念、新策略一定是站在前人的肩膀上,因此不能因?yàn)榻?jīng)典策略回測(cè)效果不佳而全盤(pán)否定,如何改進(jìn)、細(xì)化和升級(jí),使之更適合當(dāng)下的市場(chǎng)才是我們要面對(duì)的問(wèn)題。
來(lái)源:知識(shí)星球
---------------------
推薦閱讀:
1.一個(gè)量化策略師的自白(好文強(qiáng)烈推薦)
2.股票期貨經(jīng)典的量化交易策略都在這里了!(源碼)
3.期貨/股票數(shù)據(jù)大全查詢(xún)(歷史/實(shí)時(shí)/Tick/財(cái)務(wù)等)
4.三分鐘弄明白為什么貝葉斯是量化工作者最常用的工具
5.學(xué)習(xí)Python有哪些書(shū)籍?這里有一份書(shū)單送給你
6.江湖中常說(shuō)的“網(wǎng)格交易法”到底是什么?
7.10種經(jīng)典的日內(nèi)交易策略模型思路
8.干貨 | 量化選股策略模型大全
9.量化金融經(jīng)典理論、重要模型、發(fā)展簡(jiǎn)史大全
總結(jié)
以上是生活随笔為你收集整理的用Python量化海龟交易法则的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 使用微信+树莓派+Arduino+服务器
- 下一篇: C++(27)——判断数正负