增加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编译文件
*.pyc
*.pyo
# WingIDE文件
*.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
import multiprocessing
import pymongo
import copy
from vnpy.trader.vtGlobal import globalSetting
from vnpy.trader.vtObject import VtTickData, VtBarData
from vnpy.trader.vtConstant import *
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):
"""Constructor"""
# 本地停止单编号计数
self.stopOrderCount = 0
# stopOrderID = STOPORDERPREFIX + str(stopOrderCount)
# 本地停止单
self.stopOrderCount = 0 # 编号计数stopOrderID = STOPORDERPREFIX + str(stopOrderCount)
# 本地停止单字典
# key为stopOrderIDvalue为stopOrder对象
# 本地停止单字典, key为stopOrderIDvalue为stopOrder对象
self.stopOrderDict = {} # 停止单撤销后不会从本字典中删除
self.workingStopOrderDict = {} # 停止单撤销后会从本字典中删除
# 引擎类型为回测
self.engineType = ENGINETYPE_BACKTESTING
self.engineType = ENGINETYPE_BACKTESTING # 引擎类型为回测
# 回测相关
self.strategy = None # 回测策略
self.mode = self.BAR_MODE # 回测模式默认为K线
@ -62,10 +59,7 @@ class BacktestingEngine(object):
self.dbClient = None # 数据库客户端
self.dbCursor = None # 数据库指针
#self.historyData = [] # 历史数据的列表,回测用
self.initData = [] # 初始化用的数据
#self.backtestingData = [] # 回测用的数据
self.dbName = '' # 回测数据库名
self.symbol = '' # 回测集合名
@ -73,9 +67,9 @@ class BacktestingEngine(object):
self.dataEndDate = None # 回测数据结束日期datetime对象
self.strategyStartDate = None # 策略启动日期即前面的数据用于初始化datetime对象
self.limitOrderCount = 0 # 限价单编号
self.limitOrderDict = OrderedDict() # 限价单字典
self.workingLimitOrderDict = OrderedDict() # 活动限价单字典,用于进行撮合用
self.limitOrderCount = 0 # 限价单编号
self.tradeCount = 0 # 成交编号
self.tradeDict = OrderedDict() # 成交字典
@ -87,6 +81,31 @@ class BacktestingEngine(object):
self.bar = 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):
"""设置回测的启动日期"""
@ -120,6 +139,30 @@ class BacktestingEngine(object):
self.dbName = dbName
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):
"""载入历史数据"""
@ -196,19 +239,25 @@ class BacktestingEngine(object):
"""新的K线"""
self.bar = bar
self.dt = bar.datetime
self.crossLimitOrder() # 先撮合限价单
self.crossStopOrder() # 再撮合停止单
self.strategy.onBar(bar) # 推送K线到策略中
self.updateDailyClose(bar.datetime, bar.closePrice)
#----------------------------------------------------------------------
def newTick(self, tick):
"""新的Tick"""
self.tick = tick
self.dt = tick.datetime
self.crossLimitOrder()
self.crossStopOrder()
self.strategy.onTick(tick)
self.updateDailyClose(tick.datetime, tick.lastPrice)
#----------------------------------------------------------------------
def initStrategy(self, strategyClass, setting=None):
"""
@ -218,95 +267,6 @@ class BacktestingEngine(object):
self.strategy = strategyClass(self, setting)
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):
"""基于最新数据撮合限价单"""
@ -452,6 +412,104 @@ class BacktestingEngine(object):
self.strategy.onOrder(order)
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):
"""考虑到回测中不允许向数据库插入数据,防止实盘交易中的一些代码出错"""
@ -473,10 +531,10 @@ class BacktestingEngine(object):
log = str(self.dt) + ' ' + content
self.logList.append(log)
#----------------------------------------------------------------------
def output(self, content):
"""输出内容"""
print str(datetime.now()) + "\t" + content
#------------------------------------------------
# 结果计算相关
#------------------------------------------------
#----------------------------------------------------------------------
def calculateBacktestingResult(self):
@ -495,6 +553,10 @@ class BacktestingEngine(object):
posList = [0] # 每笔成交后的持仓情况
for trade in self.tradeDict.values():
# 复制成交对象,因为下面的开平仓交易配对涉及到对成交数量的修改
# 若不进行复制直接操作则计算完后所有成交的数量会变成0
trade = copy.copy(trade)
# 多头交易
if trade.direction == DIRECTION_LONG:
# 如果尚无空头交易
@ -727,29 +789,21 @@ class BacktestingEngine(object):
plt.show()
#----------------------------------------------------------------------
def putStrategyEvent(self, name):
"""发送策略更新事件,回测中忽略"""
pass
def clearBacktestingResult(self):
"""清空之前回测的结果"""
# 清空限价单相关
self.limitOrderCount = 0
self.limitOrderDict.clear()
self.workingLimitOrderDict.clear()
#----------------------------------------------------------------------
def setSlippage(self, slippage):
"""设置滑点点数"""
self.slippage = slippage
# 清空停止单相关
self.stopOrderCount = 0
self.stopOrderDict.clear()
self.workingStopOrderDict.clear()
#----------------------------------------------------------------------
def setSize(self, size):
"""设置合约大小"""
self.size = size
#----------------------------------------------------------------------
def setRate(self, rate):
"""设置佣金比例"""
self.rate = rate
#----------------------------------------------------------------------
def setPriceTick(self, priceTick):
"""设置价格最小变动"""
self.priceTick = priceTick
# 清空成交相关
self.tradeCount = 0
self.tradeDict.clear()
#----------------------------------------------------------------------
def runOptimization(self, strategyClass, optimizationSetting):
@ -785,23 +839,6 @@ class BacktestingEngine(object):
self.output(u'%s: %s' %(result[0], result[1]))
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):
"""并行优化参数"""
@ -835,14 +872,14 @@ class BacktestingEngine(object):
self.output(u'%s: %s' %(result[0], result[1]))
#----------------------------------------------------------------------
def roundToPriceTick(self, price):
"""取整价格到合约最小价格变动"""
if not self.priceTick:
return price
newPrice = round(price/self.priceTick, 0) * self.priceTick
return newPrice
def updateDailyClose(self, dt, price):
"""更新每日收盘价"""
date = dt.date()
if date not in self.dailyResultDict:
self.dailyResultDict[date] = DailyResult(date, price)
else:
self.dailyResultDict[date].closePrice = price
########################################################################
@ -868,6 +905,45 @@ class TradingResult(object):
- 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):

View File

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