demoStrategy

This commit is contained in:
msincenselee 2015-10-19 16:42:17 +08:00
parent 9dd2d7f120
commit 8b5d27f46c
26 changed files with 4875 additions and 33 deletions

View File

@ -1,14 +1,46 @@
use BackTest;
create table TB_Trade
CREATE TABLE `TB_Trade` (
`Id` varchar(255) DEFAULT NULL,
`symbol` varchar(20) DEFAULT NULL,
`orderRef` int(11) DEFAULT '0',
`tradeID` int(11) DEFAULT '0',
`direction` tinyint(4) DEFAULT NULL,
`offset` tinyint(4) DEFAULT NULL,
`price` float DEFAULT NULL,
`volume` int(11) DEFAULT NULL,
`tradeTime` datetime DEFAULT NULL,
`amount` float DEFAULT '0',
`fee` float DEFAULT '0',
`profit` float DEFAULT '0',
`profitRate` float DEFAULT '0'
)
create table TB_Bar
(
Id varchar(255),
symbol varchar(20),
orderRef varchar(20),
tradeID varchar(20),
direction varchar(10),
offset varchar(10),
price float,
volume int
open float,
high float,
low float,
close float,
date date,
time time,
datetime datetime,
volume long,
openInterest long
);
create table TB_Ema
(
Id varchar(255),
symbol varchar(20),
fastEMA float,
slowEMA float,
date date,
time time,
datetime datetime
);
);

View File

@ -69,8 +69,10 @@ class EventEngine:
# 事件处理线程
self.__thread = Thread(target = self.__run)
# 计时器,用于触发计时器事件
# 计时器
self.__timer = QTimer()
# 绑定用于触发计时器事件
self.__timer.timeout.connect(self.__onTimer)
# 这里的__handlers是一个字典用来保存对应的事件调用关系
@ -84,10 +86,15 @@ class EventEngine:
print u'eventEngine.py EventEngine.__run begin.'
# 循环
while self.__active == True:
try:
event = self.__queue.get(block = True, timeout = 1) # 获取事件的阻塞时间设为1秒
# 阻塞模式获取事件的阻塞时间设为1秒
event = self.__queue.get(block = True, timeout = 1)
# 执行事件处理
self.__process(event)
except Empty:
pass

View File

@ -160,7 +160,7 @@ class BacktestingEngine(object):
#self.listDataHistory = cur.fetchall()
fetch_counts = 0
fetch_size = 10000
fetch_size = 1000
while True:
results = cur.fetchmany(fetch_size)
@ -311,12 +311,17 @@ class BacktestingEngine(object):
event.dict_['data'] = data
self.strategyEngine.updateMarketData(event)
#保存到数据库中
# 保存交易到数据库中
self.saveTradeDataToMysql()
t2 = datetime.now()
self.writeLog(u'回测结束,{0},耗时:{1}'.format(str(t2),(t2-t1).seconds))
# 保存策略过程数据到数据库
self.strategyEngine.saveData(self.Id)
#----------------------------------------------------------------------

View File

@ -76,8 +76,8 @@ class SimpleEmaStrategy(StrategyTemplate):
# 属于updateMarketData推送的第一个Tick数据,忽略交易逻辑
self.firstMarketTick = True
self.lineK = [] # K线数据
self.lineEMA = [] # 快速、慢速EMA数据
self.lineBar = [] # K线数据
self.lineEMA = [] # 快速、慢速EMA数据
@ -250,16 +250,18 @@ class SimpleEmaStrategy(StrategyTemplate):
#self.listTime.append(t)
# 保存K线数据
k = Bar()
k.open = o
k.high = h
k.low = l
k.close = c
k.volume = volume
k.date = t.date#
k.datetime = t
bar = Bar()
bar.symbol = self.symbol
bar.open = o
bar.high = h
bar.low = l
bar.close = c
bar.volume = volume
bar.date = t.strftime('%Y-%m-%d')
bar.time = t.strftime('%H:%M:%S')
bar.datetime = t
self.lineK.append(k)
self.lineBar.append(bar)
# 计算EMA
if self.fastEMA:
@ -270,10 +272,11 @@ class SimpleEmaStrategy(StrategyTemplate):
self.slowEMA = c
emaData = EmaData()
emaData.symbol = self.symbol
emaData.fastEMA = self.fastEMA
emaData.slowEMA = self.slowEMA
emaData.date = t.date
emaData.time = t.time
emaData.date = t.strftime('%Y-%m-%d')
emaData.time = t.strftime('%H:%M:%S')
emaData.datetime = t
self.lineEMA.append(emaData)
@ -328,6 +331,17 @@ class SimpleEmaStrategy(StrategyTemplate):
tt = time(int(hh), int(mm), int(ss), microsecond=ms)
return tt
#----------------------------------------------------------------------
def saveData(self, id):
"""保存过程数据"""
# 保存K线
print u'{0}保存K线'.format(self.name)
self.engine.saveBarToMysql(id, self.lineBar)
# 保存快速EMA和慢速EMA
self.engine.saveEmaToMysql(id, self.lineEMA)
#----------------------------------------------------------------------
def print_log(event):
"""打印日志"""

View File

