2015-05-28 05:52:59 +00:00
|
|
|
|
# encoding: UTF-8
|
|
|
|
|
|
|
|
|
|
import shelve
|
2015-10-07 16:27:06 +00:00
|
|
|
|
import MySQLdb
|
2015-05-28 05:52:59 +00:00
|
|
|
|
|
|
|
|
|
from eventEngine import *
|
2015-09-16 07:34:00 +00:00
|
|
|
|
from pymongo import MongoClient as Connection
|
2015-05-28 05:52:59 +00:00
|
|
|
|
from pymongo.errors import *
|
2015-10-07 16:27:06 +00:00
|
|
|
|
from datetime import datetime, timedelta, time
|
2015-05-28 05:52:59 +00:00
|
|
|
|
|
|
|
|
|
from strategyEngine import *
|
2015-10-23 17:12:08 +00:00
|
|
|
|
import sys
|
|
|
|
|
import os
|
|
|
|
|
import cPickle
|
2015-05-28 05:52:59 +00:00
|
|
|
|
|
|
|
|
|
|
2015-10-10 09:08:13 +00:00
|
|
|
|
|
2015-05-28 05:52:59 +00:00
|
|
|
|
########################################################################
|
|
|
|
|
class LimitOrder(object):
|
|
|
|
|
"""限价单对象"""
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def __init__(self, symbol):
|
|
|
|
|
"""Constructor"""
|
2015-10-17 16:23:24 +00:00
|
|
|
|
self.symbol = symbol # 报单合约
|
|
|
|
|
self.price = 0 # 报单价格
|
|
|
|
|
self.volume = 0 # 报单合约数量
|
|
|
|
|
self.direction = None # 方向
|
|
|
|
|
self.offset = None # 开/平
|
2015-05-28 05:52:59 +00:00
|
|
|
|
|
2015-10-14 16:41:45 +00:00
|
|
|
|
#Modified by Incense Lee
|
2015-10-17 16:23:24 +00:00
|
|
|
|
self.orderTime = datetime.now() # 下单时间
|
2015-10-14 16:41:45 +00:00
|
|
|
|
|
2015-05-28 05:52:59 +00:00
|
|
|
|
|
|
|
|
|
########################################################################
|
|
|
|
|
class BacktestingEngine(object):
|
|
|
|
|
"""
|
|
|
|
|
回测引擎,作用:
|
|
|
|
|
1. 从数据库中读取数据并回放
|
|
|
|
|
2. 作为StrategyEngine创建时的参数传入
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def __init__(self):
|
|
|
|
|
"""Constructor"""
|
2015-10-17 16:23:24 +00:00
|
|
|
|
self.eventEngine = EventEngine() # 实例化
|
2015-05-28 05:52:59 +00:00
|
|
|
|
|
|
|
|
|
# 策略引擎
|
2015-10-17 16:23:24 +00:00
|
|
|
|
self.strategyEngine = None # 通过setStrategyEngine进行设置
|
2015-10-14 16:41:45 +00:00
|
|
|
|
|
2015-05-28 05:52:59 +00:00
|
|
|
|
# TICK历史数据列表,由于要使用For循环来实现仿真回放
|
|
|
|
|
# 使用list的速度比Numpy和Pandas都要更快
|
|
|
|
|
self.listDataHistory = []
|
2015-10-14 16:41:45 +00:00
|
|
|
|
|
2015-05-28 05:52:59 +00:00
|
|
|
|
# 限价单字典
|
|
|
|
|
self.dictOrder = {}
|
|
|
|
|
|
|
|
|
|
# 最新的TICK数据
|
|
|
|
|
self.currentData = None
|
|
|
|
|
|
|
|
|
|
# 回测的成交字典
|
|
|
|
|
self.listTrade = []
|
|
|
|
|
|
|
|
|
|
# 报单编号
|
|
|
|
|
self.orderRef = 0
|
|
|
|
|
|
|
|
|
|
# 成交编号
|
|
|
|
|
self.tradeID = 0
|
2015-10-14 16:41:45 +00:00
|
|
|
|
|
|
|
|
|
# 回测编号
|
|
|
|
|
self.Id = datetime.now().strftime('%Y%m%d-%H%M%S')
|
2015-10-24 16:56:43 +00:00
|
|
|
|
|
|
|
|
|
# 回测对象
|
|
|
|
|
self.symbol = ''
|
|
|
|
|
|
|
|
|
|
# 回测开始日期
|
|
|
|
|
self.startDate = None
|
|
|
|
|
|
|
|
|
|
# 回测结束日期
|
|
|
|
|
self.endDate = None
|
|
|
|
|
|
2015-05-28 05:52:59 +00:00
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def setStrategyEngine(self, engine):
|
|
|
|
|
"""设置策略引擎"""
|
|
|
|
|
self.strategyEngine = engine
|
|
|
|
|
self.writeLog(u'策略引擎设置完成')
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def connectMongo(self):
|
|
|
|
|
"""连接MongoDB数据库"""
|
|
|
|
|
try:
|
|
|
|
|
self.__mongoConnection = Connection()
|
|
|
|
|
self.__mongoConnected = True
|
|
|
|
|
self.__mongoTickDB = self.__mongoConnection['TickDB']
|
|
|
|
|
self.writeLog(u'回测引擎连接MongoDB成功')
|
|
|
|
|
except ConnectionFailure:
|
2015-10-07 16:27:06 +00:00
|
|
|
|
self.writeLog(u'回测引擎连接MongoDB失败')
|
|
|
|
|
|
|
|
|
|
|
2015-05-28 05:52:59 +00:00
|
|
|
|
#----------------------------------------------------------------------
|
2015-10-07 16:27:06 +00:00
|
|
|
|
def loadMongoDataHistory(self, symbol, startDate, endDate):
|
|
|
|
|
"""从Mongo载入历史TICK数据"""
|
2015-05-28 05:52:59 +00:00
|
|
|
|
if self.__mongoConnected:
|
|
|
|
|
collection = self.__mongoTickDB[symbol]
|
|
|
|
|
|
|
|
|
|
# 如果输入了读取TICK的最后日期
|
|
|
|
|
if endDate:
|
|
|
|
|
cx = collection.find({'date':{'$gte':startDate, '$lte':endDate}})
|
|
|
|
|
elif startDate:
|
|
|
|
|
cx = collection.find({'date':{'$gte':startDate}})
|
|
|
|
|
else:
|
|
|
|
|
cx = collection.find()
|
|
|
|
|
|
|
|
|
|
# 将TICK数据读入内存
|
|
|
|
|
self.listDataHistory = [data for data in cx]
|
|
|
|
|
|
|
|
|
|
self.writeLog(u'历史TICK数据载入完成')
|
|
|
|
|
else:
|
|
|
|
|
self.writeLog(u'MongoDB未连接,请检查')
|
2015-10-07 16:27:06 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def connectMysql(self):
|
|
|
|
|
"""连接MysqlDB"""
|
|
|
|
|
try:
|
2015-10-24 16:56:43 +00:00
|
|
|
|
self.__mysqlConnection = MySQLdb.connect(host='vnpy.cloudapp.net', user='vnpy',
|
|
|
|
|
passwd='vnpy', db='stockcn', port=3306)
|
2015-10-07 16:27:06 +00:00
|
|
|
|
self.__mysqlConnected = True
|
|
|
|
|
self.writeLog(u'回测引擎连接MysqlDB成功')
|
|
|
|
|
except ConnectionFailure:
|
|
|
|
|
self.writeLog(u'回测引擎连接MysqlDB失败')
|
2015-10-23 17:12:08 +00:00
|
|
|
|
|
2015-10-24 16:56:43 +00:00
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def setDataHistory(self, symbol, startDate, endDate):
|
|
|
|
|
"""设置Tick历史数据的加载要求"""
|
|
|
|
|
self.symbol = symbol
|
|
|
|
|
self.startDate = startDate
|
|
|
|
|
self.endDate = endDate
|
|
|
|
|
|
|
|
|
|
|
2015-10-23 17:12:08 +00:00
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def loadDataHistory(self, symbol, startDate, endDate):
|
2015-10-24 16:56:43 +00:00
|
|
|
|
"""载入历史TICK数据
|
|
|
|
|
如果加载过多数据会导致加载失败,间隔不要超过半年
|
|
|
|
|
"""
|
|
|
|
|
|
2015-10-23 17:12:08 +00:00
|
|
|
|
if not endDate:
|
|
|
|
|
endDate = datetime.today()
|
|
|
|
|
|
|
|
|
|
# 看本地缓存是否存在
|
|
|
|
|
if self.__loadDataHistoryFromLocalCache(symbol, startDate, endDate):
|
|
|
|
|
self.writeLog(u'历史TICK数据从Cache载入')
|
|
|
|
|
return
|
|
|
|
|
|
2015-10-24 00:01:02 +00:00
|
|
|
|
# 每次获取日期周期
|
2015-10-23 17:12:08 +00:00
|
|
|
|
intervalDays = 10
|
|
|
|
|
|
|
|
|
|
for i in range (0,(endDate - startDate).days +1, intervalDays):
|
|
|
|
|
d1 = startDate + timedelta(days = i )
|
|
|
|
|
|
|
|
|
|
if (endDate - d1).days > 10:
|
|
|
|
|
d2 = startDate + timedelta(days = i + intervalDays -1 )
|
|
|
|
|
else:
|
|
|
|
|
d2 = endDate
|
|
|
|
|
|
2015-10-24 00:01:02 +00:00
|
|
|
|
# 从Mysql 提取数据
|
2015-10-23 17:12:08 +00:00
|
|
|
|
self.loadMysqlDataHistory(symbol, d1, d2)
|
|
|
|
|
|
|
|
|
|
self.writeLog(u'历史TICK数据共载入{0}条'.format(len(self.listDataHistory)))
|
2015-10-24 00:01:02 +00:00
|
|
|
|
|
|
|
|
|
# 保存本地cache文件
|
2015-10-23 17:12:08 +00:00
|
|
|
|
self.__saveDataHistoryToLocalCache(symbol, startDate, endDate)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def __loadDataHistoryFromLocalCache(self, symbol, startDate, endDate):
|
|
|
|
|
"""看本地缓存是否存在"""
|
|
|
|
|
|
2015-10-24 00:01:02 +00:00
|
|
|
|
# 运行路径下cache子目录
|
2015-10-24 16:56:43 +00:00
|
|
|
|
cacheFolder = os.getcwd()+'/cache'
|
2015-10-23 17:12:08 +00:00
|
|
|
|
|
2015-10-24 00:01:02 +00:00
|
|
|
|
# cache文件
|
2015-10-24 16:56:43 +00:00
|
|
|
|
cacheFile = u'{0}/{1}_{2}_{3}.pickle'.\
|
2015-10-24 00:01:02 +00:00
|
|
|
|
format(cacheFolder, symbol, startDate.strftime('%Y-%m-%d'), endDate.strftime('%Y-%m-%d'))
|
2015-10-23 17:12:08 +00:00
|
|
|
|
|
|
|
|
|
if not os.path.isfile(cacheFile):
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
else:
|
2015-10-24 00:01:02 +00:00
|
|
|
|
# 从cache文件加载
|
2015-10-23 17:12:08 +00:00
|
|
|
|
cache = open(cacheFile,mode='r')
|
|
|
|
|
self.listDataHistory = cPickle.load(cache)
|
2015-10-24 00:01:02 +00:00
|
|
|
|
cache.close()
|
2015-10-23 17:12:08 +00:00
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def __saveDataHistoryToLocalCache(self, symbol, startDate, endDate):
|
|
|
|
|
"""保存本地缓存"""
|
2015-10-24 00:01:02 +00:00
|
|
|
|
|
|
|
|
|
# 运行路径下cache子目录
|
2015-10-24 16:56:43 +00:00
|
|
|
|
cacheFolder = os.getcwd()+'/cache'
|
2015-10-23 17:12:08 +00:00
|
|
|
|
|
2015-10-24 00:01:02 +00:00
|
|
|
|
# 创建cache子目录
|
2015-10-23 17:12:08 +00:00
|
|
|
|
if not os.path.isdir(cacheFolder):
|
|
|
|
|
os.mkdir(cacheFolder)
|
|
|
|
|
|
2015-10-24 00:01:02 +00:00
|
|
|
|
# cache 文件名
|
2015-10-24 16:56:43 +00:00
|
|
|
|
cacheFile = u'{0}/{1}_{2}_{3}.pickle'.\
|
2015-10-24 00:01:02 +00:00
|
|
|
|
format(cacheFolder, symbol, startDate.strftime('%Y-%m-%d'), endDate.strftime('%Y-%m-%d'))
|
2015-10-23 17:12:08 +00:00
|
|
|
|
|
2015-10-24 00:01:02 +00:00
|
|
|
|
# 重复存在 返回
|
2015-10-23 17:12:08 +00:00
|
|
|
|
if os.path.isfile(cacheFile):
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
else:
|
2015-10-24 00:01:02 +00:00
|
|
|
|
# 写入cache文件
|
|
|
|
|
cache = open(cacheFile, mode='w')
|
2015-10-23 17:12:08 +00:00
|
|
|
|
cPickle.dump(self.listDataHistory,cache)
|
|
|
|
|
cache.close()
|
|
|
|
|
return True
|
|
|
|
|
|
2015-10-07 16:27:06 +00:00
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def loadMysqlDataHistory(self, symbol, startDate, endDate):
|
|
|
|
|
"""从Mysql载入历史TICK数据,"""
|
2015-10-14 16:41:45 +00:00
|
|
|
|
#Todo :判断开始和结束时间,如果间隔天过长,数据量会过大,需要批次提取。
|
2015-10-07 16:27:06 +00:00
|
|
|
|
try:
|
2015-10-24 16:56:43 +00:00
|
|
|
|
self.connectMysql()
|
2015-10-07 16:27:06 +00:00
|
|
|
|
if self.__mysqlConnected:
|
|
|
|
|
|
2015-10-23 17:12:08 +00:00
|
|
|
|
|
2015-10-24 00:01:02 +00:00
|
|
|
|
# 获取指针
|
2015-10-07 16:27:06 +00:00
|
|
|
|
cur = self.__mysqlConnection.cursor(MySQLdb.cursors.DictCursor)
|
|
|
|
|
|
|
|
|
|
if endDate:
|
|
|
|
|
|
2015-10-24 00:01:02 +00:00
|
|
|
|
# 开始日期 ~ 结束日期
|
|
|
|
|
sqlstring = ' select \'{0}\' as InstrumentID, str_to_date(concat(ndate,\' \', ntime),' \
|
2015-10-07 16:27:06 +00:00
|
|
|
|
'\'%Y-%m-%d %H:%i:%s\') as UpdateTime,price as LastPrice,vol as Volume,' \
|
|
|
|
|
'position_vol as OpenInterest,bid1_price as BidPrice1,bid1_vol as BidVolume1, ' \
|
|
|
|
|
'sell1_price as AskPrice1, sell1_vol as AskVolume1 from TB_{0}MI ' \
|
2015-10-24 00:01:02 +00:00
|
|
|
|
'where ndate between cast(\'{1}\' as date) and cast(\'{2}\' as date)'.\
|
|
|
|
|
format(symbol, startDate, endDate)
|
2015-10-07 16:27:06 +00:00
|
|
|
|
|
|
|
|
|
elif startDate:
|
|
|
|
|
|
2015-10-24 00:01:02 +00:00
|
|
|
|
# 开始日期 - 当前
|
|
|
|
|
sqlstring = ' select \'{0}\' as InstrumentID,str_to_date(concat(ndate,\' \', ntime),' \
|
2015-10-07 16:27:06 +00:00
|
|
|
|
'\'%Y-%m-%d %H:%i:%s\') as UpdateTime,price as LastPrice,vol as Volume,' \
|
|
|
|
|
'position_vol as OpenInterest,bid1_price as BidPrice1,bid1_vol as BidVolume1, ' \
|
|
|
|
|
'sell1_price as AskPrice1, sell1_vol as AskVolume1 from TB__{0}MI ' \
|
2015-10-24 00:01:02 +00:00
|
|
|
|
'where ndate > cast(\'{1}\' as date)'.\
|
|
|
|
|
format( symbol, startDate)
|
2015-10-07 16:27:06 +00:00
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
2015-10-24 00:01:02 +00:00
|
|
|
|
# 所有数据
|
|
|
|
|
sqlstring =' select \'{0}\' as InstrumentID,str_to_date(concat(ndate,\' \', ntime),' \
|
2015-10-07 16:27:06 +00:00
|
|
|
|
'\'%Y-%m-%d %H:%i:%s\') as UpdateTime,price as LastPrice,vol as Volume,' \
|
|
|
|
|
'position_vol as OpenInterest,bid1_price as BidPrice1,bid1_vol as BidVolume1, ' \
|
2015-10-24 00:01:02 +00:00
|
|
|
|
'sell1_price as AskPrice1, sell1_vol as AskVolume1 from TB__{0}MI '.\
|
|
|
|
|
format(symbol)
|
2015-10-07 16:27:06 +00:00
|
|
|
|
|
2015-10-24 00:01:02 +00:00
|
|
|
|
# self.writeLog(sqlstring)
|
2015-10-07 16:27:06 +00:00
|
|
|
|
|
2015-10-24 00:01:02 +00:00
|
|
|
|
# 执行查询
|
2015-10-07 16:27:06 +00:00
|
|
|
|
count = cur.execute(sqlstring)
|
2015-10-23 17:12:08 +00:00
|
|
|
|
#self.writeLog(u'历史TICK数据共{0}条'.format(count))
|
2015-10-07 16:27:06 +00:00
|
|
|
|
|
2015-10-24 00:01:02 +00:00
|
|
|
|
# 将TICK数据一次性读入内存
|
2015-10-14 16:41:45 +00:00
|
|
|
|
#self.listDataHistory = cur.fetchall()
|
|
|
|
|
|
2015-10-24 00:01:02 +00:00
|
|
|
|
# 分批次读取
|
2015-10-14 16:41:45 +00:00
|
|
|
|
fetch_counts = 0
|
2015-10-19 08:42:17 +00:00
|
|
|
|
fetch_size = 1000
|
2015-10-14 16:41:45 +00:00
|
|
|
|
|
|
|
|
|
while True:
|
|
|
|
|
results = cur.fetchmany(fetch_size)
|
|
|
|
|
|
|
|
|
|
if not results:
|
|
|
|
|
break
|
|
|
|
|
|
2015-10-23 17:12:08 +00:00
|
|
|
|
fetch_counts = fetch_counts + len(results)
|
2015-10-14 16:41:45 +00:00
|
|
|
|
|
|
|
|
|
if not self.listDataHistory:
|
|
|
|
|
self.listDataHistory =results
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
self.listDataHistory = self.listDataHistory + results
|
|
|
|
|
|
2015-10-23 17:12:08 +00:00
|
|
|
|
self.writeLog(u'{1}~{2}历史TICK数据载入共{0}条'.format(fetch_counts,startDate,endDate))
|
2015-10-14 16:41:45 +00:00
|
|
|
|
|
2015-10-07 16:27:06 +00:00
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
self.writeLog(u'MysqlDB未连接,请检查')
|
2015-10-24 00:01:02 +00:00
|
|
|
|
|
2015-10-07 16:27:06 +00:00
|
|
|
|
except MySQLdb.Error, e:
|
2015-10-24 00:01:02 +00:00
|
|
|
|
|
2015-10-14 16:41:45 +00:00
|
|
|
|
self.writeLog(u'MysqlDB载入数据失败,请检查.Error {0}'.format(e))
|
2015-10-07 16:27:06 +00:00
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def getMysqlDeltaDate(self,symbol, startDate, decreaseDays):
|
2015-10-14 16:41:45 +00:00
|
|
|
|
"""从mysql库中获取交易日前若干天"""
|
2015-10-07 16:27:06 +00:00
|
|
|
|
try:
|
|
|
|
|
if self.__mysqlConnected:
|
|
|
|
|
|
2015-10-24 00:01:02 +00:00
|
|
|
|
# 获取mysql指针
|
2015-10-07 16:27:06 +00:00
|
|
|
|
cur = self.__mysqlConnection.cursor()
|
|
|
|
|
|
|
|
|
|
sqlstring='select distinct ndate from TB_{0}MI where ndate < ' \
|
|
|
|
|
'cast(\'{1}\' as date) order by ndate desc limit {2},1'.format(symbol, startDate, decreaseDays-1)
|
|
|
|
|
|
2015-10-24 00:01:02 +00:00
|
|
|
|
# self.writeLog(sqlstring)
|
2015-10-07 16:27:06 +00:00
|
|
|
|
|
|
|
|
|
count = cur.execute(sqlstring)
|
|
|
|
|
|
|
|
|
|
if count > 0:
|
|
|
|
|
|
2015-10-24 00:01:02 +00:00
|
|
|
|
# 提取第一条记录
|
2015-10-07 16:27:06 +00:00
|
|
|
|
result = cur.fetchone()
|
|
|
|
|
|
|
|
|
|
return result[0]
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
self.writeLog(u'MysqlDB没有查询结果,请检查日期')
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
self.writeLog(u'MysqlDB未连接,请检查')
|
|
|
|
|
|
|
|
|
|
except MySQLdb.Error, e:
|
|
|
|
|
|
2015-10-24 00:01:02 +00:00
|
|
|
|
self.writeLog(u'MysqlDB载入数据失败,请检查.Error {0}: {1}'.format(e.arg[0],e.arg[1]))
|
2015-10-07 16:27:06 +00:00
|
|
|
|
|
2015-10-24 00:01:02 +00:00
|
|
|
|
# 出错后缺省返回
|
|
|
|
|
return startDate-timedelta(days=3)
|
2015-10-07 16:27:06 +00:00
|
|
|
|
|
2015-05-28 05:52:59 +00:00
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def processLimitOrder(self):
|
2015-10-14 16:41:45 +00:00
|
|
|
|
"""
|
|
|
|
|
处理限价单
|
|
|
|
|
为体现准确性,回测引擎需要真实tick数据的买一或卖一价比对。
|
|
|
|
|
"""
|
2015-05-28 05:52:59 +00:00
|
|
|
|
for ref, order in self.dictOrder.items():
|
|
|
|
|
# 如果是买单,且限价大于等于当前TICK的卖一价,则假设成交
|
|
|
|
|
if order.direction == DIRECTION_BUY and \
|
|
|
|
|
order.price >= self.currentData['AskPrice1']:
|
|
|
|
|
self.executeLimitOrder(ref, order, self.currentData['AskPrice1'])
|
|
|
|
|
# 如果是卖单,且限价低于当前TICK的买一价,则假设全部成交
|
|
|
|
|
if order.direction == DIRECTION_SELL and \
|
|
|
|
|
order.price <= self.currentData['BidPrice1']:
|
|
|
|
|
self.executeLimitOrder(ref, order, self.currentData['BidPrice1'])
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def executeLimitOrder(self, ref, order, price):
|
2015-10-14 16:41:45 +00:00
|
|
|
|
"""
|
|
|
|
|
模拟限价单成交处理
|
|
|
|
|
回测引擎模拟成交
|
|
|
|
|
"""
|
2015-05-28 05:52:59 +00:00
|
|
|
|
# 成交回报
|
|
|
|
|
self.tradeID = self.tradeID + 1
|
|
|
|
|
|
|
|
|
|
tradeData = {}
|
|
|
|
|
tradeData['InstrumentID'] = order.symbol
|
|
|
|
|
tradeData['OrderRef'] = ref
|
|
|
|
|
tradeData['TradeID'] = str(self.tradeID)
|
|
|
|
|
tradeData['Direction'] = order.direction
|
|
|
|
|
tradeData['OffsetFlag'] = order.offset
|
|
|
|
|
tradeData['Price'] = price
|
|
|
|
|
tradeData['Volume'] = order.volume
|
2015-10-17 16:23:24 +00:00
|
|
|
|
tradeData['TradeTime'] = order.orderTime
|
2015-10-07 16:27:06 +00:00
|
|
|
|
|
|
|
|
|
print tradeData
|
|
|
|
|
|
2015-05-28 05:52:59 +00:00
|
|
|
|
tradeEvent = Event()
|
|
|
|
|
tradeEvent.dict_['data'] = tradeData
|
|
|
|
|
self.strategyEngine.updateTrade(tradeEvent)
|
|
|
|
|
|
|
|
|
|
# 报单回报
|
|
|
|
|
orderData = {}
|
|
|
|
|
orderData['InstrumentID'] = order.symbol
|
|
|
|
|
orderData['OrderRef'] = ref
|
|
|
|
|
orderData['Direction'] = order.direction
|
|
|
|
|
orderData['CombOffsetFlag'] = order.offset
|
|
|
|
|
orderData['LimitPrice'] = price
|
|
|
|
|
orderData['VolumeTotalOriginal'] = order.volume
|
|
|
|
|
orderData['VolumeTraded'] = order.volume
|
2015-10-17 16:23:24 +00:00
|
|
|
|
orderData['InsertTime'] = order.orderTime
|
2015-05-28 05:52:59 +00:00
|
|
|
|
orderData['CancelTime'] = ''
|
|
|
|
|
orderData['FrontID'] = ''
|
|
|
|
|
orderData['SessionID'] = ''
|
|
|
|
|
orderData['OrderStatus'] = ''
|
|
|
|
|
|
|
|
|
|
orderEvent = Event()
|
|
|
|
|
orderEvent.dict_['data'] = orderData
|
|
|
|
|
self.strategyEngine.updateOrder(orderEvent)
|
2015-10-14 16:41:45 +00:00
|
|
|
|
|
2015-05-28 05:52:59 +00:00
|
|
|
|
# 记录该成交到列表中
|
|
|
|
|
self.listTrade.append(tradeData)
|
2015-10-14 16:41:45 +00:00
|
|
|
|
|
2015-05-28 05:52:59 +00:00
|
|
|
|
# 删除该限价单
|
|
|
|
|
del self.dictOrder[ref]
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def startBacktesting(self):
|
|
|
|
|
"""开始回测"""
|
2015-10-10 09:08:13 +00:00
|
|
|
|
|
2015-10-24 16:56:43 +00:00
|
|
|
|
if not self.startDate:
|
|
|
|
|
self.writeLog(u'回测开始日期未设置。')
|
|
|
|
|
return
|
2015-10-10 09:08:13 +00:00
|
|
|
|
|
2015-10-24 16:56:43 +00:00
|
|
|
|
if not self.endDate:
|
|
|
|
|
self.endDate = datetime.today()
|
|
|
|
|
|
|
|
|
|
if len(self.symbol)<1:
|
|
|
|
|
self.writeLog(u'回测对象未设置。')
|
|
|
|
|
return
|
2015-10-10 09:08:13 +00:00
|
|
|
|
|
2015-10-24 16:56:43 +00:00
|
|
|
|
t1 = datetime.now()
|
2015-10-10 09:08:13 +00:00
|
|
|
|
self.writeLog(u'开始回测,{0}'.format(str(t1 )))
|
|
|
|
|
|
2015-10-24 16:56:43 +00:00
|
|
|
|
# 每次获取日期周期
|
|
|
|
|
intervalDays = 90
|
|
|
|
|
|
|
|
|
|
for i in range (0,(self.endDate - self.startDate).days +1, intervalDays):
|
|
|
|
|
d1 = self.startDate + timedelta(days = i )
|
|
|
|
|
|
|
|
|
|
if (self.endDate - d1).days > 10:
|
|
|
|
|
d2 = self.startDate + timedelta(days = i + intervalDays -1 )
|
|
|
|
|
else:
|
|
|
|
|
d2 = self.endDate
|
|
|
|
|
|
|
|
|
|
# 提取历史数据
|
|
|
|
|
self.loadDataHistory(self.symbol, d1, d2)
|
|
|
|
|
|
|
|
|
|
# 将逐笔数据推送
|
|
|
|
|
for data in self.listDataHistory:
|
2015-10-10 09:08:13 +00:00
|
|
|
|
|
2015-10-24 16:56:43 +00:00
|
|
|
|
# 记录最新的TICK数据
|
|
|
|
|
self.currentData = data
|
2015-10-10 09:08:13 +00:00
|
|
|
|
|
2015-10-24 16:56:43 +00:00
|
|
|
|
# 处理限价单
|
|
|
|
|
self.processLimitOrder()
|
2015-10-14 16:41:45 +00:00
|
|
|
|
|
2015-10-24 16:56:43 +00:00
|
|
|
|
# 推送到策略引擎中
|
|
|
|
|
event = Event()
|
|
|
|
|
event.dict_['data'] = data
|
|
|
|
|
self.strategyEngine.updateMarketData(event)
|
2015-10-14 16:41:45 +00:00
|
|
|
|
|
2015-10-24 16:56:43 +00:00
|
|
|
|
# 清空历史数据
|
|
|
|
|
self.listDataHistory = []
|
2015-10-14 16:41:45 +00:00
|
|
|
|
|
2015-10-23 17:12:08 +00:00
|
|
|
|
# 保存交易到本地结果
|
|
|
|
|
self.saveTradeData()
|
|
|
|
|
|
2015-10-19 08:42:17 +00:00
|
|
|
|
# 保存交易到数据库中
|
2015-10-14 16:41:45 +00:00
|
|
|
|
self.saveTradeDataToMysql()
|
2015-10-10 09:08:13 +00:00
|
|
|
|
|
2015-10-19 08:42:17 +00:00
|
|
|
|
|
2015-10-23 17:12:08 +00:00
|
|
|
|
|
2015-10-10 09:08:13 +00:00
|
|
|
|
t2 = datetime.now()
|
2015-10-19 08:42:17 +00:00
|
|
|
|
|
2015-10-10 09:08:13 +00:00
|
|
|
|
self.writeLog(u'回测结束,{0},耗时:{1}秒'.format(str(t2),(t2-t1).seconds))
|
|
|
|
|
|
2015-10-19 08:42:17 +00:00
|
|
|
|
# 保存策略过程数据到数据库
|
|
|
|
|
self.strategyEngine.saveData(self.Id)
|
|
|
|
|
|
2015-10-10 09:08:13 +00:00
|
|
|
|
|
2015-05-28 05:52:59 +00:00
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
2015-10-14 16:41:45 +00:00
|
|
|
|
def sendOrder(self, instrumentid, exchangeid, price, pricetype, volume, direction, offset, orderTime=datetime.now()):
|
2015-05-28 05:52:59 +00:00
|
|
|
|
"""回测发单"""
|
2015-10-17 16:23:24 +00:00
|
|
|
|
order = LimitOrder(instrumentid) # 限价报单
|
|
|
|
|
order.price = price # 报单价格
|
|
|
|
|
order.direction = direction # 买卖方向
|
|
|
|
|
order.volume = volume # 报单数量
|
2015-05-28 05:52:59 +00:00
|
|
|
|
order.offset = offset
|
2015-10-17 16:23:24 +00:00
|
|
|
|
order.orderTime = orderTime # 报单时间
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.orderRef = self.orderRef + 1 # 报单编号
|
2015-10-14 16:41:45 +00:00
|
|
|
|
|
2015-05-28 05:52:59 +00:00
|
|
|
|
self.dictOrder[str(self.orderRef)] = order
|
2015-10-17 16:23:24 +00:00
|
|
|
|
|
2015-05-28 05:52:59 +00:00
|
|
|
|
return str(self.orderRef)
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def cancelOrder(self, instrumentid, exchangeid, orderref, frontid, sessionid):
|
|
|
|
|
"""回测撤单"""
|
|
|
|
|
try:
|
|
|
|
|
del self.dictOrder[orderref]
|
|
|
|
|
except KeyError:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def writeLog(self, log):
|
|
|
|
|
"""写日志"""
|
|
|
|
|
print log
|
2015-10-07 16:27:06 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def subscribe(self, symbol, exchange):
|
|
|
|
|
"""仿真订阅合约"""
|
|
|
|
|
pass
|
|
|
|
|
|
2015-05-28 05:52:59 +00:00
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def selectInstrument(self, symbol):
|
|
|
|
|
"""读取合约数据"""
|
|
|
|
|
d = {}
|
|
|
|
|
d['ExchangeID'] = 'BackTesting'
|
|
|
|
|
return d
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def saveTradeData(self):
|
|
|
|
|
"""保存交易记录"""
|
2015-10-23 17:12:08 +00:00
|
|
|
|
#f = shelve.open('result.vn')
|
|
|
|
|
#f['listTrade'] = self.listTrade
|
|
|
|
|
#f.close()
|
|
|
|
|
|
|
|
|
|
# 保存本地pickle文件
|
|
|
|
|
resultPath=os.getcwd()+'\\result'
|
|
|
|
|
|
|
|
|
|
if not os.path.isdir(resultPath):
|
|
|
|
|
os.mkdir(resultPath)
|
|
|
|
|
|
|
|
|
|
resultFile = u'{0}\\{1}_Trade.pickle'.format(resultPath, self.Id)
|
|
|
|
|
|
|
|
|
|
cache= open(resultFile, mode='w')
|
|
|
|
|
|
|
|
|
|
cPickle.dump(self.listTrade,cache)
|
|
|
|
|
|
|
|
|
|
cache.close()
|
|
|
|
|
|
2015-05-28 05:52:59 +00:00
|
|
|
|
"""仿真订阅合约"""
|
|
|
|
|
pass
|
2015-10-07 16:27:06 +00:00
|
|
|
|
|
2015-10-14 16:41:45 +00:00
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def saveTradeDataToMysql(self):
|
|
|
|
|
"""保存交易记录到mysql,added by Incense Lee"""
|
2015-10-23 17:12:08 +00:00
|
|
|
|
|
|
|
|
|
self.connectMysql()
|
|
|
|
|
|
2015-10-14 16:41:45 +00:00
|
|
|
|
if self.__mysqlConnected:
|
2015-10-22 02:40:59 +00:00
|
|
|
|
sql='insert into BackTest.TB_Trade (Id,symbol,orderRef,tradeID,direction,offset,price,volume,tradeTime,amount) values '
|
2015-10-14 16:41:45 +00:00
|
|
|
|
values = ''
|
|
|
|
|
|
2015-10-17 16:23:24 +00:00
|
|
|
|
print u'共{0}条交易记录.'.format(len(self.listTrade))
|
2015-10-20 14:54:57 +00:00
|
|
|
|
|
|
|
|
|
if len(self.listTrade) == 0:
|
|
|
|
|
return
|
|
|
|
|
|
2015-10-14 16:41:45 +00:00
|
|
|
|
for tradeItem in self.listTrade:
|
|
|
|
|
|
|
|
|
|
if len(values) > 0:
|
|
|
|
|
values = values + ','
|
|
|
|
|
|
2015-10-20 14:54:57 +00:00
|
|
|
|
if tradeItem['OffsetFlag'] == '0':
|
|
|
|
|
amount = 0-float(tradeItem['Price'])*int(tradeItem['Volume'])
|
|
|
|
|
else:
|
|
|
|
|
amount = float(tradeItem['Price'])*int(tradeItem['Volume'])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
values = values + '(\'{0}\',\'{1}\',{2},{3},{4},{5},{6},{7},\'{8}\',{9})'.format(
|
2015-10-14 16:41:45 +00:00
|
|
|
|
self.Id,
|
|
|
|
|
tradeItem['InstrumentID'],
|
|
|
|
|
tradeItem['OrderRef'],
|
|
|
|
|
tradeItem['TradeID'],
|
|
|
|
|
tradeItem['Direction'],
|
|
|
|
|
tradeItem['OffsetFlag'],
|
|
|
|
|
tradeItem['Price'],
|
2015-10-17 16:23:24 +00:00
|
|
|
|
tradeItem['Volume'],
|
2015-10-20 14:54:57 +00:00
|
|
|
|
tradeItem['TradeTime'].strftime('%Y-%m-%d %H:%M:%S'),amount)
|
2015-10-14 16:41:45 +00:00
|
|
|
|
|
|
|
|
|
cur = self.__mysqlConnection.cursor(MySQLdb.cursors.DictCursor)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
cur.execute(sql+values)
|
|
|
|
|
self.__mysqlConnection.commit()
|
|
|
|
|
except Exception, e:
|
|
|
|
|
print e
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
self.saveTradeData()
|
2015-10-07 16:27:06 +00:00
|
|
|
|
|