该指标是有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))