@ -1,6 +1,6 @@
# encoding: UTF-8
from datetime import datetime
from datetime import datetime,timedelta
print u'StragegyEngine.py import datetime.datetime success'
from pymongo import MongoClient as Connection
@ -87,7 +87,7 @@ class Bar(object):
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
#self.vtSymbol = EMPTY_STRING # vt系统代码
self.symbol = EMPTY_STRING # 代码
#self.exchange = EMPTY_STRING # 交易所
@ -111,9 +111,9 @@ class EmaData(object):
def __init__(self):
"""Constructor"""
self.fastEMA = EMPTY_FLOAT # 快速EMA的数值
self.slowEMA = EMPTY_FLOAT # 慢速EMA的数值
self.symbol = EMPTY_STRING # 代码
self.fastEMA = EMPTY_FLOAT # 快速EMA的数值
self.slowEMA = EMPTY_FLOAT # 慢速EMA的数值
self.date = EMPTY_STRING # EMA开始的时间日期
self.time = EMPTY_STRING # 时间
@ -344,9 +344,31 @@ class StrategyEngine(object):
count = cur.execute(sqlstring)
cx = cur.fetchall()
# cx = cur.fetchall()
fetch_counts = 0
print u'历史TICK数据载入完成{1}~{2},共{0}'.format(count,startDate,endDate)
fetch_size = 1000
while True:
results = cur.fetchmany(fetch_size)
if not results:
break
if fetch_counts == 0:
cx = results
else:
cx = cx + results
fetch_counts = fetch_counts+fetch_size
print u'历史TICK数据载入{0}'.format(fetch_counts)
self.writeLog(u'历史TICK数据载入完成{1}~{2},共{0}'.format(count,startDate,endDate))
print u'策略引擎历史TICK数据载入完成{1}~{2},共{0}'.format(count,startDate,endDate)
return cx
else:
@ -386,6 +408,118 @@ class StrategyEngine(object):
td = timedelta(days=3)
return startDate-td;
#----------------------------------------------------------------------
def saveBarToMysql(self,id, barList):
"""
保存K线数据到数据库
id 回测ID
barList 对象为Bar的列表
"""
if self.__mysqlConnected:
sql='insert into BackTest.TB_Bar (Id, symbol ,open ,high ,low ,close ,date ,time ,datetime, volume, openInterest) values '
values = ''
print u'{0}条Bar记录.'.format(len(barList))
steps = 0
for bar in barList:
if len(values) > 0:
values = values + ','
values = values + '(\'{0}\',\'{1}\',{2},{3},{4},{5},\'{6}\',\'{7}\',\'{8}\',{9},{10})'.format(
id,
bar.symbol,
bar.open,
bar.high,
bar.low,
bar.close,
bar.date,
bar.time,
bar.datetime.strftime('%Y-%m-%d %H:%M:%S'),
bar.volume,
bar.openInterest)
if steps > 3600:
cur = self.__mysqlConnection.cursor(MySQLdb.cursors.DictCursor)
steps = 0
values = EMPTY_STRING
try:
cur.execute(sql+values)
self.__mysqlConnection.commit()
except Exception, e:
print e
else:
steps = steps + 1
cur = self.__mysqlConnection.cursor(MySQLdb.cursors.DictCursor)
try:
cur.execute(sql+values)
self.__mysqlConnection.commit()
except Exception, e:
print e
#----------------------------------------------------------------------
def saveEmaToMysql(self, id, emaList):
"""
保存EMA到数据库
id,回测的编号
"""
if self.__mysqlConnected:
sql='insert into BackTest.TB_Ema (Id, symbol ,fastEMA,slowEMA ,date ,time ,datetime) values '
values = ''
print u'{0}条EMA记录.'.format(len(emaList))
steps = 0
for ema in emaList:
if len(values) > 0:
values = values + ','
values = values + '(\'{0}\',\'{1}\',{2},{3},\'{4}\',\'{5}\',\'{6}\')'.format(
id,
ema.symbol,
ema.fastEMA,
ema.slowEMA,
ema.date,
ema.time,
ema.datetime.strftime('%Y-%m-%d %H:%M:%S'))
if steps > 3600:
cur = self.__mysqlConnection.cursor(MySQLdb.cursors.DictCursor)
steps = 0
values = EMPTY_STRING
try:
cur.execute(sql+values)
self.__mysqlConnection.commit()
except Exception, e:
print e
else:
steps = steps + 1
cur = self.__mysqlConnection.cursor(MySQLdb.cursors.DictCursor)
try:
cur.execute(sql+values)
self.__mysqlConnection.commit()
except Exception, e:
print e
#----------------------------------------------------------------------
def updateMarketData(self, event):
@ -732,6 +866,12 @@ class StrategyEngine(object):
for strategy in self.dictStrategy.values():
strategy.stop()
#----------------------------------------------------------------------
def saveData(self,id):
"""保存所有策略的过程数据"""
print(u'保存所有策略的过程数据')
for strategy in self.dictStrategy.values():
strategy.saveData(id)
########################################################################
class StrategyTemplate(object):
@ -780,7 +920,12 @@ class StrategyTemplate(object):
"""
self.trading = True
self.engine.writeLog(self.name + u'开始运行')
#----------------------------------------------------------------------
def saveData(self,Id):
"""保存数据"""
raise NotImplementedError
#----------------------------------------------------------------------
def stop(self):
"""

BIN
vn.trader/ContractData.vt Normal file

Binary file not shown.

View File

@ -0,0 +1,11 @@
{
"brokerID": "2011",
"tdAddress": "tcp://211.144.195.163:54505",
"qryAddress": "tcp://211.144.195.163:54506",
"mdAddress": "tcp://211.144.195.163:54513",
"productInfo": "LTS-Test",
"authCode": "N3EHKP4CYHZGM9VJ",
"tdPassword": "150601",
"mdPassword": "123",
"userID": "020090002037"
}

15
vn.trader/ctaConstant.py Normal file
View File

@ -0,0 +1,15 @@
# encoding: UTF-8
# CTA引擎中涉及到的交易方向类型
CTAORDER_BUY = u'买开'
CTAORDER_SELL = u'卖平'
CTAORDER_SHORT = u'卖开'
CTAORDER_COVER = u'买平'
# 本地停止单状态
STOPORDER_WAITING = u'等待中'
STOPORDER_CANCELLED = u'已撤销'
STOPORDER_TRIGGERED = u'已触发'
# 本地停止单前缀
STOPORDERPREFIX = 'CtaStopOrder.'

445
vn.trader/ctaEngine.py Normal file
View File

