該指標是有Richard Donchian發明的,是有3條不一樣顏色的曲線組成的,該指標用週期(通常都是20)內的最高價和最低價來顯示市場價格的波動性,當其通道窄時表示市場波動較小,反之通道寬則表示市場波動比較大。 如圖所示:react
該具體分析爲: 當價格沖沖破上軌是就是可能的買的信號;反之,衝破下軌時就是可能的賣的信號。 該指標的計算方法爲: 上線=Max(最高低,n) 下線=Min(最低價,n) 中線=(上線+下線)/2
海龜交易就是利用唐奇安通道的價格突破來捕捉趨勢。api
不過咱們在向下突破10日唐奇安下沿賣出。markdown
N值是倉位管理的核心,涉及加倉及止損。另外,N值與技術指標平均真實波幅 ATR很類似app
首先介紹真實波幅: 真實波幅是如下三個值中的最大值函數
一、當前交易日最高價和最低價的波幅 二、前一交易日的收盤價與當前交易日最高價的波幅 三、前一交易日的收盤價與當前交易日最低價的波幅
用公式寫就是:學習
接下來,N值計算公式爲:優化
其中 preN爲前面N值,TrueRange爲當前的真實波幅,此公式的真是含義爲計算以前20天(包括今天在內)的N的平均值
先給出公式:ui
首次建倉的時候,當捕捉到趨勢,即價格突破唐奇安上軌時,買入1個unit。atom
其意義就是,讓一個N值的波動與你總資金1%的波動對應,若是買入1unit單位的資產,當天震幅使得總資產的變化不超過1%。例如:spa
如今你有10萬元資金,1%波動就是1000元。假如標X的N值爲0.2元,1000元÷0.2元=5000股。也就是說,你的第一筆倉位應該是在其突破上軌(假設爲5元)時馬上買入5000股,耗資25000元。
若股價在上一次買入(或加倉)的基礎上上漲了0.5N,則加倉一個Unit。
接上面的例子:假如N值仍爲0.2。 價格來到 5 + 0.2*0.5 = 5.1時,加倉1個Unit,買入5000股,耗資25500元,剩餘資金 49500元 價格來到 5.1 + 0.2*0.5 = 5.2 時再加倉1個unit。買入5000股,耗資26000元,剩餘資金 23500元
當價格比最後一次買入價格下跌2N時,則賣出所有頭寸止損。
接上面的例子,最後一次加倉價格爲5.2。假如此時N值0.2元。 當價格下跌到 5.2 - 2*0.2 = 4.8元時,清倉。 持倉成本爲 (5+5.1+5.2)*5000/15000 = 5.1元。 此時虧損 (5.1-4.8)*15000 = 4500元 對於10萬來講 這波虧損4.5%
當股價跌破10日唐奇安通道下沿,清空頭寸結束本次交易
咱們以單隻股票爲標,創建海龜交易系統,固然,能夠將總資產均分爲n份,同時交易n個標。 計算ATR值用日線數據,監控價格突破採用分鐘線
def initialize(account):
account.last_buy_prcie = 0 #上一次買入價
account.hold_flag = False # 是否持有頭寸標誌
account.limit_unit = 4 # 限制最多買入的單元數
account.unit = 0 # 如今買入1單元的股數
咱們設計個函數,傳入值爲回測中 account.get_history()取得的某單個股票的歷史數據、股票現價、T爲計算唐奇安通道的數據長度,轉化爲dataframe格式
def IN_OR_OUT(data,price,T):
up = max(data['highPrice'].iloc[-T:])
down = min(data['lowPrice'].iloc[-int(T/2):]) # 這裏是10日唐奇安下沿
if price>up:
return 1
elif price<down:
return -1
else:
return 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 int((perValue/ATR)/100)*100
當價格相對上個買入價上漲 0.5ATR時,再買入一個unit 當價格相對上個買入價下跌 2ATR時,清倉
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 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:])
down = min(data['lowPrice'].iloc[-int(T/2):]) # 這裏是10日唐奇安下沿
if price>up:
return 1
elif price<down:
return -1
else:
return 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 int((perValue/ATR)/100)*100
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 SellComplete(hold_flag,security_position):
if len(security_position)>0 and hold_flag==False:
return True
else:
return False
import numpy as np
import pandas as pd
from __future__ import division
from CAL.PyCAL import *
import matplotlib.pyplot as plt
start = '2012-01-01' # 回測起始時間
end = '2016-01-01' # 回測結束時間
benchmark = '000001.XSHE'
universe = ['000001.XSHE']
capital_base = 100000 # 起始資金
freq = 'm' # 策略類型,'d'表示日間策略使用日線回測,'m'表示日內策略使用分鐘線回測
refresh_rate = 1 # 調倉頻率,表示執行handle_data的時間間隔,若freq = 'd'時間間隔的單位爲交易日,若freq = 'm'時間間隔爲分鐘
#----------------------------------- 記錄部分數據 -----------------------------
global record
record = {'break_up':{},'break_down':{},'stop_loss':{},'position':{},'ATR':{}} # 記錄入場、離常、止損點、持倉比、ATR
#---------------------------------------------------------------------------------------
#****************************************** 策略主體 ********************************************
def initialize(account): # 初始化虛擬帳戶狀態
account.last_buy_prcie = 0 #上一次買入價
account.hold_flag = False # 是否持有頭寸標誌
account.limit_unit = 4 # 限制最多買入的單元數
account.unit = 0 # 如今買入1單元的股數
account.add_time = 0 # 買入次數
def handle_data(account): # 每一個交易日的買入賣出指令
T = 20
data = account.get_daily_history(T+1)
stk = universe[0]
data = data[stk]
data = pd.DataFrame(data)
prices = account.reference_price[stk]
today = Date.fromDateTime(account.current_date)
today = today.toISO()
# 0 若是停牌,直接跳過
if np.isnan(prices) or prices == 0: # 停牌或是尚未上市等緣由不能交易
return
# 1 計算ATR
ATR = CalcATR(data)
record['ATR'].update({today:ATR})
# 2 判斷上次賣出是否成功,若不成功,再次賣出
if SellComplete(account.hold_flag,account.security_position):
for stk in account.security_position:
order_to(stk,0)
# 3 判斷加倉或止損
if account.hold_flag==True and len(account.security_position)>0: # 先判斷是否持倉
temp = Add_OR_Stop(prices,account.last_buy_prcie,ATR)
if temp ==1and account.add_time<account.limit_unit: # 判斷加倉
order_num = min(account.unit,account.cash) # 不夠1unit時買入剩下所有
order_to(stk,account.unit)
account.last_buy_prcie = prices
account.add_time += 1
elif temp== -1: # 判斷止損
order_to(stk,0)
initialize(account) # 從新初始化參數 very important here!
record['stop_loss'].update({today:prices})
# 4 判斷入場離場
out = IN_OR_OUT(data,prices,T)
if out ==1 and account.hold_flag==False: #入場
value = account.reference_portfolio_value * 0.01
account.unit = CalcUnit(value,ATR)
order_to(stk,account.unit)
account.add_time = 1
account.hold_flag = True
account.last_buy_prcie = prices
record['break_up'].update({today:prices})
elif out==-1 and account.hold_flag ==True: #離場
order_to(stk,0)
initialize(account) # 從新初始化參數 very important here!
record['break_down'].update({today:prices})
# 5 計算持倉比
ratio = 1 - account.cash/account.reference_portfolio_value
record['position'].update({today:ratio}) # 雖然每分鐘重算,但由於key是日期,最後覆蓋爲當日最終持倉比
return
r = pd.DataFrame(record)
adj_price = DataAPI.MktEqudAdjGet(secID=u"000001.XSHE",ticker=u"",beginDate='20120101',endDate='20160101',isOpen="",field=u"",pandas="1")
adj_price = adj_price.set_index('tradeDate')
把圖畫出來:
紅色點爲入場點; 藍色點爲離場點; 綠色點位止損點
plt.figure(figsize=(20,10))
r['ATR'].plot(label='ATR')
adj_price['closePrice'].plot(label='adj price')
adj_price['highestPrice'].plot(label='high price')
adj_price['lowestPrice'].plot(label='low price')
for i in range(len(r)):
plt.plot(i,r['break_up'].iloc[i],'.r',markersize=13)
plt.plot(i,r['break_down'].iloc[i],'.b',markersize=13)
plt.plot(i,r['stop_loss'].iloc[i],'.g',markersize=13)
plt.legend(loc=0)
能夠發現:
咱們調整下策略:
在計算ATR時,剔除最高最低爲0的部分,再作平均。
import numpy as np
import pandas as pd
from __future__ import division
from CAL.PyCAL import *
import matplotlib.pyplot as plt
start = '2012-01-01' # 回測起始時間
end = '2016-01-01' # 回測結束時間
benchmark = '000001.XSHE'
universe = ['000001.XSHE']
capital_base = 100000 # 起始資金
freq = 'm' # 策略類型,'d'表示日間策略使用日線回測,'m'表示日內策略使用分鐘線回測
refresh_rate = 1 # 調倉頻率,表示執行handle_data的時間間隔,若freq = 'd'時間間隔的單位爲交易日,若freq = 'm'時間間隔爲分鐘
#----------------------------------- 記錄部分數據 -----------------------------
global record
record = {'break_up':{},'break_down':{},'stop_loss':{},'position':{},'ATR':{}} # 記錄入場、離常、止損點、持倉比、ATR
#---------------------------------------------------------------------------------------
#****************************************** 策略主體 ********************************************
def initialize(account): # 初始化虛擬帳戶狀態
account.last_buy_prcie = 0 #上一次買入價
account.hold_flag = False # 是否持有頭寸標誌
account.limit_unit = 4 # 限制最多買入的單元數
account.unit = 0 # 如今買入1單元的股數
account.add_time = 0 # 買入次數
def handle_data(account): # 每一個交易日的買入賣出指令
T = 20
data = account.get_daily_history(T+1)
stk = universe[0]
data = data[stk]
#---------------------- 修改部分 ----------------------
data = pd.DataFrame(data)
data['highPrice'] = data['highPrice'].replace(0,np.nan)
data = data.dropna()
if len(data)<T+1:
delta = T+1 - len(data)
m = T+1+delta
while delta > 0: # 直到取滿20個不爲停牌的數據
m += delta
data = account.get_daily_history(m)
data = data[stk]
data = pd.DataFrame(data)
data['highPrice'] = data['highPrice'].replace(0,np.nan)
data = data.dropna()
delta = T+1 - len(data)
#---------------------------------------------------------
prices = account.reference_price[stk]
today = Date.fromDateTime(account.current_date)
today = today.toISO()
# 0 若是停牌,直接跳過
if np.isnan(prices) or prices == 0: # 停牌或是尚未上市等緣由不能交易
return
# 1 計算ATR
ATR = CalcATR(data)
record['ATR'].update({today:ATR})
# 2 判斷上次賣出是否成功,若不成功,再次賣出
if SellComplete(account.hold_flag,account.security_position):
for stk in account.security_position:
order_to(stk,0)
# 3 判斷加倉或止損
if account.hold_flag==True and len(account.security_position)>0: # 先判斷是否持倉
temp = Add_OR_Stop(prices,account.last_buy_prcie,ATR)
if temp ==1and account.add_time<account.limit_unit: # 判斷加倉
order_num = min(account.unit,account.cash) # 不夠1unit時買入剩下所有
order_to(stk,account.unit)
account.last_buy_prcie = prices
account.add_time += 1
elif temp== -1: # 判斷止損
order_to(stk,0)
initialize(account) # 從新初始化參數 very important here!
record['stop_loss'].update({today:prices})
# 4 判斷入場離場
out = IN_OR_OUT(data,prices,T)
if out ==1 and account.hold_flag==False: #入場
value = account.reference_portfolio_value * 0.01
account.unit = CalcUnit(value,ATR)
order_to(stk,account.unit)
account.add_time = 1
account.hold_flag = True
account.last_buy_prcie = prices
record['break_up'].update({today:prices})
elif out==-1 and account.hold_flag ==True: #離場
order_to(stk,0)
initialize(account) # 從新初始化參數 very important here!
record['break_down'].update({today:prices})
# 5 計算持倉比
ratio = 1 - account.cash/account.reference_portfolio_value
record['position'].update({today:ratio}) # 雖然每分鐘重算,但由於key是日期,最後覆蓋爲當日最終持倉比
return
累計收益相差很少,咱們再來看看記錄的數據。
紅色點爲入場點; 藍色點爲離場點; 綠色點位止損點
r = pd.DataFrame(record)
adj_price = DataAPI.MktEqudAdjGet(secID=u"000001.XSHE",ticker=u"",beginDate='20120101',endDate='20160101',isOpen="",field=u"",pandas="1")
adj_price = adj_price.set_index('tradeDate')
adj_price['highestPrice'] = adj_price['highestPrice'].replace(0,np.nan)
adj_price['lowestPrice'] = adj_price['lowestPrice'].replace(0,np.nan)
plt.figure(figsize=(20,10))
r['ATR'].plot(label='ATR')
adj_price['closePrice'].plot(label='adj price')
adj_price['highestPrice'].plot(label='high price')
adj_price['lowestPrice'].plot(label='low price')
for i in range(len(r)):
plt.plot(i,r['break_up'].iloc[i],'.r',markersize=13)
plt.plot(i,r['break_down'].iloc[i],'.b',markersize=13)
plt.plot(i,r['stop_loss'].iloc[i],'.g',markersize=13)
plt.legend(loc=0)
再看看倉位狀況
r['position'].plot(kind='bar',figsize=(200,5))