增加pyo文件过滤,开始CTA策略模块的日线统计开发

This commit is contained in:
vn.py 2017-07-14 09:05:11 +08:00
parent e1ca7de271
commit 096992c5ba
4 changed files with 258 additions and 216 deletions

1
.gitignore vendored
View File

@ -10,6 +10,7 @@ Release/
# Python编译文件 # Python编译文件
*.pyc *.pyc
*.pyo
# WingIDE文件 # WingIDE文件
*.wpr *.wpr

File diff suppressed because one or more lines are too long

View File

@ -11,13 +11,14 @@ from collections import OrderedDict
from itertools import product from itertools import product
import multiprocessing import multiprocessing
import pymongo import pymongo
import copy
from vnpy.trader.vtGlobal import globalSetting from vnpy.trader.vtGlobal import globalSetting
from vnpy.trader.vtObject import VtTickData, VtBarData from vnpy.trader.vtObject import VtTickData, VtBarData
from vnpy.trader.vtConstant import * from vnpy.trader.vtConstant import *
from vnpy.trader.vtGateway import VtOrderData, VtTradeData from vnpy.trader.vtGateway import VtOrderData, VtTradeData
from vnpy.trader.app.ctaStrategy.ctaBase import * from .ctaBase import *
######################################################################## ########################################################################
@ -34,19 +35,15 @@ class BacktestingEngine(object):
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def __init__(self): def __init__(self):
"""Constructor""" """Constructor"""
# 本地停止单编号计数 # 本地停止单
self.stopOrderCount = 0 self.stopOrderCount = 0 # 编号计数stopOrderID = STOPORDERPREFIX + str(stopOrderCount)
# stopOrderID = STOPORDERPREFIX + str(stopOrderCount)
# 本地停止单字典 # 本地停止单字典, key为stopOrderIDvalue为stopOrder对象
# key为stopOrderIDvalue为stopOrder对象
self.stopOrderDict = {} # 停止单撤销后不会从本字典中删除 self.stopOrderDict = {} # 停止单撤销后不会从本字典中删除
self.workingStopOrderDict = {} # 停止单撤销后会从本字典中删除 self.workingStopOrderDict = {} # 停止单撤销后会从本字典中删除
# 引擎类型为回测 self.engineType = ENGINETYPE_BACKTESTING # 引擎类型为回测
self.engineType = ENGINETYPE_BACKTESTING
# 回测相关
self.strategy = None # 回测策略 self.strategy = None # 回测策略
self.mode = self.BAR_MODE # 回测模式默认为K线 self.mode = self.BAR_MODE # 回测模式默认为K线
@ -62,10 +59,7 @@ class BacktestingEngine(object):
self.dbClient = None # 数据库客户端 self.dbClient = None # 数据库客户端
self.dbCursor = None # 数据库指针 self.dbCursor = None # 数据库指针
#self.historyData = [] # 历史数据的列表,回测用
self.initData = [] # 初始化用的数据 self.initData = [] # 初始化用的数据
#self.backtestingData = [] # 回测用的数据
self.dbName = '' # 回测数据库名 self.dbName = '' # 回测数据库名
self.symbol = '' # 回测集合名 self.symbol = '' # 回测集合名
@ -73,9 +67,9 @@ class BacktestingEngine(object):
self.dataEndDate = None # 回测数据结束日期datetime对象 self.dataEndDate = None # 回测数据结束日期datetime对象
self.strategyStartDate = None # 策略启动日期即前面的数据用于初始化datetime对象 self.strategyStartDate = None # 策略启动日期即前面的数据用于初始化datetime对象
self.limitOrderCount = 0 # 限价单编号
self.limitOrderDict = OrderedDict() # 限价单字典 self.limitOrderDict = OrderedDict() # 限价单字典
self.workingLimitOrderDict = OrderedDict() # 活动限价单字典,用于进行撮合用 self.workingLimitOrderDict = OrderedDict() # 活动限价单字典,用于进行撮合用
self.limitOrderCount = 0 # 限价单编号
self.tradeCount = 0 # 成交编号 self.tradeCount = 0 # 成交编号
self.tradeDict = OrderedDict() # 成交字典 self.tradeDict = OrderedDict() # 成交字典
@ -87,6 +81,31 @@ class BacktestingEngine(object):
self.bar = None self.bar = None
self.dt = None # 最新的时间 self.dt = None # 最新的时间
# 日线回测结果计算用
self.dailyResultDict = OrderedDict()
#------------------------------------------------
# 通用功能
#------------------------------------------------
#----------------------------------------------------------------------
def roundToPriceTick(self, price):
"""取整价格到合约最小价格变动"""
if not self.priceTick:
return price
newPrice = round(price/self.priceTick, 0) * self.priceTick
return newPrice
#----------------------------------------------------------------------
def output(self, content):
"""输出内容"""
print str(datetime.now()) + "\t" + content
#------------------------------------------------
# 参数设置相关
#------------------------------------------------
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def setStartDate(self, startDate='20100416', initDays=10): def setStartDate(self, startDate='20100416', initDays=10):
"""设置回测的启动日期""" """设置回测的启动日期"""
@ -120,6 +139,30 @@ class BacktestingEngine(object):
self.dbName = dbName self.dbName = dbName
self.symbol = symbol self.symbol = symbol
#----------------------------------------------------------------------
def setSlippage(self, slippage):
"""设置滑点点数"""
self.slippage = slippage
#----------------------------------------------------------------------
def setSize(self, size):
"""设置合约大小"""
self.size = size
#----------------------------------------------------------------------
def setRate(self, rate):
"""设置佣金比例"""
self.rate = rate
#----------------------------------------------------------------------
def setPriceTick(self, priceTick):
"""设置价格最小变动"""
self.priceTick = priceTick
#------------------------------------------------
# 数据回放相关
#------------------------------------------------
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def loadHistoryData(self): def loadHistoryData(self):
"""载入历史数据""" """载入历史数据"""
@ -196,19 +239,25 @@ class BacktestingEngine(object):
"""新的K线""" """新的K线"""
self.bar = bar self.bar = bar
self.dt = bar.datetime self.dt = bar.datetime
self.crossLimitOrder() # 先撮合限价单 self.crossLimitOrder() # 先撮合限价单
self.crossStopOrder() # 再撮合停止单 self.crossStopOrder() # 再撮合停止单
self.strategy.onBar(bar) # 推送K线到策略中 self.strategy.onBar(bar) # 推送K线到策略中
self.updateDailyClose(bar.datetime, bar.closePrice)
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def newTick(self, tick): def newTick(self, tick):
"""新的Tick""" """新的Tick"""
self.tick = tick self.tick = tick
self.dt = tick.datetime self.dt = tick.datetime
self.crossLimitOrder() self.crossLimitOrder()
self.crossStopOrder() self.crossStopOrder()
self.strategy.onTick(tick) self.strategy.onTick(tick)
self.updateDailyClose(tick.datetime, tick.lastPrice)
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def initStrategy(self, strategyClass, setting=None): def initStrategy(self, strategyClass, setting=None):
""" """
@ -217,96 +266,7 @@ class BacktestingEngine(object):
""" """
self.strategy = strategyClass(self, setting) self.strategy = strategyClass(self, setting)
self.strategy.name = self.strategy.className self.strategy.name = self.strategy.className
#----------------------------------------------------------------------
def sendOrder(self, vtSymbol, orderType, price, volume, strategy):
"""发单"""
self.limitOrderCount += 1
orderID = str(self.limitOrderCount)
order = VtOrderData()
order.vtSymbol = vtSymbol
order.price = self.roundToPriceTick(price)
order.totalVolume = volume
order.orderID = orderID
order.vtOrderID = orderID
order.orderTime = str(self.dt)
# CTA委托类型映射
if orderType == CTAORDER_BUY:
order.direction = DIRECTION_LONG
order.offset = OFFSET_OPEN
elif orderType == CTAORDER_SELL:
order.direction = DIRECTION_SHORT
order.offset = OFFSET_CLOSE
elif orderType == CTAORDER_SHORT:
order.direction = DIRECTION_SHORT
order.offset = OFFSET_OPEN
elif orderType == CTAORDER_COVER:
order.direction = DIRECTION_LONG
order.offset = OFFSET_CLOSE
# 保存到限价单字典中
self.workingLimitOrderDict[orderID] = order
self.limitOrderDict[orderID] = order
return orderID
#----------------------------------------------------------------------
def cancelOrder(self, vtOrderID):
"""撤单"""
if vtOrderID in self.workingLimitOrderDict:
order = self.workingLimitOrderDict[vtOrderID]
order.status = STATUS_CANCELLED
order.cancelTime = str(self.dt)
del self.workingLimitOrderDict[vtOrderID]
#----------------------------------------------------------------------
def sendStopOrder(self, vtSymbol, orderType, price, volume, strategy):
"""发停止单(本地实现)"""
self.stopOrderCount += 1
stopOrderID = STOPORDERPREFIX + str(self.stopOrderCount)
so = StopOrder()
so.vtSymbol = vtSymbol
so.price = self.roundToPriceTick(price)
so.volume = volume
so.strategy = strategy
so.status = STOPORDER_WAITING
so.stopOrderID = stopOrderID
if orderType == CTAORDER_BUY:
so.direction = DIRECTION_LONG
so.offset = OFFSET_OPEN
elif orderType == CTAORDER_SELL:
so.direction = DIRECTION_SHORT
so.offset = OFFSET_CLOSE
elif orderType == CTAORDER_SHORT:
so.direction = DIRECTION_SHORT
so.offset = OFFSET_OPEN
elif orderType == CTAORDER_COVER:
so.direction = DIRECTION_LONG
so.offset = OFFSET_CLOSE
# 保存stopOrder对象到字典中
self.stopOrderDict[stopOrderID] = so
self.workingStopOrderDict[stopOrderID] = so
# 推送停止单初始更新
self.strategy.onStopOrder(so)
return stopOrderID
#----------------------------------------------------------------------
def cancelStopOrder(self, stopOrderID):
"""撤销停止单"""
# 检查停止单是否存在
if stopOrderID in self.workingStopOrderDict:
so = self.workingStopOrderDict[stopOrderID]
so.status = STOPORDER_CANCELLED
del self.workingStopOrderDict[stopOrderID]
self.strategy.onStopOrder(so)
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def crossLimitOrder(self): def crossLimitOrder(self):
"""基于最新数据撮合限价单""" """基于最新数据撮合限价单"""
@ -451,7 +411,105 @@ class BacktestingEngine(object):
self.strategy.onStopOrder(so) self.strategy.onStopOrder(so)
self.strategy.onOrder(order) self.strategy.onOrder(order)
self.strategy.onTrade(trade) self.strategy.onTrade(trade)
#------------------------------------------------
# 策略接口相关
#------------------------------------------------
#----------------------------------------------------------------------
def sendOrder(self, vtSymbol, orderType, price, volume, strategy):
"""发单"""
self.limitOrderCount += 1
orderID = str(self.limitOrderCount)
order = VtOrderData()
order.vtSymbol = vtSymbol
order.price = self.roundToPriceTick(price)
order.totalVolume = volume
order.orderID = orderID
order.vtOrderID = orderID
order.orderTime = str(self.dt)
# CTA委托类型映射
if orderType == CTAORDER_BUY:
order.direction = DIRECTION_LONG
order.offset = OFFSET_OPEN
elif orderType == CTAORDER_SELL:
order.direction = DIRECTION_SHORT
order.offset = OFFSET_CLOSE
elif orderType == CTAORDER_SHORT:
order.direction = DIRECTION_SHORT
order.offset = OFFSET_OPEN
elif orderType == CTAORDER_COVER:
order.direction = DIRECTION_LONG
order.offset = OFFSET_CLOSE
# 保存到限价单字典中
self.workingLimitOrderDict[orderID] = order
self.limitOrderDict[orderID] = order
return orderID
#----------------------------------------------------------------------
def cancelOrder(self, vtOrderID):
"""撤单"""
if vtOrderID in self.workingLimitOrderDict:
order = self.workingLimitOrderDict[vtOrderID]
order.status = STATUS_CANCELLED
order.cancelTime = str(self.dt)
del self.workingLimitOrderDict[vtOrderID]
#----------------------------------------------------------------------
def sendStopOrder(self, vtSymbol, orderType, price, volume, strategy):
"""发停止单(本地实现)"""
self.stopOrderCount += 1
stopOrderID = STOPORDERPREFIX + str(self.stopOrderCount)
so = StopOrder()
so.vtSymbol = vtSymbol
so.price = self.roundToPriceTick(price)
so.volume = volume
so.strategy = strategy
so.status = STOPORDER_WAITING
so.stopOrderID = stopOrderID
if orderType == CTAORDER_BUY:
so.direction = DIRECTION_LONG
so.offset = OFFSET_OPEN
elif orderType == CTAORDER_SELL:
so.direction = DIRECTION_SHORT
so.offset = OFFSET_CLOSE
elif orderType == CTAORDER_SHORT:
so.direction = DIRECTION_SHORT
so.offset = OFFSET_OPEN
elif orderType == CTAORDER_COVER:
so.direction = DIRECTION_LONG
so.offset = OFFSET_CLOSE
# 保存stopOrder对象到字典中
self.stopOrderDict[stopOrderID] = so
self.workingStopOrderDict[stopOrderID] = so
# 推送停止单初始更新
self.strategy.onStopOrder(so)
return stopOrderID
#----------------------------------------------------------------------
def cancelStopOrder(self, stopOrderID):
"""撤销停止单"""
# 检查停止单是否存在
if stopOrderID in self.workingStopOrderDict:
so = self.workingStopOrderDict[stopOrderID]
so.status = STOPORDER_CANCELLED
del self.workingStopOrderDict[stopOrderID]
self.strategy.onStopOrder(so)
#----------------------------------------------------------------------
def putStrategyEvent(self, name):
"""发送策略更新事件,回测中忽略"""
pass
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def insertData(self, dbName, collectionName, data): def insertData(self, dbName, collectionName, data):
"""考虑到回测中不允许向数据库插入数据,防止实盘交易中的一些代码出错""" """考虑到回测中不允许向数据库插入数据,防止实盘交易中的一些代码出错"""
@ -473,10 +531,10 @@ class BacktestingEngine(object):
log = str(self.dt) + ' ' + content log = str(self.dt) + ' ' + content
self.logList.append(log) self.logList.append(log)
#----------------------------------------------------------------------
def output(self, content): #------------------------------------------------
"""输出内容""" # 结果计算相关
print str(datetime.now()) + "\t" + content #------------------------------------------------
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def calculateBacktestingResult(self): def calculateBacktestingResult(self):
@ -495,6 +553,10 @@ class BacktestingEngine(object):
posList = [0] # 每笔成交后的持仓情况 posList = [0] # 每笔成交后的持仓情况
for trade in self.tradeDict.values(): for trade in self.tradeDict.values():
# 复制成交对象,因为下面的开平仓交易配对涉及到对成交数量的修改
# 若不进行复制直接操作则计算完后所有成交的数量会变成0
trade = copy.copy(trade)
# 多头交易 # 多头交易
if trade.direction == DIRECTION_LONG: if trade.direction == DIRECTION_LONG:
# 如果尚无空头交易 # 如果尚无空头交易
@ -727,30 +789,22 @@ class BacktestingEngine(object):
plt.show() plt.show()
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def putStrategyEvent(self, name): def clearBacktestingResult(self):
"""发送策略更新事件,回测中忽略""" """清空之前回测的结果"""
pass # 清空限价单相关
self.limitOrderCount = 0
#---------------------------------------------------------------------- self.limitOrderDict.clear()
def setSlippage(self, slippage): self.workingLimitOrderDict.clear()
"""设置滑点点数"""
self.slippage = slippage
#---------------------------------------------------------------------- # 清空停止单相关
def setSize(self, size): self.stopOrderCount = 0
"""设置合约大小""" self.stopOrderDict.clear()
self.size = size self.workingStopOrderDict.clear()
#---------------------------------------------------------------------- # 清空成交相关
def setRate(self, rate): self.tradeCount = 0
"""设置佣金比例""" self.tradeDict.clear()
self.rate = rate
#----------------------------------------------------------------------
def setPriceTick(self, priceTick):
"""设置价格最小变动"""
self.priceTick = priceTick
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def runOptimization(self, strategyClass, optimizationSetting): def runOptimization(self, strategyClass, optimizationSetting):
"""优化参数""" """优化参数"""
@ -785,23 +839,6 @@ class BacktestingEngine(object):
self.output(u'%s: %s' %(result[0], result[1])) self.output(u'%s: %s' %(result[0], result[1]))
return result return result
#----------------------------------------------------------------------
def clearBacktestingResult(self):
"""清空之前回测的结果"""
# 清空限价单相关
self.limitOrderCount = 0
self.limitOrderDict.clear()
self.workingLimitOrderDict.clear()
# 清空停止单相关
self.stopOrderCount = 0
self.stopOrderDict.clear()
self.workingStopOrderDict.clear()
# 清空成交相关
self.tradeCount = 0
self.tradeDict.clear()
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def runParallelOptimization(self, strategyClass, optimizationSetting): def runParallelOptimization(self, strategyClass, optimizationSetting):
"""并行优化参数""" """并行优化参数"""
@ -833,17 +870,17 @@ class BacktestingEngine(object):
self.output(u'优化结果:') self.output(u'优化结果:')
for result in resultList: for result in resultList:
self.output(u'%s: %s' %(result[0], result[1])) self.output(u'%s: %s' %(result[0], result[1]))
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def roundToPriceTick(self, price): def updateDailyClose(self, dt, price):
"""取整价格到合约最小价格变动""" """更新每日收盘价"""
if not self.priceTick: date = dt.date()
return price
newPrice = round(price/self.priceTick, 0) * self.priceTick
return newPrice
if date not in self.dailyResultDict:
self.dailyResultDict[date] = DailyResult(date, price)
else:
self.dailyResultDict[date].closePrice = price
######################################################################## ########################################################################
class TradingResult(object): class TradingResult(object):
@ -868,6 +905,45 @@ class TradingResult(object):
- self.commission - self.slippage) # 净盈亏 - self.commission - self.slippage) # 净盈亏
########################################################################
class DailyResult(object):
"""每日交易的结果"""
#----------------------------------------------------------------------
def __init__(self, date, closePrice):
"""Constructor"""
self.date = date # 日期
self.closePrice = closePrice # 当日收盘价
self.previousCloase = 0 # 昨日收盘价
self.tradeList = [] # 成交列表
self.openPosition = 0 # 开盘时的持仓
self.closePosition = 0 # 收盘时的持仓
self.tradingPnl = 0 # 交易盈亏
self.positionPnl = 0 # 持仓盈亏
#----------------------------------------------------------------------
def calculatePnl(self):
"""计算盈亏"""
# 持仓部分
self.positionPnl = self.openPosition * (self.closePrice - self.previousCloase)
self.closePosition = self.openPosition
# 交易部分
for trade in self.tradeList:
if trade.direction == DIRECTION_LONG:
posChange = trade.volume
else:
posChange = -trade.volume
self.tradingPnl += posChange * (self.closePrice - trade.price)
self.closePosition += posChange
######################################################################## ########################################################################
class OptimizationSetting(object): class OptimizationSetting(object):

View File

@ -9,14 +9,14 @@ import traceback
import json import json
from vtFunction import getJsonPath from vtFunction import getJsonPath
globalSetting = {} # 全局配置字典
settingFileName = "VT_setting.json" settingFileName = "VT_setting.json"
settingFilePath = getJsonPath(settingFileName, __file__) settingFilePath = getJsonPath(settingFileName, __file__)
globalSetting = {} # 全局配置字典
try: try:
f = file(settingFileName) f = file(settingFilePath)
globalSetting = json.load(f) globalSetting = json.load(f)
except: except:
traceback.print_exc() traceback.print_exc()