@ -0,0 +1,445 @@
# encoding: UTF-8
from datetime import datetime
import json
from collections import OrderedDict
from eventEngine import *
from vtConstant import *
from vtGateway import VtOrderReq, VtCancelOrderReq
from ctaConstant import *
from ctaStrategies import strategyClassDict
########################################################################
class StopOrder(object):
"""本地停止单"""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
self.vtSymbol = EMPTY_STRING
self.orderType = EMPTY_UNICODE
self.price = EMPTY_FLOAT
self.volume = EMPTY_INT
self.strategy = None # 下停止单的策略对象
self.stopOrderID = EMPTY_STRING # 停止单的本地编号
self.status = EMPTY_STRING # 停止单状态
########################################################################
class CtaBarData(object):
"""K线数据"""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
self.vtSymbol = EMPTY_STRING # vt系统代码
self.symbol = EMPTY_STRING # 代码
self.exchange = EMPTY_STRING # 交易所
self.open = EMPTY_FLOAT # OHLC
self.high = EMPTY_FLOAT
self.low = EMPTY_FLOAT
self.close = EMPTY_FLOAT
self.date = EMPTY_STRING # bar开始的时间日期
self.time = EMPTY_STRING # 时间
self.datetime = None # python的datetime时间对象
self.volume = EMPTY_INT # 成交量
self.openInterest = EMPTY_INT # 持仓量
########################################################################
class CtaTickData(object):
"""Tick数据"""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
self.vtSymbol = EMPTY_STRING # vt系统代码
self.symbol = EMPTY_STRING # 合约代码
self.exchange = EMPTY_STRING # 交易所代码
# 成交数据
self.lastPrice = EMPTY_FLOAT # 最新成交价
self.volume = EMPTY_INT # 最新成交量
self.openInterest = EMPTY_INT # 持仓量
self.upperLimit = EMPTY_FLOAT # 涨停价
self.lowerLimit = EMPTY_FLOAT # 跌停价
# tick的时间
self.date = EMPTY_STRING # 日期
self.time = EMPTY_STRING # 时间
self.datetime = None # python的datetime时间对象
# 五档行情
self.bidPrice1 = EMPTY_FLOAT
self.bidPrice2 = EMPTY_FLOAT
self.bidPrice3 = EMPTY_FLOAT
self.bidPrice4 = EMPTY_FLOAT
self.bidPrice5 = EMPTY_FLOAT
self.askPrice1 = EMPTY_FLOAT
self.askPrice2 = EMPTY_FLOAT
self.askPrice3 = EMPTY_FLOAT
self.askPrice4 = EMPTY_FLOAT
self.askPrice5 = EMPTY_FLOAT
self.bidVolume1 = EMPTY_INT
self.bidVolume2 = EMPTY_INT
self.bidVolume3 = EMPTY_INT
self.bidVolume4 = EMPTY_INT
self.bidVolume5 = EMPTY_INT
self.askVolume1 = EMPTY_INT
self.askVolume2 = EMPTY_INT
self.askVolume3 = EMPTY_INT
self.askVolume4 = EMPTY_INT
self.askVolume5 = EMPTY_INT
########################################################################
class CtaEngine(object):
"""CTA策略引擎"""
settingFileName = 'CTA_setting.json'
#----------------------------------------------------------------------
def __init__(self, mainEngine, eventEngine, dataEngine):
"""Constructor"""
self.mainEngine = mainEngine
self.eventEngine = eventEngine
self.dataEngine = dataEngine
# 保存策略对象的字典
# key为策略名称value为策略对象注意策略名称不允许重复
self.strategyDict = {}
# 保存vtSymbol和策略对象映射的字典用于推送tick数据
# 由于可能多个strategy交易同一个vtSymbol因此key为vtSymbol
# value为包含所有相关strategy对象的list
self.tickStrategyDict = {}
# 保存vtOrderID和strategy对象映射的字典用于推送order和trade数据
# key为vtOrderIDvalue为strategy对象
self.orderStrategyDict = {}
# 本地停止单编号计数
self.stopOrderCount = 0
# stopOrderID = STOPORDERPREFIX + str(stopOrderCount)
# 本地停止单字典
# key为stopOrderIDvalue为stopOrder对象
self.stopOrderDict = {} # 停止单撤销后不会从本字典中删除
self.workingStopOrderDict = {} # 停止单撤销后会从本字典中删除
#----------------------------------------------------------------------
def sendOrder(self, vtSymbol, orderType, price, volume, strategy):
"""发单"""
contract = self.dataEngine.getContract(vtSymbol)
req = VtOrderReq()
req.symbol = contract.symbol
req.exchange = contract.exchange
req.price = price
req.volume = volume
# 设计为CTA引擎发出的委托只允许使用限价单
req.priceType = PRICETYPE_LIMITPRICE
# CTA委托类型映射
if orderType == CTAORDER_BUY:
req.direction = DIRECTION_LONG
req.offset = OFFSET_OPEN
elif orderType == CTAORDER_SELL:
req.direction = DIRECTION_SHORT
req.offset = OFFSET_CLOSE
elif orderType == CTAORDER_SHORT:
req.direction = DIRECTION_SHORT
req.offset = OFFSET_OPEN
elif orderType == CTAORDER_COVER:
req.direction = DIRECTION_LONG
req.offset = OFFSET_CLOSE
vtOrderID = self.mainEngine.sendOrder(req) # 发单
self.orderDict[vtOrderID] = strategy # 保存vtOrderID和策略的映射关系
return vtOrderID
#----------------------------------------------------------------------
def cancelOrder(self, vtOrderID):
"""撤单"""
# 查询报单对象
order = self.dataEngine.getOrder(vtOrderID)
# 如果查询成功
if order:
# 检查是否报单还有效,只有有效时才发出撤单指令
orderFinished = (order.status==STATUS_ALLTRADED or order.status==STATUS_CANCELLED)
if not orderFinished:
req = VtCancelOrderReq()
req.symbol = order.symbol
req.exchange = order.exchange
req.frontID = order.frontID
req.sessionID = order.sessionID
req.orderID = order.orderID
self.mainEngine.cancelOrder(req, order.gatewayName)
#----------------------------------------------------------------------
def sendStopOrder(self, vtSymbol, orderType, price, volume, strategy):
"""发停止单(本地实现)"""
self.stopOrderCount += 1
stopOrderID = STOPORDERPREFIX + str(self.stopOrderCount)
so = StopOrder()
so.vtSymbol = vtSymbol
so.orderType = orderType
so.price = price
so.volume = volume
so.strategy = strategy
so.stopOrderID = stopOrderID
so.status = STOPORDER_WAITING
# 保存stopOrder对象到字典中
self.stopOrderDict[stopOrderID] = so
self.workingStopOrderDict[stopOrderID] = so
return stopOrderID
#----------------------------------------------------------------------
def cancelStopOrder(self, stopOrderID):
"""撤销停止单"""
# 检查停止单是否存在
if stopOrderID in self.workingStopOrderDict:
so = self.workingStopOrderDict[stopOrderID]
so.status = STOPORDER_CANCELLED
del self.workingStopOrderDict[stopOrderID]
#----------------------------------------------------------------------
def processStopOrder(self, tick):
"""收到行情后处理本地停止单(检查是否要立即发出)"""
vtSymbol = tick.vtSymbol
# 首先检查是否有策略交易该合约
if vtSymbol in self.tickStrategyDict:
# 遍历等待中的停止单,检查是否会被触发
for so in self.workingStopOrderDict.values():
if so.vtSymbol == vtSymbol:
longTriggered = so.direction==DIRECTION_LONG and tick.lastPrice>=so.price # 多头停止单被触发
shortTriggered = so.direction==DIRECTION_SHORT and tick.lasatPrice<=so.price # 空头停止单被触发
if longTriggered or shortTriggered:
# 买入和卖出分别以涨停跌停价发单(模拟市价单)
if so.direction==DIRECTION_LONG:
price = tick.upperLimit
else:
price = tick.lowerLimit
so.status = STOPORDER_TRIGGERED
self.sendOrder(so.vtSymbol, so.orderType, price, so.volume, so.strategy)
del self.workingStopOrderDict[so.stopOrderID]
#----------------------------------------------------------------------
def procecssTick(self, tick):
"""处理行情推送"""
# 收到tick行情后先处理本地停止单检查是否要立即发出
self.processStopOrder(tick)
# 推送tick到对应的策略对象进行处理
if tick.vtSymbol in self.tickStrategyDict:
# 将vtTickData数据转化为ctaTickData
ctaTick = CtaTickData()
d = ctaTick.__dict__
for key in d.keys():
d[key] = tick.__getattribute__(key)
# 添加datetime字段
ctaTick.datetime = datetime.strptime(' '.join([tick.date, tick.time]), '%Y%m%d %H:%M:%S.%f')
# 逐个推送到策略对象中
l = self.tickStrategyDict[tick.vtSymbol]
for strategy in l:
strategy.onTick(tick)
#----------------------------------------------------------------------
def processOrder(self, order):
"""处理委托推送"""
if order.vtOrderID in self.orderStrategyDict:
strategy = self.orderStrategyDict[order.vtOrderID]
strategy.onOrder(order)
#----------------------------------------------------------------------
def processTrade(self, trade):
"""处理成交推送"""
if trade.vtOrderID in self.orderStrategyDict:
strategy = self.orderStrategyDict[order.vtOrderID]
strategy.onTrade(trade)
#----------------------------------------------------------------------
def registerEvent(self):
"""注册事件监听"""
self.eventEngine.register(EVENT_TICK, self.procecssTick)
self.eventEngine.register(EVENT_ORDER, self.processOrder)
self.eventEngine.register(EVENT_TRADE, self.processTrade)
#----------------------------------------------------------------------
def insertData(self, dbName, collectionName, data):
"""插入数据到数据库这里的data可以是CtaTickData或者CtaBarData"""
self.mainEngine.dbInsert(dbName, collectionName, data.__dict__)
#----------------------------------------------------------------------
def loadBar(self, dbName, collectionName, startDate):
"""从数据库中读取Bar数据startDate是datetime对象"""
d = {'datetime':{'$gte':startDate}}
cursor = self.mainEngine.dbQuery(dbName, collectionName, d)
l = []
for d in cursor:
bar = CtaBarData()
bar.__dict__ = d
l.append(bar)
return l
#----------------------------------------------------------------------
def loadTick(self, dbName, collectionName, startDate):
"""从数据库中读取Tick数据startDate是datetime对象"""
d = {'datetime':{'$gte':startDate}}
cursor = self.mainEngine.dbQuery(dbName, collectionName, d)
l = []
for d in cursor:
tick = CtaTickData()
tick.__dict__ = d
l.append(tick)
return l
#----------------------------------------------------------------------
def getToday(self):
"""获取代表今日的datetime对象"""
today = datetime.today()
today = today.replace(hour=0, minute=0, second=0, microsecond=0)
return today
#----------------------------------------------------------------------
def writeCtaLog(self, content):
"""快速发出CTA模块日志事件"""
log = VtLogData()
log.logContent = content
event = Event(type_=EVENT_CTA_LOG)
event.dict_['data'] = log
self.eventEngine.put(event)
#----------------------------------------------------------------------
def initStrategy(self, name, strategyClass, paramDict=None):
"""初始化策略"""
# 防止策略重名
if name not in self.strategyDict:
self.strategyDict[name] = strategyClass(self, paramDict) # 创建策略对象
else:
self.writeCtaLog(u'存在策略对象重名:' + name)
#---------------------------------------------------------------------
def startStrategy(self, name):
"""启动策略"""
if name in self.strategyDict:
strategy = self.strategyDict[name]
if not strategy.trading:
strategy.trading = True
strategy.start()
else:
self.writeCtaLog(u'策略对象不存在:' + name)
#----------------------------------------------------------------------
def stopStrategy(self, name):
"""停止策略"""
if name in self.strategyDict:
strategy = self.strategyDict[name]
if strategy.trading:
strategy.trading = False
strategy.stop()
# 对该策略发出的所有限价单进行撤单
for vtOrderID, s in self.orderStrategyDict.items():
if s is strategy:
self.cancelOrder(vtOrderID)
# 对该策略发出的所有本地停止单撤单
for stopOrderID, so in self.workingStopOrderDict.items():
if so.strategy is strategy:
self.cancelStopOrder(stopOrderID)
else:
self.writeCtaLog(u'策略对象不存在:' + name)
#----------------------------------------------------------------------
def saveStrategySetting(self):
"""保存引擎中的策略配置"""
with open(self.settingFileName, 'w') as f:
d = {}
for name, strategy in self.strategyDict.items():
setting = {}
setting['strategyClassName'] = strategy.strategyClassName
for param in strategy.paramList:
setting[param] = strategy.__getattribute__(param)
d[name] = setting
jsonD = json.dumps(d, indent=4)
f.write(jsonD)
#----------------------------------------------------------------------
def loadStrategySetting(self):
"""读取引擎中的策略配置"""
with open(self.settingFileName) as f:
d = json.load(f)
for name, setting in d.items():
strategyClassName = setting['strategyClassName']
if strategyClassName in strategyClassDict:
strategyClass = strategyClassDict[strategyClassName]
self.initStrategy(name, strategyClass, setting)
else:
self.writeCtaLog(u'无法找到策略类:' + strategyClassName)
break
#----------------------------------------------------------------------
def getStrategyVarialbe(self, name):
"""获取策略当前的变量字典"""
if name in self.strategyDict:
strategy = self.strategyDict[name]
d = strategy.__dict__
varDict = OrderedDict()
for key in strategy.varList:
if key in d:
varDict[key] = d[key]
return varDict
else:
self.writeCtaLog(u'策略对象不存在:' + name)
return None
#----------------------------------------------------------------------
def getStrategyParameter(self, name):
"""获取策略的参数字典"""
if name in self.strategyDict:
strategy = self.strategyDict[name]
d = strategy.__dict__
varDict = OrderedDict()
for key in strategy.paramList:
if key in d:
varDict[key] = d[key]
return varDict
else:
self.writeCtaLog(u'策略对象不存在:' + name)
return None

