#encoding:UTF-8
"""
一个ATR—RSI指标结合的交易策略,适合用在股指的一分钟和五分钟线上;
注意事项:
1.将IF0000_1min.csv用ctaHistoryData.py导入MongoDB,直接运行本文件即可回测策略
"""
from ctaBase import*
from ctaTemplate import CtaTemplate
import talib
import numpy as np
class AtrRsiStrategy(CtaTemplate):
"""结合ATR和RSI指标的一个分钟线交易策略"""
className='AtrRsiStrategy'
author=u'用python的交易员'
#策略参数
atrLength=22 #计算ATR指标的窗口数
atrMaLength=10 #计算ATR均线的窗口数
rsiLength=5 #计算RSI的窗口数
rsiEntry=16 #RSI的开仓信号
trailingPercent=0.8 #百分比移动止损
initDays=10 #初始化数据所用的天数
fixedSize=1 #每次交易的数量
#策略变量
bar=None K线对象
barMinute=EMPTY_STRING #K线当前的分钟
bufferSize=100 #需要缓存的数据的大小
bufferCount=0 #目前已经缓存了的数据的计数
highArray=np.zeros(bufferSize) #K线最高价的数组
lowArray=np.zeros(bufferSize) #K线的最低价的数据
closeArray=np.zeros(bufferSize) #K线收盘价
artCount=0 #目前已经缓存了的ATR的计数
atrArray=np.zeros(bufferSize) #ATR指标的数组
atrValue=0 #最新的ATR指标数值
artMa=0 #ATR移动平均的数值
rsiValue=0 #RSI指标的数值
rsiBuy=0 #RSI买开阈值
rsiSell=0 #RSI卖开阈值
intraTradeHigh=0 #移动止损用的持仓器内最高价
intraTradeLow=0 #移动止损用的持仓器内最低价
orderList=[] #保存委托代码的列表
#参数列表,保存了参数的名称
paramList=['name',
'className',
'author',
'vtSymbol',
'atrLength',
'atrMaLength',
'rsiLength',
'rsiEntry',
'traillingPercent']
#变量列表,保存了变量的名称
varList=['inited',
'trading',
'pos',
'atrValue',
'atrMa',
'rsiValue',
'rsiBuy',
'rsiSell']
def __init__(self,ctaEngine,setting):
"""Constructor"""
super(AtrRsiStrategy,self).__init__(ctaEngine,setting)
#注意策略类中的可变对象属性(通常list和dict等),在策略初始化时需要重新创建,
#否则会出现多个策略实例之间数据共享的情况,有可能导致潜在的策略逻辑错误风险,
#策略类中的这些可变对象属性可以选择不写,全部放在__init__下面,写主要是为了阅读方便
def onInit(self):
"""初始化策略(必须由用户继承实现)"""
self.writeCtaLog(u'$策略初始化'$self.name)
#初始化RSI的入场阈值
self.rsiBuy=50+self.rsiEntry
self.rsiSell=50-self.rsiEntry
#载入历史数据,并采用回放计算的方式初始化策略数值
initData=self.loadBar(self.initDays)
for bar in initData:
self.onBar(bar)
self.putEvent()
def onStart(self):
"""启动策略(必须由用户继承实现)"""
self.writeCtaLog(u'$策略启动'%self.name)
self.putEvent()
def onStop(self):
"""停止策略(必须由用户继承实现)"""
self.writeCtaLog(u'$s策略停止'%self.name)
self.putEvent()
def onTick(self,tick):
"""收到行情TICK推送(必须由用户继承实现)"""
#计算K线
tickMinute=tick.datetime.minute
if tickMinute !=self.barMinute:
if self.bar:
self.onBar(self.bar)
bar=CtaBarData()
bar.vtSymbol=tick.vtSymbol
bar.symbol=tick.symbol
bar.exchange=tick.exchange
bar.open=tick.lastPrice
bar.high=tick.lastPrice
bar.low=tick.lastPrice
bar.close=tick.lastPrice
bar.date=tick.date
bar.time=tick.time
bar.datetime=tick.datetime #K线的时间设为第一个Tick的时间
self.bar=bar #这种写法为了减少一层访问,加快速度
self.barMinute=tickMinute #更新当前的分钟
else:
bar=self.bar #同样为了加快速度
bar.high=max(bar.high,ticklastPrice)
bar.low=min(bar.low,tick.lastPrice)
bar.close=tick.lastPrice
def onBar(self,bar):
"""收到Bar推送(必须由用户继承实现)"""
#撤销之前发出的尚未成交的委托 (包括限价单和停止单)
for orderID in self.orderList:
self.cancelOrder(orderID)
self.orderList=[]
#保存K线数据
self.closeArray[0:bufferSize-1]=self.closeArray[1:self.bufferSize]
self.highArray[0:self.bufferSize-1]=self.highArray[1:self.bufferSize]
self.lowArray[0:self.bufferSize-1]=self.lowArray[1:self.bufferSize]
self.closeArray[-1]=bar.close
self.highArray[-1]=bar.high
self.lowArray[-1]=bar.low
self.bufferCount+=1
if self.bufferCount<self.bufferSize:
return
#计算指标数值
self.atrValue=talib.ATR(self.highArray,
self.lowArray,
self.closeArray,
self.atrLength[-1]
self.atrArray[0:self.bufferSize-1]=self.atrArray[1:self.bufferSize]
self.atrArray[-1]=self.atrValue
self.atrCount+=1
if self.atrCount<self.bufferSize:
return
self.atrMa=talib.Ma(self.atrArray,
self.atrMaLength[-1]
self.rsiValue=talib.RSI(self.closeArray,
self.rsiLength)[-1]
#判断是否要进行交易
#当前无仓位
if self.pos==0:
self.intraTradeHigh=bar.high
self.intraTradeLow=bar.low
#ATR数值上穿其移动平均线,说明行情短期内波动加大
#即处于趋势的概率比较大,适合CTA开仓
if self.atrValue>self.atrMa:
#使用RSI指标的趋势行情是,会在超买超卖区钝化特征,作为开仓信号
if self.rsiValue>self.rsiBuy:
#这里为了保证成交,选择超价5个整数点下单
self.buy(bar.close+5,self.fixedSize)
elif self.rsiValue<self.rsiSell:
self.short(bar.close-5,self.fixedSize)
#持有多头仓位
elif self.pos>0:
#计算多头持有期内的最高价,以及重置最低价
self.intraTradeHigh=max(self.intraTradeHigh,bar.high)
self.intraTradeLow=bar.low
#计算多头移动止损
longStop=self.intraTradeHigh*(1-self.trailingPercent/100)
#发出本地止损委托,并且把委托号记录下来,用于后续撤单
orderID=self.sell(longStop,abs(self.pos),stop=True)
self.orderList.append(orderID)
#持有空头仓位
elif self.pos<0:
self.intraTradeLow=min(self.intraTradeLow,bar.low)
self.intraTradeHigh=bar.high
shortStop=self.intraTradeLow*(1+self.trailingPercent/100)
orderID=self.cover(shortStop,abs(self.pos),stop=True)
self.orderList.append(orderID)
#发出状态更新事件
self.putEvent()
def onOrder(self,order):
"""收到委托变化推送(必须由用户继承实现)"""
pass
def onTrade(self,trade):
#发出状态更新事件
self.putEvent()
if __name__=='__main__':
#提供直接双击回测的功能
#导入PyQt4的包是为了保证matplotlib使用PyQt4而不是Pyside,防止初始化出错
from ctaBactesting import*
from PyQt4 import QtCore,QtGui
#创建回测引擎
engine=BacktestingEngine()
#设置引擎的虎刺儿模式为K线
engine.setBacktestingMode(engine.BAR_MODE)
#设置回测用的数据起始日期
engine.setStartDate('2012010')
#设置产品相关参数
engine.setSlippage(0.2) #股指为1跳
engine.setRate(0.3/10000) #万0.3
engine.setSize(300) #股指合约大小
#设置使用的历史数据库
engine.setDataBase(MINUTE_DB_NAME,'IF0000')
#在引擎中创建策略对象
d={'atrLength':11}
engine.initStrategy(AtrStrategy,d)
#开始回测
engine.runBacktesting()
#显示回测结果
engine.showBacktestingResult()
## 跑优化
#setting = OptimizationSetting() # 新建一个优化任务设置对象
#setting.setOptimizeTarget('capital') # 设置优化排序的目标是策略净盈利
#setting.addParameter('atrLength', 12, 20, 2) # 增加第一个优化参数atrLength,起始11,结束12,步进1
#setting.addParameter('atrMa', 20, 30, 5) # 增加第二个优化参数atrMa,起始20,结束30,步进1
#setting.addParameter('rsiLength', 5) # 增加一个固定数值的参数
## 性能测试环境:I7-3770,主频3.4G, 8核心,内存16G,Windows 7 专业版
## 测试时还跑着一堆其他的程序,性能仅供参考
#import time
#start = time.time()
## 运行单进程优化函数,自动输出结果,耗时:359秒
#engine.runOptimization(AtrRsiStrategy, setting)
## 多进程优化,耗时:89秒
##engine.runParallelOptimization(AtrRsiStrategy, setting)
#print u'耗时:%s' %(time.time()-start)