2017-04-04 12:17:38 +00:00
|
|
|
|
# encoding: UTF-8
|
|
|
|
|
|
|
|
|
|
"""
|
2017-04-07 13:46:14 +00:00
|
|
|
|
DualThrust交易策略
|
2017-04-04 12:17:38 +00:00
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
from datetime import time
|
|
|
|
|
|
|
|
|
|
from ctaBase import *
|
|
|
|
|
from ctaTemplate import CtaTemplate
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
########################################################################
|
|
|
|
|
class DualThrustStrategy(CtaTemplate):
|
|
|
|
|
"""DualThrust交易策略"""
|
|
|
|
|
className = 'DualThrustStrategy'
|
|
|
|
|
author = u'用Python的交易员'
|
|
|
|
|
|
|
|
|
|
# 策略参数
|
|
|
|
|
fixedSize = 100
|
|
|
|
|
k1 = 0.4
|
|
|
|
|
k2 = 0.6
|
|
|
|
|
|
|
|
|
|
initDays = 10
|
|
|
|
|
|
|
|
|
|
# 策略变量
|
|
|
|
|
bar = None # K线对象
|
|
|
|
|
barMinute = EMPTY_STRING # K线当前的分钟
|
|
|
|
|
barList = [] # K线对象的列表
|
|
|
|
|
|
|
|
|
|
dayOpen = 0
|
|
|
|
|
dayHigh = 0
|
|
|
|
|
dayLow = 0
|
|
|
|
|
|
|
|
|
|
range = 0
|
|
|
|
|
longEntry = 0
|
|
|
|
|
shortEntry = 0
|
|
|
|
|
exitTime = time(hour=14, minute=55)
|
|
|
|
|
|
|
|
|
|
longEntered = False
|
|
|
|
|
shortEntered = False
|
|
|
|
|
|
|
|
|
|
orderList = [] # 保存委托代码的列表
|
|
|
|
|
|
|
|
|
|
# 参数列表,保存了参数的名称
|
|
|
|
|
paramList = ['name',
|
|
|
|
|
'className',
|
|
|
|
|
'author',
|
|
|
|
|
'vtSymbol',
|
|
|
|
|
'k1',
|
|
|
|
|
'k2']
|
|
|
|
|
|
|
|
|
|
# 变量列表,保存了变量的名称
|
|
|
|
|
varList = ['inited',
|
|
|
|
|
'trading',
|
|
|
|
|
'pos',
|
|
|
|
|
'range',
|
|
|
|
|
'longEntry',
|
|
|
|
|
'shortEntry',
|
|
|
|
|
'exitTime']
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def __init__(self, ctaEngine, setting):
|
|
|
|
|
"""Constructor"""
|
|
|
|
|
super(DualThrustStrategy, self).__init__(ctaEngine, setting)
|
|
|
|
|
|
|
|
|
|
self.barList = []
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def onInit(self):
|
|
|
|
|
"""初始化策略(必须由用户继承实现)"""
|
|
|
|
|
self.writeCtaLog(u'%s策略初始化' %self.name)
|
|
|
|
|
|
|
|
|
|
# 载入历史数据,并采用回放计算的方式初始化策略数值
|
|
|
|
|
initData = self.loadBar(self.initDays)
|
|
|
|
|
for bar in initData:
|
|
|
|
|
self.onBar(bar)
|
|
|
|
|
|
|
|
|
|
self.putEvent()
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def onStart(self):
|
|
|
|
|
"""启动策略(必须由用户继承实现)"""
|
|
|
|
|
self.writeCtaLog(u'%s策略启动' %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: # 否则继续累加新的K线
|
|
|
|
|
bar = self.bar # 写法同样为了加快速度
|
|
|
|
|
|
|
|
|
|
bar.high = max(bar.high, tick.lastPrice)
|
|
|
|
|
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 = []
|
|
|
|
|
|
|
|
|
|
# 计算指标数值
|
|
|
|
|
self.barList.append(bar)
|
|
|
|
|
|
|
|
|
|
if len(self.barList) <= 2:
|
|
|
|
|
return
|
|
|
|
|
else:
|
|
|
|
|
self.barList.pop(0)
|
|
|
|
|
lastBar = self.barList[-2]
|
|
|
|
|
|
|
|
|
|
# 新的一天
|
|
|
|
|
if lastBar.datetime.date() != bar.datetime.date():
|
|
|
|
|
# 如果已经初始化
|
|
|
|
|
if self.dayHigh:
|
|
|
|
|
self.range = self.dayHigh - self.dayLow
|
|
|
|
|
self.longEntry = bar.open + self.k1 * self.range
|
|
|
|
|
self.shortEntry = bar.open - self.k2 * self.range
|
|
|
|
|
|
|
|
|
|
self.dayOpen = bar.open
|
|
|
|
|
self.dayHigh = bar.high
|
|
|
|
|
self.dayLow = bar.low
|
|
|
|
|
|
|
|
|
|
self.longEntered = False
|
|
|
|
|
self.shortEntered = False
|
|
|
|
|
else:
|
|
|
|
|
self.dayHigh = max(self.dayHigh, bar.high)
|
|
|
|
|
self.dayLow = min(self.dayLow, bar.low)
|
|
|
|
|
|
|
|
|
|
# 尚未到收盘
|
|
|
|
|
if not self.range:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
if bar.datetime.time() < self.exitTime:
|
|
|
|
|
if self.pos == 0:
|
|
|
|
|
if bar.close > self.dayOpen:
|
|
|
|
|
if not self.longEntered:
|
|
|
|
|
vtOrderID = self.buy(self.longEntry, self.fixedSize, stop=True)
|
|
|
|
|
self.orderList.append(vtOrderID)
|
|
|
|
|
else:
|
|
|
|
|
if not self.shortEntered:
|
|
|
|
|
vtOrderID = self.short(self.shortEntry, self.fixedSize, stop=True)
|
|
|
|
|
self.orderList.append(vtOrderID)
|
|
|
|
|
|
|
|
|
|
# 持有多头仓位
|
|
|
|
|
elif self.pos > 0:
|
|
|
|
|
self.longEntered = True
|
|
|
|
|
|
|
|
|
|
# 多头止损单
|
|
|
|
|
vtOrderID = self.sell(self.shortEntry, self.fixedSize, stop=True)
|
|
|
|
|
self.orderList.append(vtOrderID)
|
|
|
|
|
|
|
|
|
|
# 空头开仓单
|
|
|
|
|
if not self.shortEntered:
|
|
|
|
|
vtOrderID = self.short(self.shortEntry, self.fixedSize, stop=True)
|
|
|
|
|
self.orderList.append(vtOrderID)
|
|
|
|
|
|
|
|
|
|
# 持有空头仓位
|
|
|
|
|
elif self.pos < 0:
|
|
|
|
|
self.shortEntered = True
|
|
|
|
|
|
|
|
|
|
# 空头止损单
|
|
|
|
|
vtOrderID = self.cover(self.longEntry, self.fixedSize, stop=True)
|
|
|
|
|
self.orderList.append(vtOrderID)
|
|
|
|
|
|
|
|
|
|
# 多头开仓单
|
|
|
|
|
if not self.longEntered:
|
|
|
|
|
vtOrderID = self.buy(self.longEntry, self.fixedSize, stop=True)
|
|
|
|
|
self.orderList.append(vtOrderID)
|
|
|
|
|
|
|
|
|
|
# 收盘平仓
|
|
|
|
|
else:
|
|
|
|
|
if self.pos > 0:
|
|
|
|
|
vtOrderID = self.sell(bar.close * 0.99, abs(self.pos))
|
|
|
|
|
self.orderList.append(vtOrderID)
|
|
|
|
|
elif self.pos < 0:
|
|
|
|
|
vtOrderID = self.cover(bar.close * 1.01, abs(self.pos))
|
|
|
|
|
self.orderList.append(vtOrderID)
|
|
|
|
|
|
|
|
|
|
# 发出状态更新事件
|
|
|
|
|
self.putEvent()
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def onOrder(self, order):
|
|
|
|
|
"""收到委托变化推送(必须由用户继承实现)"""
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def onTrade(self, trade):
|
|
|
|
|
# 发出状态更新事件
|
|
|
|
|
self.putEvent()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|
# 提供直接双击回测的功能
|
|
|
|
|
# 导入PyQt4的包是为了保证matplotlib使用PyQt4而不是PySide,防止初始化出错
|
|
|
|
|
from ctaBacktesting import *
|
|
|
|
|
from PyQt4 import QtCore, QtGui
|
|
|
|
|
|
|
|
|
|
# 创建回测引擎
|
|
|
|
|
engine = BacktestingEngine()
|
|
|
|
|
|
|
|
|
|
# 设置引擎的回测模式为K线
|
|
|
|
|
engine.setBacktestingMode(engine.BAR_MODE)
|
|
|
|
|
|
|
|
|
|
# 设置回测用的数据起始日期
|
|
|
|
|
engine.setStartDate('20120101')
|
|
|
|
|
|
|
|
|
|
# 设置产品相关参数
|
|
|
|
|
engine.setSlippage(0.2) # 股指1跳
|
|
|
|
|
engine.setRate(0.3/10000) # 万0.3
|
|
|
|
|
engine.setSize(300) # 股指合约大小
|
|
|
|
|
engine.setPriceTick(0.2) # 股指最小价格变动
|
|
|
|
|
|
|
|
|
|
# 设置使用的历史数据库
|
|
|
|
|
engine.setDatabase(MINUTE_DB_NAME, 'IF0000')
|
|
|
|
|
|
|
|
|
|
# 在引擎中创建策略对象
|
|
|
|
|
engine.initStrategy(DualThrustStrategy, {})
|
|
|
|
|
|
|
|
|
|
# 开始跑回测
|
|
|
|
|
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)
|