View File

@ -0,0 +1,7 @@
# encoding: UTF-8
'''
在本文件中引入所有希望在系统中使用的策略类
'''
strategyClassDict = {}

View File

@ -0,0 +1,164 @@
# encoding: UTF-8
from ctaConstant import *
########################################################################
class CtaStrategyTemplate(object):
"""CTA策略模板"""
# 策略类的名称
strategyClassName = 'Template'
# 参数列表,保存了参数的名称
paramList = ['vtSymbol']
# 变量列表,保存了变量的名称
varList = []
#----------------------------------------------------------------------
def __init__(self, ctaEngine, setting=None):
"""Constructor"""
self.ctaEngine = ctaEngine
self.vtSymbol = EMPTY_STRING # 交易的合约vt系统代码
self.tickDbName = EMPTY_STRING # tick数据库名称
self.barDbName = EMPTY_STRING # bar数据库名称
self.trading = False # 控制是否启动交易
self.init() # 初始化策略
if setting:
self.setParam(setting)
#----------------------------------------------------------------------
def init(self):
"""初始化策略(必须由用户继承实现)"""
raise NotImplementedError
#----------------------------------------------------------------------
def start(self):
"""启动策略(必须由用户继承实现)"""
raise NotImplementedError
#----------------------------------------------------------------------
def stop(self):
"""停止策略(必须由用户继承实现)"""
raise NotImplementedError
#----------------------------------------------------------------------
def onTick(self, tick):
"""收到行情TICK推送必须由用户继承实现"""
raise NotImplementedError
#----------------------------------------------------------------------
def onOrder(self, order):
"""收到委托变化推送(必须由用户继承实现)"""
raise NotImplementedError
#----------------------------------------------------------------------
def onTrade(self, trade):
"""收到成交推送(必须由用户继承实现)"""
raise NotImplementedError
#----------------------------------------------------------------------
def onBar(self, bar):
"""收到Bar推送必须由用户继承实现"""
raise NotImplementedError
#----------------------------------------------------------------------
def buy(self, price, volume, stop=False):
"""买开"""
# 如果stop为True则意味着发本地停止单
if self.trading:
if stop:
orderID = self.ctaEngine.sendStopOrder(self.vtSymbol, CTAORDER_BUY, price, volume, self)
else:
orderID = self.ctaEngine.sendOrder(self.vtSymbol, CTAORDER_BUY, price, volume, self)
return orderID
else:
return None
#----------------------------------------------------------------------
def sell(self, price, volume, stop=False):
"""卖平"""
# 如果stop为True则意味着发本地停止单
if self.trading:
if stop:
orderID = self.ctaEngine.sendStopOrder(self.vtSymbol, CTAORDER_SELL, price, volume, self)
else:
orderID = self.ctaEngine.sendOrder(self.vtSymbol, CTAORDER_SELL, price, volume, self)
return orderID
else:
return None
#----------------------------------------------------------------------
def short(self, price, volume, stop=False):
"""卖开"""
# 如果stop为True则意味着发本地停止单
if self.trading:
if stop:
orderID = self.ctaEngine.sendStopOrder(self.vtSymbol, CTAORDER_SHORT, price, volume, self)
else:
orderID = self.ctaEngine.sendOrder(self.vtSymbol, CTAORDER_SHORT, price, volume, self)
return orderID
else:
return None
#----------------------------------------------------------------------
def cover(self, price, volume, stop=False):
"""买平"""
if self.trading:
# 如果stop为True则意味着发本地停止单
if stop:
orderID = self.ctaEngine.sendStopOrder(self.vtSymbol, CTAORDER_COVER, price, volume, self)
else:
orderID = self.ctaEngine.sendOrder(self.vtSymbol, CTAORDER_COVER, price, volume, self)
return orderID
else:
return None
#----------------------------------------------------------------------
def cancelOrder(self, orderID):
"""撤单"""
if STOPORDERPREFIX in orderID:
self.ctaEngine.cancelStopOrder(orderID)
else:
self.ctaEngine.cancelOrder(orderID)
#----------------------------------------------------------------------
def insertTick(self, tick):
"""向数据库中插入tick数据"""
self.ctaEngine.insertData(self.tickDbName, self.vtSymbol, tick)
#----------------------------------------------------------------------
def insertBar(self, bar):
"""向数据库中插入bar数据"""
self.ctaEngine.insertData(self.barDbName, self.vtSymbol, bar)
#----------------------------------------------------------------------
def loadTick(self, startDate):
"""读取tick数据"""
return self.ctaEngine.loadTick(self.tickDbName, self.vtSymbol, startDate)
#----------------------------------------------------------------------
def loadBar(self, startDate):
"""读取bar数据"""
return self.ctaEngine.loadBar(self.barDbName, self.vtSymbol, startDate)
#----------------------------------------------------------------------
def setParam(self, setting):
"""设置参数"""
d = self.__dict__
for key in self.paramList:
if key in setting:
d[key] = paramDict[key]
#----------------------------------------------------------------------
def getToday(self):
"""查询当前日期"""
return self.ctaEngine.getToday()

2181
vn.trader/ltsDataType.py Normal file

File diff suppressed because it is too large Load Diff

1244
vn.trader/ltsGateway.py Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

4
vn.trader/uiCtaWidget.py Normal file
View File

@ -0,0 +1,4 @@
# encoding: UTF-8
'''CTA模块相关的GUI控制组件'''

276
vn.trader/uiMainWindow.py Normal file
View File

@ -0,0 +1,276 @@
# encoding: UTF-8
import psutil
from uiBasicWidget import *
########################################################################
class MainWindow(QtGui.QMainWindow):
"""主窗口"""
#----------------------------------------------------------------------
def __init__(self, mainEngine, eventEngine, dataEngine):
"""Constructor"""
super(MainWindow, self).__init__()
self.mainEngine = mainEngine
self.eventEngine = eventEngine
self.dataEngine = dataEngine
self.initUi()
#----------------------------------------------------------------------
def initUi(self):
"""初始化界面"""
self.setWindowTitle('VnTrader')
self.initCentral()
self.initMenu()
self.initStatusBar()
#----------------------------------------------------------------------
def initCentral(self):
"""初始化中心区域"""
marketM = MarketMonitor(self.eventEngine)
logM = LogMonitor(self.eventEngine)
errorM = ErrorMonitor(self.eventEngine)
tradeM = TradeMonitor(self.eventEngine)
orderM = OrderMonitor(self.eventEngine, self.mainEngine)
positionM = PositionMonitor(self.eventEngine)
accountM = AccountMonitor(self.eventEngine)
tradingW = TradingWidget(self.mainEngine, self.mainEngine.eventEngine, self.mainEngine.dataEngine)
leftTab = QtGui.QTabWidget()
leftTab.addTab(logM, u'日志')
leftTab.addTab(errorM, u'错误')
leftTab.addTab(accountM, u'账户')
rightTab = QtGui.QTabWidget()
rightTab.addTab(tradeM, u'成交')
rightTab.addTab(orderM, u'委托')
rightTab.addTab(positionM, u'持仓')
hbox = QtGui.QHBoxLayout()
hbox.addWidget(tradingW)
hbox.addWidget(marketM)
grid = QtGui.QGridLayout()
grid.addLayout(hbox, 0, 0, 1, 2)
grid.addWidget(leftTab, 1, 0)
grid.addWidget(rightTab, 1, 1)
central = QtGui.QWidget()
central.setLayout(grid)
self.setCentralWidget(central)
#----------------------------------------------------------------------
def initMenu(self):
"""初始化菜单"""
# 创建操作
connectCtpAction = QtGui.QAction(u'连接CTP', self)
connectCtpAction.triggered.connect(self.connectCtp)
connectLtsAction = QtGui.QAction(u'连接LTS', self)
connectLtsAction.triggered.connect(self.connectLts)
connectWindAction = QtGui.QAction(u'连接Wind', self)
connectWindAction.triggered.connect(self.connectWind)
testAction = QtGui.QAction(u'测试', self)
testAction.triggered.connect(self.testSubscribe)
exitAction = QtGui.QAction(u'退出', self)
exitAction.triggered.connect(self.close)
aboutAction = QtGui.QAction(u'关于', self)
aboutAction.triggered.connect(self.openAbout)
contractAction = QtGui.QAction(u'查询合约', self)
contractAction.triggered.connect(self.openContract)
# 创建菜单
menubar = self.menuBar()
sysMenu = menubar.addMenu(u'系统')
sysMenu.addAction(connectCtpAction)
sysMenu.addAction(connectLtsAction)
sysMenu.addAction(connectWindAction)
sysMenu.addAction(testAction)
sysMenu.addAction(exitAction)
functionMenu = menubar.addMenu(u'功能')
functionMenu.addAction(contractAction)
helpMenu = menubar.addMenu(u'帮助')
helpMenu.addAction(aboutAction)
#----------------------------------------------------------------------
def initStatusBar(self):
"""初始化状态栏"""
self.statusLabel = QtGui.QLabel()
self.statusLabel.setAlignment(QtCore.Qt.AlignLeft)
self.statusBar().addPermanentWidget(self.statusLabel)
self.statusLabel.setText(self.getCpuMemory())
self.sbCount = 0
self.sbTrigger = 10 # 10秒刷新一次
self.eventEngine.register(EVENT_TIMER, self.updateStatusBar)
#----------------------------------------------------------------------
def updateStatusBar(self, event):
"""在状态栏更新CPU和内存信息"""
self.sbCount += 1
if self.sbCount == self.sbTrigger:
self.sbCount = 0
self.statusLabel.setText(self.getCpuMemory())
#----------------------------------------------------------------------
def getCpuMemory(self):
"""获取CPU和内存状态信息"""
cpuPercent = psutil.cpu_percent()
memoryPercent = psutil.virtual_memory().percent
return u'CPU使用率%d%% 内存使用率:%d%%' % (cpuPercent, memoryPercent)
#----------------------------------------------------------------------
def connectCtp(self):
"""连接CTP接口"""
self.mainEngine.connect('CTP')
#----------------------------------------------------------------------
def connectLts(self):
"""连接LTS接口"""
self.mainEngine.connect('LTS')
#----------------------------------------------------------------------
def connectWind(self):
"""连接Wind接口"""
self.mainEngine.connect('Wind')
#----------------------------------------------------------------------
def testSubscribe(self):
"""测试订阅"""
req = VtSubscribeReq()
req.symbol = '600000'
req.exchange = EXCHANGE_SSE
self.mainEngine.subscribe(req, 'Wind')
req = VtSubscribeReq()
req.symbol = '000062'
req.exchange = EXCHANGE_SZSE
self.mainEngine.subscribe(req, 'Wind')
req = VtSubscribeReq()
req.symbol = 'IF1511'
req.exchange = EXCHANGE_CFFEX
self.mainEngine.subscribe(req, 'Wind')
req = VtSubscribeReq()
req.symbol = 'CU1601'
req.exchange = EXCHANGE_SHFE
self.mainEngine.subscribe(req, 'Wind')
req = VtSubscribeReq()
req.symbol = 'C1601'
req.exchange = EXCHANGE_DCE
self.mainEngine.subscribe(req, 'Wind')
req = VtSubscribeReq()
req.symbol = 'SR1601'
req.exchange = EXCHANGE_CZCE
self.mainEngine.subscribe(req, 'Wind')
#----------------------------------------------------------------------
def openAbout(self):
"""打开关于"""
try:
self.aboutW.show()
except AttributeError:
self.aboutW = AboutWidget(self)
self.aboutW.show()
#----------------------------------------------------------------------
def openContract(self):
"""打开合约查询"""
try:
self.contractM.show()
except AttributeError:
self.contractM = ContractMonitor(self.mainEngine.dataEngine)
self.contractM.show()
#----------------------------------------------------------------------
def closeEvent(self, event):
"""关闭事件"""
reply = QtGui.QMessageBox.question(self, u'退出',
u'确认退出?', QtGui.QMessageBox.Yes |
QtGui.QMessageBox.No, QtGui.QMessageBox.No)
if reply == QtGui.QMessageBox.Yes:
self.mainEngine.exit()
event.accept()
else:
event.ignore()
########################################################################
class AboutWidget(QtGui.QDialog):
"""显示关于信息"""
#----------------------------------------------------------------------
def __init__(self, parent=None):
"""Constructor"""
super(AboutWidget, self).__init__(parent)
self.initUi()
#----------------------------------------------------------------------
def initUi(self):
""""""
self.setWindowTitle(u'关于')
text = u"""
VnTrader
更新日期2015/9/29
作者用Python的交易员
LicenseMIT
主页vnpy.org
Githubgithub.com/vnpy/vnpy
QQ交流群262656087
开发环境
操作系统Windows 7 专业版 64
Python发行版Python 2.7.6 (Anaconda 1.9.2 Win-32)
CTPvn.ctp 2015/6/1
图形库PyQt4 4.11.3 Py2.7-x32
事件驱动引擎vn.event
开发环境WingIDE 5.0.6
"""
label = QtGui.QLabel()
label.setText(text)
label.setMinimumWidth(500)
vbox = QtGui.QVBoxLayout()
vbox.addWidget(label)
self.setLayout(vbox)

BIN
vn.trader/vnltsmd.pyd Normal file

Binary file not shown.

BIN
vn.trader/vnltsqry.pyd Normal file

Binary file not shown.

BIN
vn.trader/vnltstd.pyd Normal file

Binary file not shown.

BIN
vn.trader/vnpy.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

57
vn.trader/vtConstant.py Normal file
View File

@ -0,0 +1,57 @@
# encoding: UTF-8
# 默认空值
EMPTY_STRING = ''
EMPTY_UNICODE = u''
EMPTY_INT = 0
EMPTY_FLOAT = 0.0
# 方向常量
DIRECTION_NONE = u'无方向'
DIRECTION_LONG = u''
DIRECTION_SHORT = u''
DIRECTION_UNKNOWN = u'未知'
DIRECTION_NET = u''
# 开平常量
OFFSET_NONE = u'无开平'
OFFSET_OPEN = u'开仓'
OFFSET_CLOSE = u'平仓'
OFFSET_CLOSETODAY = u'平今'
OFFSET_CLOSESYESTERDAY = u'平昨'
OFFSET_UNKNOWN = u'未知'
# 状态常量
STATUS_NOTTRADED = u'未成交'
STATUS_PARTTRADED = u'部分成交'
STATUS_ALLTRADED = u'全部成交'
STATUS_CANCELLED = u'已撤销'
STATUS_UNKNOWN = u'未知'
# 合约类型常量
PRODUCT_EQUITY = u'股票'
PRODUCT_FUTURES = u'期货'
PRODUCT_OPTION = u'期权'
PRODUCT_INDEX = u'指数'
PRODUCT_COMBINATION = u'组合'
PRODUCT_UNKNOWN = u'未知'
# 价格类型常量
PRICETYPE_LIMITPRICE = u'限价'
PRICETYPE_MARKETPRICE = u'市价'
PRICETYPE_FAK = u'FAK'
PRICETYPE_FOK = u'FOK'
# 期权类型
OPTION_CALL = u'看涨期权'
OPTION_PUT = u'看跌期权'
# 交易所类型
EXCHANGE_SSE = u'SSE' # 上交所
EXCHANGE_SZSE = u'SZSE' # 深交所
EXCHANGE_CFFEX = u'CFFEX' # 中金所
EXCHANGE_SHFE = u'SHFE' # 上期所
EXCHANGE_CZCE = u'CZCE' # 郑商所
EXCHANGE_DCE = u'DCE' # 大商所
EXCHANGE_UNKNOWN = 'UNKNOWN'# 未知交易所
EXCHANGE_NONE = '' # 空交易所

26
vn.trader/vtFunction.py Normal file
View File

@ -0,0 +1,26 @@
# encoding: UTF-8
"""
包含一些开放中常用的函数
"""
import decimal
MAX_NUMBER = 1000000000
MAX_DECIMAL = 4
#----------------------------------------------------------------------
def safeUnicode(value):
"""检查接口数据潜在的错误,保证转化为的字符串正确"""
# 检查是数字接近0时会出现的浮点数上限
if type(value) is int or type(value) is float:
if value > MAX_NUMBER:
value = 0
# 检查防止小数点位过多
if type(value) is float:
d = decimal.Decimal(str(value))
if abs(d.as_tuple().exponent) > MAX_DECIMAL:
value = round(value, ndigits=MAX_DECIMAL)
return unicode(value)

26
vn.trader/vtMain.py Normal file
View File

@ -0,0 +1,26 @@
# encoding: UTF-8
import sys
import ctypes
from vtEngine import MainEngine
from uiMainWindow import *
#----------------------------------------------------------------------
def main():
"""主程序入口"""
# 设置底部任务栏图标win7以下请注释掉
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID('vn.py demo')
app = QtGui.QApplication(sys.argv)
app.setWindowIcon(QtGui.QIcon('vnpy.ico'))
app.setFont(BASIC_FONT)
mainEngine = MainEngine()
mainWindow = MainWindow(mainEngine, mainEngine.eventEngine, mainEngine.dataEngine)
mainWindow.showMaximized()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

183
vn.trader/windGateway.py Normal file
View File

@ -0,0 +1,183 @@
# encoding: UTF-8
'''
Wind Python API的gateway接入
'''
from threading import Thread
try:
from WindPy import w
except ImportError:
print u'请先安装WindPy接口'
from vtGateway import *
# 交易所类型映射
exchangeMap = {}
exchangeMap[EXCHANGE_SSE] = 'SH'
exchangeMap[EXCHANGE_SZSE] = 'SZ'
exchangeMap[EXCHANGE_CFFEX] = 'CFE'
exchangeMap[EXCHANGE_SHFE] = 'SHF'
exchangeMap[EXCHANGE_DCE] = 'DCE'
exchangeMap[EXCHANGE_CZCE] = 'CZC'
exchangeMap[EXCHANGE_UNKNOWN] = ''
exchangeMapReverse = {v:k for k,v in exchangeMap.items()}
########################################################################
class WindGateway(VtGateway):
"""Wind接口"""
# 订阅wsq时传入的字段列表
wsqParamMap = {}
wsqParamMap['rt_last'] = 'lastPrice'
wsqParamMap['rt_last_vol'] = 'volume'
wsqParamMap['rt_oi'] = 'openInterest'
wsqParamMap['rt_open'] = 'openPrice'
wsqParamMap['rt_high'] = 'highPrice'
wsqParamMap['rt_low'] = 'lowPrice'
wsqParamMap['rt_pre_close'] = 'preClosePrice'
wsqParamMap['rt_high_limit'] = 'upperLimit'
wsqParamMap['rt_low_limit'] = 'lowerLimit'
wsqParamMap['rt_bid1'] = 'bidPrice1'
wsqParamMap['rt_bid2'] = 'bidPrice2'
wsqParamMap['rt_bid3'] = 'bidPrice3'
wsqParamMap['rt_bid4'] = 'bidPrice4'
wsqParamMap['rt_bid5'] = 'bidPrice5'
wsqParamMap['rt_ask1'] = 'askPrice1'
wsqParamMap['rt_ask2'] = 'askPrice2'
wsqParamMap['rt_ask3'] = 'askPrice3'
wsqParamMap['rt_ask4'] = 'askPrice4'
wsqParamMap['rt_ask5'] = 'askPrice5'
wsqParamMap['rt_bsize1'] = 'bidVolume1'
wsqParamMap['rt_bsize2'] = 'bidVolume2'
wsqParamMap['rt_bsize3'] = 'bidVolume3'
wsqParamMap['rt_bsize4'] = 'bidVolume4'
wsqParamMap['rt_bsize5'] = 'bidVolume5'
wsqParamMap['rt_asize1'] = 'askVolume1'
wsqParamMap['rt_asize2'] = 'askVolume2'
wsqParamMap['rt_asize3'] = 'askVolume3'
wsqParamMap['rt_asize4'] = 'askVolume4'
wsqParamMap['rt_asize5'] = 'askVolume5'
wsqParam = ','.join(wsqParamMap.keys())
#----------------------------------------------------------------------
def __init__(self, eventEngine, gatewayName='Wind'):
"""Constructor"""
super(WindGateway, self).__init__(eventEngine, gatewayName)
self.w = w # Wind API对象
self.connected = False # 连接状态
# Wind的wsq更新采用的是增量更新模式每次推送只会更新发生变化的字段
# 而vt中的tick是完整更新因此需要本地维护一个所有字段的快照
self.tickDict = {}
self.registerEvent()
#----------------------------------------------------------------------
def connect(self):
"""连接"""
# 由于w.start方法会阻塞较长时间
# 因此设计为异步模式,交给事件处理线程去处理
# 另外w.start和WingIDE的debug模块有冲突会导致异常退出
event = Event(type_=EVENT_WIND_CONNECTREQ)
self.eventEngine.put(event)
#----------------------------------------------------------------------
def subscribe(self, subscribeReq):
"""订阅行情"""
windSymbol = '.'.join([subscribeReq.symbol, exchangeMap[subscribeReq.exchange]])
data = self.w.wsq(windSymbol, self.wsqParam, func=self.wsqCallBack)
#----------------------------------------------------------------------
def sendOrder(self, orderReq):
"""发单"""
log = VtLogData()
log.gatewayName = self.gatewayName
log.logContent = u'Wind接口未实现发单功能'
self.onLog(log)
#----------------------------------------------------------------------
def cancelOrder(self, cancelOrderReq):
"""撤单"""
log = VtLogData()
log.gatewayName = self.gatewayName
log.logContent = u'Wind接口未实现撤单功能'
self.onLog(log)
#----------------------------------------------------------------------
def getAccount(self):
"""查询账户资金"""
log = VtLogData()
log.gatewayName = self.gatewayName
log.logContent = u'Wind接口未实现查询账户功能'
self.onLog(log)
#----------------------------------------------------------------------
def getPosition(self):
"""查询持仓"""
log = VtLogData()
log.gatewayName = self.gatewayName
log.logContent = u'Wind接口未实现查询持仓功能'
self.onLog(log)
#----------------------------------------------------------------------
def close(self):
self.w.stop()
#----------------------------------------------------------------------
def registerEvent(self):
"""注册事件监听"""
self.eventEngine.register(EVENT_WIND_CONNECTREQ, self.wConnect)
#----------------------------------------------------------------------
def wsqCallBack(self, data):
"""收到wsq推送"""
windSymbol = data.Codes[0]
if windSymbol in self.tickDict:
tick = self.tickDict[windSymbol]
else:
tick = VtTickData()
tick.gatewayName = self.gatewayName
symbolSplit = windSymbol.split('.')
tick.symbol = symbolSplit[0]
tick.exchange = exchangeMapReverse[symbolSplit[1]]
tick.vtSymbol = '.'.join([tick.symbol, tick.exchange])
self.tickDict[windSymbol] = tick
dt = data.Times[0]
tick.time = dt.strftime('%H:%M:%S')
tick.date = dt.strftime('%Y%m%d')
# 采用遍历的形式读取数值
fields = data.Fields
values = data.Data
d = tick.__dict__
for n, field in enumerate(fields):
field = field.lower()
key = self.wsqParamMap[field]
value = values[n][0]
d[key] = value
self.onTick(tick)
#----------------------------------------------------------------------
def wConnect(self, event):
"""利用事件处理线程去异步连接Wind接口"""
result = self.w.start()
log = VtLogData()
log.gatewayName = self.gatewayName
if not result.ErrorCode:
log.logContent = u'Wind接口连接成功'
else:
log.logContent = u'Wind接口连接失败错误代码%d' %result.ErrorCode
self.onLog(log)