初步完成了vn.trader,包含CTP、LTS、WIND的接口,以及CTA策略模块,策略模块的测试尚未全部完成
This commit is contained in:
parent
13fbf771b7
commit
c3cf86c452
5
.gitignore
vendored
5
.gitignore
vendored
@ -36,4 +36,7 @@ Release/
|
||||
|
||||
# 其他文件
|
||||
*.dump
|
||||
*.vssettings
|
||||
*.vssettings
|
||||
|
||||
# 不想同步的
|
||||
*.local
|
@ -39,8 +39,7 @@ class Config(object):
|
||||
toke_ = '44ebc0f058981f85382595f9f15f967' + \
|
||||
'0c7eaf2695de30dd752e8f33e9022baa0'
|
||||
|
||||
token = '7c2e59e212dbff90ffd6b382c7afb57' + \
|
||||
'bc987a99307d382b058af6748f591d723'
|
||||
token = '575593eb7696aec7339224c0fac2313780d8645f68b77369dcb35f8bcb419a0b'
|
||||
|
||||
body = {
|
||||
'ssl': False,
|
||||
|
@ -101,7 +101,7 @@ def test_mktbar_M1_get_interM():
|
||||
|
||||
if __name__ == '__main__':
|
||||
#test_config()
|
||||
#test_mktbar_D1()
|
||||
test_mktbar_D1()
|
||||
#test_bond_D1()
|
||||
#test_fut_D1()
|
||||
#test_fund_D1()
|
||||
@ -116,4 +116,4 @@ if __name__ == '__main__':
|
||||
#test_mongod_get_all()
|
||||
#test_mktbar_M1_get_drudgery()
|
||||
#test_mktbar_M1_get_all()
|
||||
test_mktbar_M1_get_interM()
|
||||
#test_mktbar_M1_get_interM()
|
Binary file not shown.
@ -1414,7 +1414,7 @@ int TdApi::reqOrderInsert(dict req, int nRequestID)
|
||||
CSecurityFtdcInputOrderField myreq = CSecurityFtdcInputOrderField();
|
||||
memset(&myreq, 0, sizeof(myreq));
|
||||
getChar(req, "ContingentCondition", &myreq.ContingentCondition);
|
||||
getChar(req, "CombOffsetFlag", myreq.CombOffsetFlag);
|
||||
//getChar(req, "CombOffsetFlag", myreq.CombOffsetFlag);
|
||||
getChar(req, "UserID", myreq.UserID);
|
||||
getChar(req, "LimitPrice", myreq.LimitPrice);
|
||||
getInt(req, "UserForceClose", &myreq.UserForceClose);
|
||||
@ -1436,6 +1436,19 @@ int TdApi::reqOrderInsert(dict req, int nRequestID)
|
||||
getInt(req, "RequestID", &myreq.RequestID);
|
||||
getChar(req, "Direction", &myreq.Direction);
|
||||
|
||||
//´¦ÀíCombOffsetFlag
|
||||
if (req.has_key("CombOffsetFlag"))
|
||||
{
|
||||
object o2 = req["CombOffsetFlag"];
|
||||
extract<string> x2(o2);
|
||||
if (x2.check())
|
||||
{
|
||||
string s2 = x2();
|
||||
const char *buffer2 = s2.c_str();
|
||||
myreq.CombOffsetFlag[0] = *buffer2;
|
||||
}
|
||||
}
|
||||
|
||||
int i = this->api->ReqOrderInsert(&myreq, nRequestID);
|
||||
return i;
|
||||
};
|
||||
|
14
vn.trader/CTA_setting.json
Normal file
14
vn.trader/CTA_setting.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"Test1": {
|
||||
"strategyClassName": "TestStrategy",
|
||||
"vtSymbol": "IF1511"
|
||||
},
|
||||
"Test3": {
|
||||
"strategyClassName": "TestStrategy",
|
||||
"vtSymbol": "IF1512"
|
||||
},
|
||||
"Test2": {
|
||||
"strategyClassName": "TestStrategy",
|
||||
"vtSymbol": "IH1511"
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"brokerID": "8070",
|
||||
"tdAddress": "tcp://27.115.57.147:41205",
|
||||
"password": "123456",
|
||||
"mdAddress": "tcp://27.115.57.147:41213",
|
||||
"userID": "750000"
|
||||
"brokerID": "9999",
|
||||
"tdAddress": "tcp://180.168.146.187:10000",
|
||||
"password": "simnow申请",
|
||||
"mdAddress": "tcp://180.168.212.228:41213",
|
||||
"userID": "simnow申请"
|
||||
}
|
BIN
vn.trader/ContractData.vt
Normal file
BIN
vn.trader/ContractData.vt
Normal file
Binary file not shown.
11
vn.trader/LTS_connect.json
Normal file
11
vn.trader/LTS_connect.json
Normal 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": "华宝证券申请",
|
||||
"mdPassword": "华宝证券申请",
|
||||
"userID": "华宝证券申请"
|
||||
}
|
15
vn.trader/ctaConstant.py
Normal file
15
vn.trader/ctaConstant.py
Normal 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.'
|
472
vn.trader/ctaEngine.py
Normal file
472
vn.trader/ctaEngine.py
Normal file
@ -0,0 +1,472 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
from datetime import datetime
|
||||
import json
|
||||
from collections import OrderedDict
|
||||
|
||||
from eventEngine import *
|
||||
from vtConstant import *
|
||||
from vtGateway import VtSubscribeReq, VtOrderReq, VtCancelOrderReq, VtLogData
|
||||
|
||||
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为vtOrderID,value为strategy对象
|
||||
self.orderStrategyDict = {}
|
||||
|
||||
# 本地停止单编号计数
|
||||
self.stopOrderCount = 0
|
||||
# stopOrderID = STOPORDERPREFIX + str(stopOrderCount)
|
||||
|
||||
# 本地停止单字典
|
||||
# key为stopOrderID,value为stopOrder对象
|
||||
self.stopOrderDict = {} # 停止单撤销后不会从本字典中删除
|
||||
self.workingStopOrderDict = {} # 停止单撤销后会从本字典中删除
|
||||
|
||||
# 注册事件监听
|
||||
self.registerEvent()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
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 procecssTickEvent(self, event):
|
||||
"""处理行情推送"""
|
||||
tick = event.dict_['data']
|
||||
# 收到tick行情后,先处理本地停止单(检查是否要立即发出)
|
||||
self.processStopOrder(tick)
|
||||
|
||||
# 推送tick到对应的策略对象进行处理
|
||||
if tick.vtSymbol in self.tickStrategyDict:
|
||||
# 将vtTickData数据转化为ctaTickData
|
||||
ctaTick = CtaTickData()
|
||||
d = ctaTick.__dict__
|
||||
for key in d.keys():
|
||||
if key != 'datetime':
|
||||
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 processOrderEvent(self, event):
|
||||
"""处理委托推送"""
|
||||
order = event.dict_['data']
|
||||
|
||||
if order.vtOrderID in self.orderStrategyDict:
|
||||
strategy = self.orderStrategyDict[order.vtOrderID]
|
||||
strategy.onOrder(order)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def processTradeEvent(self, event):
|
||||
"""处理成交推送"""
|
||||
trade = event.dict_['data']
|
||||
|
||||
if trade.vtOrderID in self.orderStrategyDict:
|
||||
strategy = self.orderStrategyDict[order.vtOrderID]
|
||||
strategy.onTrade(trade)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def registerEvent(self):
|
||||
"""注册事件监听"""
|
||||
self.eventEngine.register(EVENT_TICK, self.procecssTickEvent)
|
||||
self.eventEngine.register(EVENT_ORDER, self.processOrderEvent)
|
||||
self.eventEngine.register(EVENT_TRADE, self.processTradeEvent)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
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:
|
||||
# 创建策略对象
|
||||
strategy = strategyClass(self, name, paramDict)
|
||||
self.strategyDict[name] = strategy
|
||||
|
||||
# 保存Tick映射关系
|
||||
if strategy.vtSymbol in self.tickStrategyDict:
|
||||
l = self.tickStrategyDict[strategy.vtSymbol]
|
||||
else:
|
||||
l = []
|
||||
self.tickStrategyDict[strategy.vtSymbol] = l
|
||||
l.append(strategy)
|
||||
|
||||
# 订阅合约
|
||||
contract = self.dataEngine.getContract(strategy.vtSymbol)
|
||||
if contract:
|
||||
req = VtSubscribeReq()
|
||||
req.symbol = contract.symbol
|
||||
req.exchange = contract.exchange
|
||||
self.mainEngine.subscribe(req, contract.gatewayName)
|
||||
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 getStrategyVar(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 getStrategyParam(self, name):
|
||||
"""获取策略的参数字典"""
|
||||
if name in self.strategyDict:
|
||||
strategy = self.strategyDict[name]
|
||||
d = strategy.__dict__
|
||||
paramDict = OrderedDict()
|
||||
|
||||
for key in strategy.paramList:
|
||||
if key in d:
|
||||
paramDict[key] = d[key]
|
||||
|
||||
return paramDict
|
||||
else:
|
||||
self.writeCtaLog(u'策略对象不存在:' + name)
|
||||
return None
|
||||
|
||||
|
10
vn.trader/ctaStrategies.py
Normal file
10
vn.trader/ctaStrategies.py
Normal file
@ -0,0 +1,10 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
'''
|
||||
在本文件中引入所有希望在系统中使用的策略类
|
||||
'''
|
||||
|
||||
from ctaStrategyTemplate import TestStrategy
|
||||
|
||||
strategyClassDict = {}
|
||||
strategyClassDict[u'TestStrategy'] = TestStrategy
|
233
vn.trader/ctaStrategyTemplate.py
Normal file
233
vn.trader/ctaStrategyTemplate.py
Normal file
@ -0,0 +1,233 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
from vtConstant import *
|
||||
from ctaConstant import *
|
||||
|
||||
|
||||
########################################################################
|
||||
class CtaStrategyTemplate(object):
|
||||
"""CTA策略模板"""
|
||||
# 策略类的名称
|
||||
strategyClassName = 'Template'
|
||||
|
||||
# 参数列表,保存了参数的名称
|
||||
paramList = ['vtSymbol']
|
||||
|
||||
# 变量列表,保存了变量的名称
|
||||
varList = ['trading']
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self, ctaEngine, name, setting=None):
|
||||
"""Constructor"""
|
||||
self.ctaEngine = ctaEngine
|
||||
self.name = name
|
||||
|
||||
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] = setting[key]
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def getToday(self):
|
||||
"""查询当前日期"""
|
||||
return self.ctaEngine.getToday()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def writeCtaLog(self, content):
|
||||
"""记录CTA日志"""
|
||||
self.ctaEngine.writeCtaLog(content)
|
||||
|
||||
|
||||
|
||||
########################################################################
|
||||
class TestStrategy(CtaStrategyTemplate):
|
||||
"""测试策略"""
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self, ctaEngine, name, setting=None):
|
||||
"""Constructor"""
|
||||
super(TestStrategy, self).__init__(ctaEngine, name, setting)
|
||||
|
||||
self.strategyClassName = 'TestStrategy'
|
||||
|
||||
self.author = u'用Python的交易员' # 作者
|
||||
|
||||
self.pos = EMPTY_INT # 持仓
|
||||
self.lastPrice = EMPTY_FLOAT # 最新价
|
||||
|
||||
# 参数和变量列表设置
|
||||
self.paramList.append(u'author')
|
||||
|
||||
self.varList.append('pos')
|
||||
self.varList.append('lastPrice')
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def init(self):
|
||||
"""初始化策略(必须由用户继承实现)"""
|
||||
self.writeCtaLog(u'测试策略%s初始化' %self.name)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def start(self):
|
||||
"""启动策略(必须由用户继承实现)"""
|
||||
self.writeCtaLog(u'测试策略%s启动' %self.name)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def stop(self):
|
||||
"""停止策略(必须由用户继承实现)"""
|
||||
self.writeCtaLog(u'测试策略%s停止' %self.name)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onTick(self, tick):
|
||||
"""收到行情TICK推送(必须由用户继承实现)"""
|
||||
self.writeCtaLog(u'测试策略%s收到Tick' %self.name)
|
||||
self.lastPrice = tick.lastPrice
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onOrder(self, order):
|
||||
"""收到委托变化推送(必须由用户继承实现)"""
|
||||
self.writeCtaLog(u'onOrder不会被调用')
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onTrade(self, trade):
|
||||
"""收到成交推送(必须由用户继承实现)"""
|
||||
self.writeCtaLog(u'onTrade不会被调用')
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onBar(self, bar):
|
||||
"""收到Bar推送(必须由用户继承实现)"""
|
||||
self.writeCtaLog(u'测试策略%s收到Bar' %self.name)
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,5 +1,13 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
'''
|
||||
vn.ctp的gateway接入
|
||||
|
||||
考虑到现阶段大部分CTP中的ExchangeID字段返回的都是空值
|
||||
vtSymbol直接使用symbol
|
||||
'''
|
||||
|
||||
|
||||
import os
|
||||
import json
|
||||
|
||||
@ -8,6 +16,47 @@ from vnctptd import TdApi
|
||||
from ctpDataType import *
|
||||
from vtGateway import *
|
||||
|
||||
# 以下为一些VT类型和CTP类型的映射字典
|
||||
# 价格类型映射
|
||||
priceTypeMap = {}
|
||||
priceTypeMap[PRICETYPE_LIMITPRICE] = defineDict["THOST_FTDC_OPT_LimitPrice"]
|
||||
priceTypeMap[PRICETYPE_MARKETPRICE] = defineDict["THOST_FTDC_OPT_AnyPrice"]
|
||||
priceTypeMapReverse = {v: k for k, v in priceTypeMap.items()}
|
||||
|
||||
# 方向类型映射
|
||||
directionMap = {}
|
||||
directionMap[DIRECTION_LONG] = defineDict['THOST_FTDC_D_Buy']
|
||||
directionMap[DIRECTION_SHORT] = defineDict['THOST_FTDC_D_Sell']
|
||||
directionMapReverse = {v: k for k, v in directionMap.items()}
|
||||
|
||||
# 开平类型映射
|
||||
offsetMap = {}
|
||||
offsetMap[OFFSET_OPEN] = defineDict['THOST_FTDC_OF_Open']
|
||||
offsetMap[OFFSET_CLOSE] = defineDict['THOST_FTDC_OF_Close']
|
||||
offsetMap[OFFSET_CLOSETODAY] = defineDict['THOST_FTDC_OF_CloseToday']
|
||||
offsetMap[OFFSET_CLOSESYESTERDAY] = defineDict['THOST_FTDC_OF_CloseYesterday']
|
||||
offsetMapReverse = {v:k for k,v in offsetMap.items()}
|
||||
|
||||
# 交易所类型映射
|
||||
exchangeMap = {}
|
||||
#exchangeMap[EXCHANGE_CFFEX] = defineDict['THOST_FTDC_EIDT_CFFEX']
|
||||
#exchangeMap[EXCHANGE_SHFE] = defineDict['THOST_FTDC_EIDT_SHFE']
|
||||
#exchangeMap[EXCHANGE_CZCE] = defineDict['THOST_FTDC_EIDT_CZCE']
|
||||
#exchangeMap[EXCHANGE_DCE] = defineDict['THOST_FTDC_EIDT_DCE']
|
||||
exchangeMap[EXCHANGE_CFFEX] = 'CFFEX'
|
||||
exchangeMap[EXCHANGE_SHFE] = 'SHFE'
|
||||
exchangeMap[EXCHANGE_CZCE] = 'CZCE'
|
||||
exchangeMap[EXCHANGE_DCE] = 'DCE'
|
||||
exchangeMap[EXCHANGE_UNKNOWN] = ''
|
||||
exchangeMapReverse = {v:k for k,v in exchangeMap.items()}
|
||||
|
||||
# 持仓类型映射
|
||||
posiDirectionMap = {}
|
||||
posiDirectionMap[DIRECTION_NET] = defineDict["THOST_FTDC_PD_Net"]
|
||||
posiDirectionMap[DIRECTION_LONG] = defineDict["THOST_FTDC_PD_Long"]
|
||||
posiDirectionMap[DIRECTION_SHORT] = defineDict["THOST_FTDC_PD_Short"]
|
||||
posiDirectionMapReverse = {v:k for k,v in posiDirectionMap.items()}
|
||||
|
||||
|
||||
########################################################################
|
||||
class CtpGateway(VtGateway):
|
||||
@ -24,6 +73,8 @@ class CtpGateway(VtGateway):
|
||||
self.mdConnected = False # 行情API连接状态,登录完成后为True
|
||||
self.tdConnected = False # 交易API连接状态
|
||||
|
||||
self.qryEnabled = False # 是否要启动循环查询
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def connect(self):
|
||||
"""连接"""
|
||||
@ -56,11 +107,14 @@ class CtpGateway(VtGateway):
|
||||
# 创建行情和交易接口对象
|
||||
self.mdApi.connect(userID, password, brokerID, mdAddress)
|
||||
self.tdApi.connect(userID, password, brokerID, tdAddress)
|
||||
|
||||
# 初始化并启动查询
|
||||
self.initQuery()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def subscribe(self, subscribeReq):
|
||||
"""订阅行情"""
|
||||
self.mdApi(subscribeReq)
|
||||
self.mdApi.subscribe(subscribeReq)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def sendOrder(self, orderReq):
|
||||
@ -85,8 +139,52 @@ class CtpGateway(VtGateway):
|
||||
#----------------------------------------------------------------------
|
||||
def close(self):
|
||||
"""关闭"""
|
||||
self.mdApi.close()
|
||||
self.tdApi.close()
|
||||
if self.mdConnected:
|
||||
self.mdApi.close()
|
||||
if self.tdConnected:
|
||||
self.tdApi.close()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def initQuery(self):
|
||||
"""初始化连续查询"""
|
||||
if self.qryEnabled:
|
||||
# 需要循环的查询函数列表
|
||||
self.qryFunctionList = [self.getAccount, self.getPosition]
|
||||
|
||||
self.qryCount = 0 # 查询触发倒计时
|
||||
self.qryTrigger = 2 # 查询触发点
|
||||
self.qryNextFunction = 0 # 上次运行的查询函数索引
|
||||
|
||||
self.startQuery()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def query(self, event):
|
||||
"""注册到事件处理引擎上的查询函数"""
|
||||
self.qryCount += 1
|
||||
|
||||
if self.qryCount > self.qryTrigger:
|
||||
# 清空倒计时
|
||||
self.qryCount = 0
|
||||
|
||||
# 执行查询函数
|
||||
function = self.qryFunctionList[self.qryNextFunction]
|
||||
function()
|
||||
|
||||
# 计算下次查询函数的索引,如果超过了列表长度,则重新设为0
|
||||
self.qryNextFunction += 1
|
||||
if self.qryNextFunction == len(self.qryFunctionList):
|
||||
self.qryNextFunction = 0
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def startQuery(self):
|
||||
"""启动连续查询"""
|
||||
self.eventEngine.register(EVENT_TIMER, self.query)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def setQryEnabled(self, qryEnabled):
|
||||
"""设置是否要启动循环查询"""
|
||||
self.qryEnabled = qryEnabled
|
||||
|
||||
|
||||
|
||||
########################################################################
|
||||
@ -106,7 +204,6 @@ class CtpMdApi(MdApi):
|
||||
self.connectionStatus = False # 连接状态
|
||||
self.loginStatus = False # 登录状态
|
||||
|
||||
|
||||
self.subscribedSymbols = set() # 已订阅合约代码
|
||||
|
||||
self.userID = EMPTY_STRING # 账号
|
||||
@ -123,7 +220,6 @@ class CtpMdApi(MdApi):
|
||||
log.gatewayName = self.gatewayName
|
||||
log.logContent = u'行情服务器连接成功'
|
||||
self.gateway.onLog(log)
|
||||
|
||||
self.login()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
@ -162,6 +258,7 @@ class CtpMdApi(MdApi):
|
||||
self.gateway.mdConnected = True
|
||||
|
||||
log = VtLogData()
|
||||
log.gatewayName = self.gatewayName
|
||||
log.logContent = u'行情服务器登录完成'
|
||||
self.gateway.onLog(log)
|
||||
|
||||
@ -214,18 +311,25 @@ class CtpMdApi(MdApi):
|
||||
def onRtnDepthMarketData(self, data):
|
||||
"""行情推送"""
|
||||
tick = VtTickData()
|
||||
tick.gatewayName = self.gatewayName
|
||||
|
||||
tick.symbol = data['InstrumentID']
|
||||
tick.vtSymbol = '.'.join([self.gatewayName, tick.symbol])
|
||||
tick.exchange = exchangeMapReverse.get(data['ExchangeID'], u'未知')
|
||||
tick.vtSymbol = tick.symbol #'.'.join([tick.symbol, EXCHANGE_UNKNOWN])
|
||||
|
||||
tick.lastPrice = data['LastPrice']
|
||||
tick.volume = data['Volume']
|
||||
tick.openInterest = data['OpenInterest']
|
||||
tick.tickTime = '.'.join([data['UpdateTime'], str(data['UpdateMillisec']/100)])
|
||||
tick.time = '.'.join([data['UpdateTime'], str(data['UpdateMillisec']/100)])
|
||||
tick.date = data['TradingDay']
|
||||
|
||||
tick.openPrice = data['OpenPrice']
|
||||
tick.highPrice = data['HighestPrice']
|
||||
tick.lowPrice = data['LowestPrice']
|
||||
tick.preClosePrice = data['PreClosePrice']
|
||||
|
||||
tick.upperLimit = data['UpperLimitPrice']
|
||||
tick.lowerLimit = data['LowerLimitPrice']
|
||||
|
||||
# CTP只有一档行情
|
||||
tick.bidPrice1 = data['BidPrice1']
|
||||
@ -280,7 +384,10 @@ class CtpMdApi(MdApi):
|
||||
#----------------------------------------------------------------------
|
||||
def subscribe(self, subscribeReq):
|
||||
"""订阅合约"""
|
||||
self.subscribeMarketData(subscribeReq.symbol)
|
||||
# 这里的设计是,如果尚未登录就调用了订阅方法
|
||||
# 则先保存订阅请求,登录完成后会自动订阅
|
||||
if self.loginStatus:
|
||||
self.subscribeMarketData(str(subscribeReq.symbol))
|
||||
self.subscribedSymbols.add(subscribeReq)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
@ -305,27 +412,6 @@ class CtpMdApi(MdApi):
|
||||
class CtpTdApi(TdApi):
|
||||
"""CTP交易API实现"""
|
||||
|
||||
# 以下为一些VT类型和CTP类型的映射字典
|
||||
# 价格类型映射
|
||||
priceTypeMap = {}
|
||||
priceTypeMap[PRICETYPE_LIMITPRICE] = defineDict["THOST_FTDC_OPT_LimitPrice"]
|
||||
priceTypeMap[PRICETYPE_MARKETPRICE] = defineDict["THOST_FTDC_OPT_AnyPrice"]
|
||||
priceTypeMapReverse = {v: k for k, v in priceTypeMap.items()}
|
||||
|
||||
# 方向类型映射
|
||||
directionMap = {}
|
||||
directionMap[DIRECTION_LONG] = defineDict['THOST_FTDC_D_Buy']
|
||||
directionMap[DIRECTION_SHORT] = defineDict['THOST_FTDC_D_Sell']
|
||||
directionMapReverse = {v: k for k, v in directionMap.items()}
|
||||
|
||||
# 开平类型映射
|
||||
offsetMap = {}
|
||||
offsetMap[OFFSET_OPEN] = defineDict['THOST_FTDC_OF_Open']
|
||||
offsetMap[OFFSET_CLOSE] = defineDict['THOST_FTDC_OF_Close']
|
||||
offsetMap[OFFSET_CLOSETODAY] = defineDict['THOST_FTDC_OF_CloseToday']
|
||||
offsetMap[OFFSET_CLOSESYESTERDAY] = defineDict['THOST_FTDC_OF_CloseYesterday']
|
||||
offsetMapReverse = {v:k for k,v in offsetMap.items()}
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self, gateway):
|
||||
"""API对象的初始化函数"""
|
||||
@ -345,6 +431,9 @@ class CtpTdApi(TdApi):
|
||||
self.brokerID = EMPTY_STRING # 经纪商代码
|
||||
self.address = EMPTY_STRING # 服务器地址
|
||||
|
||||
self.frontID = EMPTY_INT # 前置机编号
|
||||
self.sessionID = EMPTY_INT # 会话编号
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onFrontConnected(self):
|
||||
"""服务器连接"""
|
||||
@ -384,6 +473,8 @@ class CtpTdApi(TdApi):
|
||||
"""登陆回报"""
|
||||
# 如果登录成功,推送日志信息
|
||||
if error['ErrorID'] == 0:
|
||||
self.frontID = str(data['FrontID'])
|
||||
self.sessionID = str(data['SessionID'])
|
||||
self.loginStatus = True
|
||||
self.gateway.mdConnected = True
|
||||
|
||||
@ -536,17 +627,13 @@ class CtpTdApi(TdApi):
|
||||
|
||||
# 保存代码
|
||||
pos.symbol = data['InstrumentID']
|
||||
pos.vtSymbol = '.'.join([self.gatewayName, pos.symbol])
|
||||
pos.vtSymbol = pos.symbol # 这里因为data中没有ExchangeID这个字段
|
||||
|
||||
# 方向和持仓冻结数量
|
||||
if data['PosiDirection'] == '1':
|
||||
pos.direction = DIRECTION_NET
|
||||
pos.direction = posiDirectionMapReverse.get(data['PosiDirection'], '')
|
||||
if pos.direction == DIRECTION_NET or pos.direction == DIRECTION_LONG:
|
||||
pos.frozen = data['LongFrozen']
|
||||
if data['PosiDirection'] == '2':
|
||||
pos.direction = DIRECTION_LONG
|
||||
pos.frozen = data['LongFrozen']
|
||||
elif data['PosiDirection'] == '3':
|
||||
pos.direction = DIRECTION_SHORT
|
||||
elif pos.direction == DIRECTION_SHORT:
|
||||
pos.frozen = data['ShortFrozen']
|
||||
|
||||
# 持仓量
|
||||
@ -583,7 +670,7 @@ class CtpTdApi(TdApi):
|
||||
# 这里的balance和快期中的账户不确定是否一样,需要测试
|
||||
account.balance = (data['PreBalance'] - data['PreCredit'] - data['PreMortgage'] +
|
||||
data['Mortgage'] - data['Withdraw'] + data['Deposit'] +
|
||||
data['ClostProfit'] + data['PositionProfit'] + data['CashIn'] -
|
||||
data['CloseProfit'] + data['PositionProfit'] + data['CashIn'] -
|
||||
data['Commission'])
|
||||
|
||||
# 推送
|
||||
@ -624,10 +711,11 @@ class CtpTdApi(TdApi):
|
||||
"""合约查询回报"""
|
||||
contract = VtContractData()
|
||||
contract.gatewayName = self.gatewayName
|
||||
|
||||
|
||||
contract.symbol = data['InstrumentID']
|
||||
contract.vtSymbol = '.'.join([self.gatewayName, contract.symbol])
|
||||
contract.name = data['InstrumentName']
|
||||
contract.exchange = exchangeMapReverse[data['ExchangeID']]
|
||||
contract.vtSymbol = contract.symbol #'.'.join([contract.symbol, contract.exchange])
|
||||
contract.name = data['InstrumentName'].decode('GBK')
|
||||
|
||||
# 合约数值
|
||||
contract.size = data['VolumeMultiple']
|
||||
@ -637,20 +725,28 @@ class CtpTdApi(TdApi):
|
||||
|
||||
# 合约类型
|
||||
if data['ProductClass'] == '1':
|
||||
contract.productClass == PRODUCT_FUTURES
|
||||
contract.productClass = PRODUCT_FUTURES
|
||||
elif data['ProductClass'] == '2':
|
||||
contract.productClass = PRODUCT_OPTION
|
||||
elif data['ProductClass'] == '3':
|
||||
contract.productClass = PRODUCT_COMBINATION
|
||||
else:
|
||||
contract.productClass = PRODUCT_UNKNOWN
|
||||
|
||||
# 期权类型
|
||||
if data['OptionType'] == '1':
|
||||
if data['OptionsType'] == '1':
|
||||
contract.optionType = OPTION_CALL
|
||||
elif data['OptionType'] == '2':
|
||||
elif data['OptionsType'] == '2':
|
||||
contract.optionType = OPTION_PUT
|
||||
|
||||
# 推送
|
||||
self.gateway.onContract(contract)
|
||||
|
||||
if last:
|
||||
log = VtLogData()
|
||||
log.gatewayName = self.gatewayName
|
||||
log.logContent = u'交易合约信息获取完成'
|
||||
self.gateway.onLog(log)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onRspQryDepthMarketData(self, data, error, n, last):
|
||||
@ -779,10 +875,10 @@ class CtpTdApi(TdApi):
|
||||
|
||||
# 保存代码和报单号
|
||||
order.symbol = data['InstrumentID']
|
||||
order.vtSymbol = '.'.join([self.gatewayName, order.symbol])
|
||||
order.exchange = exchangeMapReverse[data['ExchangeID']]
|
||||
order.vtSymbol = order.symbol #'.'.join([order.symbol, order.exchange])
|
||||
|
||||
order.orderID = data['OrderRef']
|
||||
order.vtOrderID = '.'.join([self.gatewayName, order.orderID])
|
||||
|
||||
# 方向
|
||||
if data['Direction'] == '0':
|
||||
@ -821,6 +917,12 @@ class CtpTdApi(TdApi):
|
||||
order.frontID = data['FrontID']
|
||||
order.sessionID = data['SessionID']
|
||||
|
||||
# CTP的报单号一致性维护需要基于frontID, sessionID, orderID三个字段
|
||||
# 但在本接口设计中,已经考虑了CTP的OrderRef的自增性,避免重复
|
||||
# 唯一可能出现OrderRef重复的情况是多处登录并在非常接近的时间内(几乎同时发单)
|
||||
# 考虑到VtTrader的应用场景,认为以上情况不会构成问题
|
||||
order.vtOrderID = '.'.join([self.gatewayName, order.orderID])
|
||||
|
||||
# 推送
|
||||
self.gateway.onOrder(order)
|
||||
|
||||
@ -829,11 +931,12 @@ class CtpTdApi(TdApi):
|
||||
"""成交回报"""
|
||||
# 创建报单数据对象
|
||||
trade = VtTradeData()
|
||||
order.gatewayName = self.gatewayName
|
||||
trade.gatewayName = self.gatewayName
|
||||
|
||||
# 保存代码和报单号
|
||||
trade.symbol = data['InstrumentID']
|
||||
trade.vtSymbol = '.'.join([self.gatewayName, trade.symbol])
|
||||
trade.exchange = exchangeMapReverse[data['ExchangeID']]
|
||||
trade.vtSymbol = trade.symbol #'.'.join([trade.symbol, trade.exchange])
|
||||
|
||||
trade.tradeID = data['TradeID']
|
||||
trade.vtTradeID = '.'.join([self.gatewayName, trade.tradeID])
|
||||
@ -842,20 +945,10 @@ class CtpTdApi(TdApi):
|
||||
trade.vtOrderID = '.'.join([self.gatewayName, trade.orderID])
|
||||
|
||||
# 方向
|
||||
if data['Direction'] == '0':
|
||||
trade.direction = DIRECTION_LONG
|
||||
elif data['Direction'] == '1':
|
||||
trade.direction = DIRECTION_SHORT
|
||||
else:
|
||||
trade.direction = DIRECTION_UNKNOWN
|
||||
trade.direction = directionMapReverse.get(data['Direction'], '')
|
||||
|
||||
# 开平
|
||||
if data['OffsetFlag'] == '0':
|
||||
trade.offset = OFFSET_OPEN
|
||||
elif data['OffsetFlag'] == '1':
|
||||
trade.offset = OFFSET_CLOSE
|
||||
else:
|
||||
trade.offset = OFFSET_UNKNOW
|
||||
trade.offset = offsetMapReverse.get(data['OffsetFlag'], '')
|
||||
|
||||
# 价格、报单量等数值
|
||||
trade.price = data['Price']
|
||||
@ -1146,9 +1239,9 @@ class CtpTdApi(TdApi):
|
||||
|
||||
# 下面如果由于传入的类型本接口不支持,则会返回空字符串
|
||||
try:
|
||||
req['OrderPriceType'] = self.priceTypeMap[orderReq.priceType]
|
||||
req['Direction'] = self.directionMap[orderReq.priceType]
|
||||
req['CombOffsetFlag'] = self.offsetMap[orderReq.offset]
|
||||
req['OrderPriceType'] = priceTypeMap[orderReq.priceType]
|
||||
req['Direction'] = directionMap[orderReq.direction]
|
||||
req['CombOffsetFlag'] = offsetMap[orderReq.offset]
|
||||
except KeyError:
|
||||
return ''
|
||||
|
||||
@ -1168,7 +1261,8 @@ class CtpTdApi(TdApi):
|
||||
self.reqOrderInsert(req, self.reqID)
|
||||
|
||||
# 返回订单号(字符串),便于某些算法进行动态管理
|
||||
return str(self.orderRef)
|
||||
vtOrderID = '.'.join([self.gatewayName, str(self.orderRef)])
|
||||
return vtOrderID
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def cancelOrder(self, cancelOrderReq):
|
||||
@ -1179,7 +1273,7 @@ class CtpTdApi(TdApi):
|
||||
|
||||
req['InstrumentID'] = cancelOrderReq.symbol
|
||||
req['ExchangeID'] = cancelOrderReq.exchange
|
||||
req['OrderRef'] = cancelOrderReq.orderRef
|
||||
req['OrderRef'] = cancelOrderReq.orderID
|
||||
req['FrontID'] = cancelOrderReq.frontID
|
||||
req['SessionID'] = cancelOrderReq.sessionID
|
||||
|
||||
@ -1192,4 +1286,30 @@ class CtpTdApi(TdApi):
|
||||
#----------------------------------------------------------------------
|
||||
def close(self):
|
||||
"""关闭"""
|
||||
self.exit()
|
||||
self.exit()
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def test():
|
||||
"""测试"""
|
||||
from PyQt4 import QtCore
|
||||
import sys
|
||||
|
||||
def print_log(event):
|
||||
log = event.dict_['data']
|
||||
print ':'.join([log.logTime, log.logContent])
|
||||
|
||||
app = QtCore.QCoreApplication(sys.argv)
|
||||
|
||||
eventEngine = EventEngine()
|
||||
eventEngine.register(EVENT_LOG, print_log)
|
||||
eventEngine.start()
|
||||
|
||||
gateway = CtpGateway(eventEngine)
|
||||
gateway.connect()
|
||||
|
||||
sys.exit(app.exec_())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test()
|
@ -143,7 +143,7 @@ class EventEngine:
|
||||
"""注销事件处理函数监听"""
|
||||
# 尝试获取该事件类型对应的处理函数列表,若无则忽略该次注销请求
|
||||
try:
|
||||
handlerList = self.handlers[type_]
|
||||
handlerList = self.__handlers[type_]
|
||||
|
||||
# 如果该函数存在于列表中,则移除
|
||||
if handler in handlerList:
|
||||
@ -151,7 +151,7 @@ class EventEngine:
|
||||
|
||||
# 如果函数列表为空,则从引擎中移除该事件类型
|
||||
if not handlerList:
|
||||
del self.handlers[type_]
|
||||
del self.__handlers[type_]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
@ -24,6 +24,12 @@ EVENT_ACCOUNT = 'eAccount.' # 账户回报事件
|
||||
EVENT_CONTRACT = 'eContract.' # 合约基础信息回报事件
|
||||
EVENT_ERROR = 'eError.' # 错误回报事件
|
||||
|
||||
# CTA模块相关
|
||||
EVENT_CTA_LOG = 'eCtaLog' # CTA相关的日志事件
|
||||
|
||||
# Wind接口相关
|
||||
EVENT_WIND_CONNECTREQ = 'eWindConnectReq' # Wind接口请求连接事件
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def test():
|
||||
|
2181
vn.trader/ltsDataType.py
Normal file
2181
vn.trader/ltsDataType.py
Normal file
File diff suppressed because it is too large
Load Diff
1240
vn.trader/ltsGateway.py
Normal file
1240
vn.trader/ltsGateway.py
Normal file
File diff suppressed because it is too large
Load Diff
BIN
vn.trader/securitymduserapi.dll
Normal file
BIN
vn.trader/securitymduserapi.dll
Normal file
Binary file not shown.
BIN
vn.trader/securityqueryapi.dll
Normal file
BIN
vn.trader/securityqueryapi.dll
Normal file
Binary file not shown.
BIN
vn.trader/securitytraderapi.dll
Normal file
BIN
vn.trader/securitytraderapi.dll
Normal file
Binary file not shown.
@ -5,22 +5,152 @@ from collections import OrderedDict
|
||||
from PyQt4 import QtGui, QtCore
|
||||
|
||||
from eventEngine import *
|
||||
|
||||
from vtFunction import *
|
||||
from vtGateway import *
|
||||
|
||||
|
||||
BASIC_FONT = QtGui.QFont(u'微软雅黑', 12)
|
||||
|
||||
|
||||
########################################################################
|
||||
class BasicCell(QtGui.QTableWidgetItem):
|
||||
"""基础的单元格"""
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self, text=None):
|
||||
"""Constructor"""
|
||||
super(BasicCell, self).__init__()
|
||||
self.data = None
|
||||
if text:
|
||||
self.setContent(text)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def setContent(self, text):
|
||||
"""设置内容"""
|
||||
self.setText(text)
|
||||
|
||||
|
||||
########################################################################
|
||||
class DirectionCell(QtGui.QTableWidgetItem):
|
||||
"""用来显示买卖方向的单元格"""
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self, text=None):
|
||||
"""Constructor"""
|
||||
super(DirectionCell, self).__init__()
|
||||
self.data = None
|
||||
if text:
|
||||
self.setContent(text)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def setContent(self, text):
|
||||
"""设置内容"""
|
||||
if text == DIRECTION_LONG or text == DIRECTION_NET:
|
||||
self.setForeground(QtGui.QColor('red'))
|
||||
elif text == DIRECTION_SHORT:
|
||||
self.setForeground(QtGui.QColor('green'))
|
||||
self.setText(text)
|
||||
|
||||
|
||||
########################################################################
|
||||
class NameCell(QtGui.QTableWidgetItem):
|
||||
"""用来显示合约中文的单元格"""
|
||||
dataEngine = None
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self, text=None, dataEngine=None):
|
||||
"""Constructor"""
|
||||
super(NameCell, self).__init__()
|
||||
self.data = None
|
||||
if text:
|
||||
self.setContent(text)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
@staticmethod
|
||||
def setDataEngine(self, dataEngine):
|
||||
"""设置读取合约用的数据引擎对象"""
|
||||
self.dataEngine = dataEngine
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def setContent(self, text):
|
||||
"""设置内容"""
|
||||
if self.dataEngine:
|
||||
# 首先尝试正常获取合约对象
|
||||
contract = self.dataEngine.getContract(text)
|
||||
|
||||
# 如果交易所代码为UNKNOWN,则删除交易所代码部分后再试
|
||||
if EXCHANGE_UNKNOWN in text:
|
||||
i = text.index('.')
|
||||
symbol = text[:i]
|
||||
contract = self.dataEngine.getContract(symbol)
|
||||
|
||||
# 如果能读取合约信息
|
||||
if contract:
|
||||
self.setText(contract.name)
|
||||
|
||||
|
||||
########################################################################
|
||||
class BidCell(QtGui.QTableWidgetItem):
|
||||
"""买价单元格"""
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self, text=None):
|
||||
"""Constructor"""
|
||||
super(BidCell, self).__init__()
|
||||
self.data = None
|
||||
|
||||
self.setBackground(QtGui.QColor(255,174,201))
|
||||
|
||||
if text:
|
||||
self.setContent(text)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def setContent(self, text):
|
||||
"""设置内容"""
|
||||
self.setText(text)
|
||||
|
||||
|
||||
########################################################################
|
||||
class AskCell(QtGui.QTableWidgetItem):
|
||||
"""买价单元格"""
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self, text=None):
|
||||
"""Constructor"""
|
||||
super(AskCell, self).__init__()
|
||||
self.data = None
|
||||
|
||||
self.setBackground(QtGui.QColor(160,255,160))
|
||||
|
||||
if text:
|
||||
self.setContent(text)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def setContent(self, text):
|
||||
"""设置内容"""
|
||||
self.setText(text)
|
||||
|
||||
|
||||
########################################################################
|
||||
class BasicMonitor(QtGui.QTableWidget):
|
||||
"""基础监控"""
|
||||
"""
|
||||
基础监控
|
||||
|
||||
headerDict中的值对应的字典格式如下
|
||||
{'chinese': u'中文名', 'cellType': BasicCell}
|
||||
|
||||
"""
|
||||
signal = QtCore.pyqtSignal(type(Event()))
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self, eventEngine, parent=None):
|
||||
def __init__(self, eventEngine=None, parent=None):
|
||||
"""Constructor"""
|
||||
super(BasicMonitor, self).__init__(parent)
|
||||
|
||||
self.eventEngine = eventEngine
|
||||
|
||||
# 保存表头标签用
|
||||
self.headerDict = OrderedDict() # 有序字典,key是英文名,value是对应的中文名
|
||||
self.headerDict = OrderedDict() # 有序字典,key是英文名,value是对应的配置字典
|
||||
self.headerList = [] # 对应self.headerDict.keys()
|
||||
|
||||
# 保存相关数据用
|
||||
@ -33,6 +163,9 @@ class BasicMonitor(QtGui.QTableWidget):
|
||||
# 字体
|
||||
self.font = None
|
||||
|
||||
# 保存数据对象到单元格
|
||||
self.saveData = False
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def setHeaderDict(self, headerDict):
|
||||
"""设置表头有序字典"""
|
||||
@ -53,46 +186,65 @@ class BasicMonitor(QtGui.QTableWidget):
|
||||
def setFont(self, font):
|
||||
"""设置字体"""
|
||||
self.font = font
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def setSaveData(self, saveData):
|
||||
"""设置是否要保存数据到单元格"""
|
||||
self.saveData = saveData
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def initTable(self):
|
||||
"""初始化表格"""
|
||||
# 设置表格的列数
|
||||
col = len(self.labelDict)
|
||||
col = len(self.headerDict)
|
||||
self.setColumnCount(col)
|
||||
|
||||
# 设置列表头
|
||||
self.setHorizontalHeaderLabels(self.labelDict.values())
|
||||
labels = [d['chinese'] for d in self.headerDict.values()]
|
||||
self.setHorizontalHeaderLabels(labels)
|
||||
|
||||
# 关闭左边的垂直表头
|
||||
self.verticalHeader().setVisible(False)
|
||||
|
||||
# 设为不可编辑
|
||||
self.setEditTriggers(self.NoEditTriggers)
|
||||
|
||||
# 设为行交替颜色
|
||||
self.setAlternatingRowColors(True)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def registerEvent(self):
|
||||
"""注册GUI更新相关的事件监听"""
|
||||
self.signal.connect(self.updateData)
|
||||
self.signal.connect(self.updateEvent)
|
||||
self.eventEngine.register(self.eventType, self.signal.emit)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def updateEvent(self, event):
|
||||
"""收到事件更新"""
|
||||
data = event.dict_['data']
|
||||
self.updateData(data)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def updateData(self, event):
|
||||
"""更新数据"""
|
||||
data = event.dict_['data']
|
||||
key = data.__getattribute__(self.dataKey)
|
||||
|
||||
def updateData(self, data):
|
||||
"""将数据更新到表格中"""
|
||||
# 如果设置了dataKey,则采用存量更新模式
|
||||
if self.dataKey:
|
||||
key = data.__getattribute__(self.dataKey)
|
||||
# 如果键在数据字典中不存在,则先插入新的一行,并创建对应单元格
|
||||
if key not in self.dataDict:
|
||||
self.insertRow(0)
|
||||
d = {}
|
||||
for n, header in enumerate(self.headerList):
|
||||
content = unicode(data.__getattribute__(header))
|
||||
cell = QtGui.QTableWidgetItem(content)
|
||||
content = safeUnicode(data.__getattribute__(header))
|
||||
cellType = self.headerDict[header]['cellType']
|
||||
cell = cellType(content)
|
||||
|
||||
if self.font:
|
||||
cell.setFont(font) # 如果设置了特殊字体,则进行单元格设置
|
||||
cell.setFont(self.font) # 如果设置了特殊字体,则进行单元格设置
|
||||
|
||||
if self.saveData: # 如果设置了保存数据对象,则进行对象保存
|
||||
cell.data = data
|
||||
|
||||
self.setItem(0, n, cell)
|
||||
d[header] = cell
|
||||
self.dataDict[key] = d
|
||||
@ -100,17 +252,26 @@ class BasicMonitor(QtGui.QTableWidget):
|
||||
else:
|
||||
d = self.dataDict[key]
|
||||
for header in self.headerList:
|
||||
content = unicode(data.__getattribute__(header))
|
||||
content = safeUnicode(data.__getattribute__(header))
|
||||
cell = d[header]
|
||||
cell.setText(content)
|
||||
cell.setContent(content)
|
||||
|
||||
if self.saveData: # 如果设置了保存数据对象,则进行对象保存
|
||||
cell.data = data
|
||||
# 否则采用增量更新模式
|
||||
else:
|
||||
self.insertRow(0)
|
||||
for n, header in enumerate(self.headerList):
|
||||
content = unicode(data.__getattribute__(header))
|
||||
cell = QtGui.QTableWidgetItem(content)
|
||||
content = safeUnicode(data.__getattribute__(header))
|
||||
cellType = self.headerDict[header]['cellType']
|
||||
cell = cellType(content)
|
||||
|
||||
if self.font:
|
||||
cell.setFont(font)
|
||||
cell.setFont(self.font)
|
||||
|
||||
if self.saveData:
|
||||
cell.data = data
|
||||
|
||||
self.setItem(0, n, cell)
|
||||
|
||||
# 调整列宽
|
||||
@ -133,19 +294,20 @@ class MarketMonitor(BasicMonitor):
|
||||
|
||||
# 设置表头有序字典
|
||||
d = OrderedDict()
|
||||
d['symbol'] = u'合约代码'
|
||||
d['vtSymbol'] = u'系统代码'
|
||||
d['lastPrice'] = u'最新价'
|
||||
d['volume'] = u'成交量'
|
||||
d['openInterest'] = u'持仓量'
|
||||
d['openPrice'] = u'开盘价'
|
||||
d['highPrice'] = u'最高价'
|
||||
d['lowPrice'] = u'最低价'
|
||||
d['bidPrice1'] = u'买一价'
|
||||
d['bidVolume1'] = u'买一量'
|
||||
d['askPrice1'] = u'卖一价'
|
||||
d['askVolume1'] = u'卖一量'
|
||||
d['tickTime'] = u'时间'
|
||||
d['symbol'] = {'chinese':u'合约代码', 'cellType':BasicCell}
|
||||
d['vtSymbol'] = {'chinese':u'名称', 'cellType':NameCell}
|
||||
d['lastPrice'] = {'chinese':u'最新价', 'cellType':BasicCell}
|
||||
d['volume'] = {'chinese':u'成交量', 'cellType':BasicCell}
|
||||
d['openInterest'] = {'chinese':u'持仓量', 'cellType':BasicCell}
|
||||
d['openPrice'] = {'chinese':u'开盘价', 'cellType':BasicCell}
|
||||
d['highPrice'] = {'chinese':u'最高价', 'cellType':BasicCell}
|
||||
d['lowPrice'] = {'chinese':u'最低价', 'cellType':BasicCell}
|
||||
d['bidPrice1'] = {'chinese':u'买一价', 'cellType':BidCell}
|
||||
d['bidVolume1'] = {'chinese':u'买一量', 'cellType':BidCell}
|
||||
d['askPrice1'] = {'chinese':u'卖一价', 'cellType':AskCell}
|
||||
d['askVolume1'] = {'chinese':u'卖一量', 'cellType':AskCell}
|
||||
d['time'] = {'chinese':u'时间', 'cellType':BasicCell}
|
||||
d['gatewayName'] = {'chinese':u'接口', 'cellType':BasicCell}
|
||||
self.setHeaderDict(d)
|
||||
|
||||
# 设置数据键
|
||||
@ -154,6 +316,9 @@ class MarketMonitor(BasicMonitor):
|
||||
# 设置监控事件类型
|
||||
self.setEventType(EVENT_TICK)
|
||||
|
||||
# 设置字体
|
||||
self.setFont(BASIC_FONT)
|
||||
|
||||
# 初始化表格
|
||||
self.initTable()
|
||||
|
||||
@ -170,13 +335,14 @@ class LogMonitor(BasicMonitor):
|
||||
"""Constructor"""
|
||||
super(LogMonitor, self).__init__(eventEngine, parent)
|
||||
|
||||
d = OrderedDict()
|
||||
d['gatewayName'] = u'接口'
|
||||
d['logTime'] = u'时间'
|
||||
d['logContent'] = u'内容'
|
||||
d = OrderedDict()
|
||||
d['logTime'] = {'chinese':u'时间', 'cellType':BasicCell}
|
||||
d['logContent'] = {'chinese':u'内容', 'cellType':BasicCell}
|
||||
d['gatewayName'] = {'chinese':u'接口', 'cellType':BasicCell}
|
||||
self.setHeaderDict(d)
|
||||
|
||||
self.setEventType(EVENT_LOG)
|
||||
self.setFont(BASIC_FONT)
|
||||
self.initTable()
|
||||
self.registerEvent()
|
||||
|
||||
@ -190,13 +356,15 @@ class ErrorMonitor(BasicMonitor):
|
||||
"""Constructor"""
|
||||
super(ErrorMonitor, self).__init__(eventEngine, parent)
|
||||
|
||||
d = OrderedDict()
|
||||
d['gatewayName'] = u'接口'
|
||||
d['errorID'] = u'错误代码'
|
||||
d['errorMsg'] = u'错误信息'
|
||||
d = OrderedDict()
|
||||
d['errorID'] = {'chinese':u'错误代码', 'cellType':BasicCell}
|
||||
d['errorMsg'] = {'chinese':u'错误信息', 'cellType':BasicCell}
|
||||
d['additionalInfo'] = {'chinese':u'补充信息', 'cellType':BasicCell}
|
||||
d['gatewayName'] = {'chinese':u'接口', 'cellType':BasicCell}
|
||||
self.setHeaderDict(d)
|
||||
|
||||
self.setEventType(EVENT_ERROR)
|
||||
self.setFont(BASIC_FONT)
|
||||
self.initTable()
|
||||
self.registerEvent()
|
||||
|
||||
@ -211,18 +379,20 @@ class TradeMonitor(BasicMonitor):
|
||||
super(TradeMonitor, self).__init__(eventEngine, parent)
|
||||
|
||||
d = OrderedDict()
|
||||
d['gatewayName'] = u'接口'
|
||||
d['tradeID'] = u'成交编号'
|
||||
d['orderID'] = u'委托编号'
|
||||
d['symbol'] = u'合约代码'
|
||||
d['direction'] = u'方向'
|
||||
d['offset'] = u'开平'
|
||||
d['price'] = u'价格'
|
||||
d['volume'] = u'数量'
|
||||
d['tradeTime'] = u'成交时间'
|
||||
d['tradeID'] = {'chinese':u'成交编号', 'cellType':BasicCell}
|
||||
d['orderID'] = {'chinese':u'委托编号', 'cellType':BasicCell}
|
||||
d['symbol'] = {'chinese':u'合约代码', 'cellType':BasicCell}
|
||||
d['vtSymbol'] = {'chinese':u'名称', 'cellType':NameCell}
|
||||
d['direction'] = {'chinese':u'方向', 'cellType':DirectionCell}
|
||||
d['offset'] = {'chinese':u'开平', 'cellType':BasicCell}
|
||||
d['price'] = {'chinese':u'价格', 'cellType':BasicCell}
|
||||
d['volume'] = {'chinese':u'数量', 'cellType':BasicCell}
|
||||
d['tradeTime'] = {'chinese':u'成交时间', 'cellType':BasicCell}
|
||||
d['gatewayName'] = {'chinese':u'接口', 'cellType':BasicCell}
|
||||
self.setHeaderDict(d)
|
||||
|
||||
self.setEventType(EVENT_TRADE)
|
||||
self.setFont(BASIC_FONT)
|
||||
self.initTable()
|
||||
self.registerEvent()
|
||||
|
||||
@ -232,30 +402,57 @@ class OrderMonitor(BasicMonitor):
|
||||
"""委托监控"""
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self, eventEngine, parent=None):
|
||||
def __init__(self, eventEngine, mainEngine, parent=None):
|
||||
"""Constructor"""
|
||||
super(OrderMonitor, self).__init__(eventEngine, parent)
|
||||
|
||||
self.mainEngine = mainEngine
|
||||
|
||||
d = OrderedDict()
|
||||
d['gatewayName'] = u'接口'
|
||||
d['orderID'] = u'委托编号'
|
||||
d['symbol'] = u'合约代码'
|
||||
d['direction'] = u'方向'
|
||||
d['offset'] = u'开平'
|
||||
d['price'] = u'价格'
|
||||
d['totalVolume'] = u'委托数量'
|
||||
d['tradedVolume'] = u'成交数量'
|
||||
d['status'] = u'状态'
|
||||
d['orderTime'] = u'委托时间'
|
||||
d['tradeTime'] = u'撤销时间'
|
||||
d['frontID'] = u'前置编号'
|
||||
d['sessionID'] = u'会话编号'
|
||||
d['orderID'] = {'chinese':u'委托编号', 'cellType':BasicCell}
|
||||
d['symbol'] = {'chinese':u'合约代码', 'cellType':BasicCell}
|
||||
d['vtSymbol'] = {'chinese':u'名称', 'cellType':NameCell}
|
||||
d['direction'] = {'chinese':u'方向', 'cellType':DirectionCell}
|
||||
d['offset'] = {'chinese':u'开平', 'cellType':BasicCell}
|
||||
d['price'] = {'chinese':u'价格', 'cellType':BasicCell}
|
||||
d['totalVolume'] = {'chinese':u'委托数量', 'cellType':BasicCell}
|
||||
d['tradedVolume'] = {'chinese':u'成交数量', 'cellType':BasicCell}
|
||||
d['status'] = {'chinese':u'状态', 'cellType':BasicCell}
|
||||
d['orderTime'] = {'chinese':u'委托时间', 'cellType':BasicCell}
|
||||
d['cancelTime'] = {'chinese':u'撤销时间', 'cellType':BasicCell}
|
||||
d['frontID'] = {'chinese':u'前置编号', 'cellType':BasicCell}
|
||||
d['sessionID'] = {'chinese':u'会话编号', 'cellType':BasicCell}
|
||||
d['gatewayName'] = {'chinese':u'接口', 'cellType':BasicCell}
|
||||
self.setHeaderDict(d)
|
||||
|
||||
self.setDataKey('vtOrderID')
|
||||
self.setEventType(EVENT_ORDER)
|
||||
self.setFont(BASIC_FONT)
|
||||
self.setSaveData(True)
|
||||
|
||||
self.initTable()
|
||||
self.registerEvent()
|
||||
|
||||
self.connectSignal()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def connectSignal(self):
|
||||
"""连接信号"""
|
||||
# 双击单元格撤单
|
||||
self.itemDoubleClicked.connect(self.cancelOrder)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def cancelOrder(self, cell):
|
||||
"""根据单元格的数据撤单"""
|
||||
order = cell.data
|
||||
|
||||
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)
|
||||
|
||||
|
||||
########################################################################
|
||||
@ -268,16 +465,18 @@ class PositionMonitor(BasicMonitor):
|
||||
super(PositionMonitor, self).__init__(eventEngine, parent)
|
||||
|
||||
d = OrderedDict()
|
||||
d['gatewayName'] = u'接口'
|
||||
d['symbol'] = u'合约代码'
|
||||
d['direction'] = u'方向'
|
||||
d['position'] = u'持仓量'
|
||||
d['frozen'] = u'冻结量'
|
||||
d['price'] = u'价格'
|
||||
d['symbol'] = {'chinese':u'合约代码', 'cellType':BasicCell}
|
||||
d['vtSymbol'] = {'chinese':u'名称', 'cellType':NameCell}
|
||||
d['direction'] = {'chinese':u'方向', 'cellType':DirectionCell}
|
||||
d['position'] = {'chinese':u'持仓量', 'cellType':BasicCell}
|
||||
d['frozen'] = {'chinese':u'冻结量', 'cellType':BasicCell}
|
||||
d['price'] = {'chinese':u'价格', 'cellType':BasicCell}
|
||||
d['gatewayName'] = {'chinese':u'接口', 'cellType':BasicCell}
|
||||
self.setHeaderDict(d)
|
||||
|
||||
self.setDataKey('vtPositionName')
|
||||
self.setEventType(EVENT_POSITION)
|
||||
self.setFont(BASIC_FONT)
|
||||
self.initTable()
|
||||
self.registerEvent()
|
||||
|
||||
@ -292,20 +491,451 @@ class AccountMonitor(BasicMonitor):
|
||||
super(AccountMonitor, self).__init__(eventEngine, parent)
|
||||
|
||||
d = OrderedDict()
|
||||
d['gatewayName'] = u'接口'
|
||||
d['accountID'] = u'账户'
|
||||
d['preBalance'] = u'昨结'
|
||||
d['balance'] = u'净值'
|
||||
d['available'] = u'可用'
|
||||
d['commission'] = u'手续费'
|
||||
d['margin'] = u'保证金'
|
||||
d['closeProfit'] = u'平仓盈亏'
|
||||
d['positionProfit'] = u'持仓盈亏'
|
||||
d['accountID'] = {'chinese':u'账户', 'cellType':BasicCell}
|
||||
d['preBalance'] = {'chinese':u'昨结', 'cellType':BasicCell}
|
||||
d['balance'] = {'chinese':u'净值', 'cellType':BasicCell}
|
||||
d['available'] = {'chinese':u'可用', 'cellType':BasicCell}
|
||||
d['commission'] = {'chinese':u'手续费', 'cellType':BasicCell}
|
||||
d['margin'] = {'chinese':u'保证金', 'cellType':BasicCell}
|
||||
d['closeProfit'] = {'chinese':u'平仓盈亏', 'cellType':BasicCell}
|
||||
d['positionProfit'] = {'chinese':u'持仓盈亏', 'cellType':BasicCell}
|
||||
d['gatewayName'] = {'chinese':u'接口', 'cellType':BasicCell}
|
||||
self.setHeaderDict(d)
|
||||
|
||||
self.setDataKey('vtAccountID')
|
||||
self.setEventType(EVENT_ACCOUNT)
|
||||
self.setFont(BASIC_FONT)
|
||||
self.initTable()
|
||||
self.registerEvent()
|
||||
|
||||
|
||||
########################################################################
|
||||
class TradingWidget(QtGui.QFrame):
|
||||
"""简单交易组件"""
|
||||
signal = QtCore.pyqtSignal(type(Event()))
|
||||
|
||||
directionList = [DIRECTION_LONG,
|
||||
DIRECTION_SHORT]
|
||||
|
||||
offsetList = [OFFSET_OPEN,
|
||||
OFFSET_CLOSE,
|
||||
OFFSET_CLOSESYESTERDAY,
|
||||
OFFSET_CLOSETODAY]
|
||||
|
||||
priceTypeList = [PRICETYPE_LIMITPRICE,
|
||||
PRICETYPE_MARKETPRICE,
|
||||
PRICETYPE_FAK,
|
||||
PRICETYPE_FOK]
|
||||
|
||||
exchangeList = [EXCHANGE_NONE,
|
||||
EXCHANGE_CFFEX,
|
||||
EXCHANGE_SHFE,
|
||||
EXCHANGE_DCE,
|
||||
EXCHANGE_CZCE,
|
||||
EXCHANGE_SSE,
|
||||
EXCHANGE_SZSE]
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self, mainEngine, eventEngine, dataEngine, parent=None):
|
||||
"""Constructor"""
|
||||
super(TradingWidget, self).__init__(parent)
|
||||
self.mainEngine = mainEngine
|
||||
self.eventEngine = eventEngine
|
||||
self.dataEngine = dataEngine
|
||||
|
||||
self.symbol = ''
|
||||
|
||||
self.initUi()
|
||||
self.connectSignal()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def initUi(self):
|
||||
"""初始化界面"""
|
||||
self.setWindowTitle(u'交易')
|
||||
self.setMaximumWidth(400)
|
||||
self.setFrameShape(self.Box) # 设置边框
|
||||
self.setLineWidth(1)
|
||||
|
||||
# 左边部分
|
||||
labelSymbol = QtGui.QLabel(u'代码')
|
||||
labelName = QtGui.QLabel(u'名称')
|
||||
labelDirection = QtGui.QLabel(u'方向类型')
|
||||
labelOffset = QtGui.QLabel(u'开平')
|
||||
labelPrice = QtGui.QLabel(u'价格')
|
||||
labelVolume = QtGui.QLabel(u'数量')
|
||||
labelPriceType = QtGui.QLabel(u'价格类型')
|
||||
labelExchange = QtGui.QLabel(u'交易所')
|
||||
|
||||
self.lineSymbol = QtGui.QLineEdit()
|
||||
self.lineName = QtGui.QLineEdit()
|
||||
|
||||
self.comboDirection = QtGui.QComboBox()
|
||||
self.comboDirection.addItems(self.directionList)
|
||||
|
||||
self.comboOffset = QtGui.QComboBox()
|
||||
self.comboOffset.addItems(self.offsetList)
|
||||
|
||||
self.spinPrice = QtGui.QDoubleSpinBox()
|
||||
self.spinPrice.setDecimals(4)
|
||||
self.spinPrice.setMinimum(0)
|
||||
self.spinPrice.setMaximum(100000)
|
||||
|
||||
self.spinVolume = QtGui.QSpinBox()
|
||||
self.spinVolume.setMinimum(0)
|
||||
self.spinVolume.setMaximum(1000000)
|
||||
|
||||
self.comboPriceType = QtGui.QComboBox()
|
||||
self.comboPriceType.addItems(self.priceTypeList)
|
||||
|
||||
self.comboExchange = QtGui.QComboBox()
|
||||
self.comboExchange.addItems(self.exchangeList)
|
||||
|
||||
gridleft = QtGui.QGridLayout()
|
||||
gridleft.addWidget(labelSymbol, 0, 0)
|
||||
gridleft.addWidget(labelName, 1, 0)
|
||||
gridleft.addWidget(labelDirection, 2, 0)
|
||||
gridleft.addWidget(labelOffset, 3, 0)
|
||||
gridleft.addWidget(labelPrice, 4, 0)
|
||||
gridleft.addWidget(labelVolume, 5, 0)
|
||||
gridleft.addWidget(labelPriceType, 6, 0)
|
||||
gridleft.addWidget(labelExchange, 7, 0)
|
||||
gridleft.addWidget(self.lineSymbol, 0, 1)
|
||||
gridleft.addWidget(self.lineName, 1, 1)
|
||||
gridleft.addWidget(self.comboDirection, 2, 1)
|
||||
gridleft.addWidget(self.comboOffset, 3, 1)
|
||||
gridleft.addWidget(self.spinPrice, 4, 1)
|
||||
gridleft.addWidget(self.spinVolume, 5, 1)
|
||||
gridleft.addWidget(self.comboPriceType, 6, 1)
|
||||
gridleft.addWidget(self.comboExchange, 7, 1)
|
||||
|
||||
# 右边部分
|
||||
labelBid1 = QtGui.QLabel(u'买一')
|
||||
labelBid2 = QtGui.QLabel(u'买二')
|
||||
labelBid3 = QtGui.QLabel(u'买三')
|
||||
labelBid4 = QtGui.QLabel(u'买四')
|
||||
labelBid5 = QtGui.QLabel(u'买五')
|
||||
|
||||
labelAsk1 = QtGui.QLabel(u'卖一')
|
||||
labelAsk2 = QtGui.QLabel(u'卖二')
|
||||
labelAsk3 = QtGui.QLabel(u'卖三')
|
||||
labelAsk4 = QtGui.QLabel(u'卖四')
|
||||
labelAsk5 = QtGui.QLabel(u'卖五')
|
||||
|
||||
self.labelBidPrice1 = QtGui.QLabel()
|
||||
self.labelBidPrice2 = QtGui.QLabel()
|
||||
self.labelBidPrice3 = QtGui.QLabel()
|
||||
self.labelBidPrice4 = QtGui.QLabel()
|
||||
self.labelBidPrice5 = QtGui.QLabel()
|
||||
self.labelBidVolume1 = QtGui.QLabel()
|
||||
self.labelBidVolume2 = QtGui.QLabel()
|
||||
self.labelBidVolume3 = QtGui.QLabel()
|
||||
self.labelBidVolume4 = QtGui.QLabel()
|
||||
self.labelBidVolume5 = QtGui.QLabel()
|
||||
|
||||
self.labelAskPrice1 = QtGui.QLabel()
|
||||
self.labelAskPrice2 = QtGui.QLabel()
|
||||
self.labelAskPrice3 = QtGui.QLabel()
|
||||
self.labelAskPrice4 = QtGui.QLabel()
|
||||
self.labelAskPrice5 = QtGui.QLabel()
|
||||
self.labelAskVolume1 = QtGui.QLabel()
|
||||
self.labelAskVolume2 = QtGui.QLabel()
|
||||
self.labelAskVolume3 = QtGui.QLabel()
|
||||
self.labelAskVolume4 = QtGui.QLabel()
|
||||
self.labelAskVolume5 = QtGui.QLabel()
|
||||
|
||||
labelLast = QtGui.QLabel(u'最新')
|
||||
self.labelLastPrice = QtGui.QLabel()
|
||||
self.labelReturn = QtGui.QLabel()
|
||||
|
||||
self.labelLastPrice.setMinimumWidth(60)
|
||||
self.labelReturn.setMinimumWidth(60)
|
||||
|
||||
gridRight = QtGui.QGridLayout()
|
||||
gridRight.addWidget(labelAsk5, 0, 0)
|
||||
gridRight.addWidget(labelAsk4, 1, 0)
|
||||
gridRight.addWidget(labelAsk3, 2, 0)
|
||||
gridRight.addWidget(labelAsk2, 3, 0)
|
||||
gridRight.addWidget(labelAsk1, 4, 0)
|
||||
gridRight.addWidget(labelLast, 5, 0)
|
||||
gridRight.addWidget(labelBid1, 6, 0)
|
||||
gridRight.addWidget(labelBid2, 7, 0)
|
||||
gridRight.addWidget(labelBid3, 8, 0)
|
||||
gridRight.addWidget(labelBid4, 9, 0)
|
||||
gridRight.addWidget(labelBid5, 10, 0)
|
||||
|
||||
gridRight.addWidget(self.labelAskPrice5, 0, 1)
|
||||
gridRight.addWidget(self.labelAskPrice4, 1, 1)
|
||||
gridRight.addWidget(self.labelAskPrice3, 2, 1)
|
||||
gridRight.addWidget(self.labelAskPrice2, 3, 1)
|
||||
gridRight.addWidget(self.labelAskPrice1, 4, 1)
|
||||
gridRight.addWidget(self.labelLastPrice, 5, 1)
|
||||
gridRight.addWidget(self.labelBidPrice1, 6, 1)
|
||||
gridRight.addWidget(self.labelBidPrice2, 7, 1)
|
||||
gridRight.addWidget(self.labelBidPrice3, 8, 1)
|
||||
gridRight.addWidget(self.labelBidPrice4, 9, 1)
|
||||
gridRight.addWidget(self.labelBidPrice5, 10, 1)
|
||||
|
||||
gridRight.addWidget(self.labelAskVolume5, 0, 2)
|
||||
gridRight.addWidget(self.labelAskVolume4, 1, 2)
|
||||
gridRight.addWidget(self.labelAskVolume3, 2, 2)
|
||||
gridRight.addWidget(self.labelAskVolume2, 3, 2)
|
||||
gridRight.addWidget(self.labelAskVolume1, 4, 2)
|
||||
gridRight.addWidget(self.labelReturn, 5, 2)
|
||||
gridRight.addWidget(self.labelBidVolume1, 6, 2)
|
||||
gridRight.addWidget(self.labelBidVolume2, 7, 2)
|
||||
gridRight.addWidget(self.labelBidVolume3, 8, 2)
|
||||
gridRight.addWidget(self.labelBidVolume4, 9, 2)
|
||||
gridRight.addWidget(self.labelBidVolume5, 10, 2)
|
||||
|
||||
# 发单按钮
|
||||
buttonSendOrder = QtGui.QPushButton(u'发单')
|
||||
buttonCancelAll = QtGui.QPushButton(u'全撤')
|
||||
|
||||
size = buttonSendOrder.sizeHint()
|
||||
buttonSendOrder.setMinimumHeight(size.height()*2) # 把按钮高度设为默认两倍
|
||||
buttonCancelAll.setMinimumHeight(size.height()*2)
|
||||
|
||||
# 整合布局
|
||||
hbox = QtGui.QHBoxLayout()
|
||||
hbox.addLayout(gridleft)
|
||||
hbox.addLayout(gridRight)
|
||||
|
||||
vbox = QtGui.QVBoxLayout()
|
||||
vbox.addLayout(hbox)
|
||||
vbox.addWidget(buttonSendOrder)
|
||||
vbox.addWidget(buttonCancelAll)
|
||||
vbox.addStretch()
|
||||
|
||||
self.setLayout(vbox)
|
||||
|
||||
# 关联更新
|
||||
buttonSendOrder.clicked.connect(self.sendOrder)
|
||||
buttonCancelAll.clicked.connect(self.cancelAll)
|
||||
self.lineSymbol.returnPressed.connect(self.updateSymbol)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def updateSymbol(self):
|
||||
"""合约变化"""
|
||||
symbol = unicode(self.lineSymbol.text())
|
||||
exchange = unicode(self.comboExchange.currentText())
|
||||
|
||||
if exchange:
|
||||
vtSymbol = '.'.join([symbol, exchange])
|
||||
contract = self.dataEngine.getContract(vtSymbol)
|
||||
else:
|
||||
contract = self.dataEngine.getContract(symbol)
|
||||
|
||||
if contract:
|
||||
exchange = contract.exchange # 保证有交易所代码
|
||||
|
||||
self.lineName.setText(contract.name)#.decode('GBK'))
|
||||
|
||||
# 清空价格数量
|
||||
self.spinPrice.setValue(0)
|
||||
self.spinVolume.setValue(0)
|
||||
|
||||
# 清空行情显示
|
||||
self.labelBidPrice1.setText('')
|
||||
self.labelBidPrice2.setText('')
|
||||
self.labelBidPrice3.setText('')
|
||||
self.labelBidPrice4.setText('')
|
||||
self.labelBidPrice5.setText('')
|
||||
self.labelBidVolume1.setText('')
|
||||
self.labelBidVolume2.setText('')
|
||||
self.labelBidVolume3.setText('')
|
||||
self.labelBidVolume4.setText('')
|
||||
self.labelBidVolume5.setText('')
|
||||
self.labelAskPrice1.setText('')
|
||||
self.labelAskPrice2.setText('')
|
||||
self.labelAskPrice3.setText('')
|
||||
self.labelAskPrice4.setText('')
|
||||
self.labelAskPrice5.setText('')
|
||||
self.labelAskVolume1.setText('')
|
||||
self.labelAskVolume2.setText('')
|
||||
self.labelAskVolume3.setText('')
|
||||
self.labelAskVolume4.setText('')
|
||||
self.labelAskVolume5.setText('')
|
||||
self.labelLastPrice.setText('')
|
||||
self.labelReturn.setText('')
|
||||
|
||||
# 重新注册事件监听
|
||||
self.eventEngine.unregister(EVENT_TICK + self.symbol, self.signal.emit)
|
||||
self.eventEngine.register(EVENT_TICK + contract.vtSymbol, self.signal.emit)
|
||||
|
||||
# 订阅合约
|
||||
req = VtSubscribeReq()
|
||||
req.symbol = symbol
|
||||
req.exchange = exchange
|
||||
self.mainEngine.subscribe(req, contract.gatewayName)
|
||||
|
||||
# 更新组件当前交易的合约
|
||||
self.symbol = contract.vtSymbol
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def updateTick(self, event):
|
||||
"""更新行情"""
|
||||
tick = event.dict_['data']
|
||||
|
||||
if tick.vtSymbol == self.symbol:
|
||||
self.labelBidPrice1.setText(str(tick.bidPrice1))
|
||||
self.labelAskPrice1.setText(str(tick.askPrice1))
|
||||
self.labelBidVolume1.setText(str(tick.bidVolume1))
|
||||
self.labelAskVolume1.setText(str(tick.askVolume1))
|
||||
|
||||
if tick.bidPrice2:
|
||||
self.labelBidPrice2.setText(str(tick.bidPrice2))
|
||||
self.labelBidPrice3.setText(str(tick.bidPrice3))
|
||||
self.labelBidPrice4.setText(str(tick.bidPrice4))
|
||||
self.labelBidPrice5.setText(str(tick.bidPrice5))
|
||||
|
||||
self.labelAskPrice2.setText(str(tick.askPrice2))
|
||||
self.labelAskPrice3.setText(str(tick.askPrice3))
|
||||
self.labelAskPrice4.setText(str(tick.askPrice4))
|
||||
self.labelAskPrice5.setText(str(tick.askPrice5))
|
||||
|
||||
self.labelBidVolume2.setText(str(tick.bidVolume2))
|
||||
self.labelBidVolume3.setText(str(tick.bidVolume3))
|
||||
self.labelBidVolume4.setText(str(tick.bidVolume4))
|
||||
self.labelBidVolume5.setText(str(tick.bidVolume5))
|
||||
|
||||
self.labelAskVolume2.setText(str(tick.askVolume2))
|
||||
self.labelAskVolume3.setText(str(tick.askVolume3))
|
||||
self.labelAskVolume4.setText(str(tick.askVolume4))
|
||||
self.labelAskVolume5.setText(str(tick.askVolume5))
|
||||
|
||||
self.labelLastPrice.setText(str(tick.lastPrice))
|
||||
rt = (tick.lastPrice/tick.preClosePrice)-1
|
||||
self.labelReturn.setText(('%.2f' %(rt*100))+'%')
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def connectSignal(self):
|
||||
"""连接Signal"""
|
||||
self.signal.connect(self.updateTick)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def sendOrder(self):
|
||||
"""发单"""
|
||||
symbol = str(self.lineSymbol.text())
|
||||
exchange = str(self.comboExchange.currentText())
|
||||
|
||||
if exchange:
|
||||
vtSymbol = '.'.join([symbol, exchange])
|
||||
contract = self.dataEngine.getContract(vtSymbol)
|
||||
else:
|
||||
contract = self.dataEngine.getContract(symbol)
|
||||
|
||||
if contract:
|
||||
req = VtOrderReq()
|
||||
req.symbol = symbol
|
||||
req.exchange = contract.exchange
|
||||
req.price = self.spinPrice.value()
|
||||
req.volume = self.spinVolume.value()
|
||||
req.direction = unicode(self.comboDirection.currentText())
|
||||
req.priceType = unicode(self.comboPriceType.currentText())
|
||||
req.offset = unicode(self.comboOffset.currentText())
|
||||
self.mainEngine.sendOrder(req, contract.gatewayName)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def cancelAll(self):
|
||||
"""一键撤销所有委托"""
|
||||
l = self.dataEngine.getAllWorkingOrders()
|
||||
for order in l:
|
||||
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)
|
||||
|
||||
|
||||
########################################################################
|
||||
class ContractMonitor(BasicMonitor):
|
||||
"""合约查询"""
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self, dataEngine, parent=None):
|
||||
"""Constructor"""
|
||||
super(ContractMonitor, self).__init__(parent=parent)
|
||||
|
||||
self.dataEngine = dataEngine
|
||||
|
||||
d = OrderedDict()
|
||||
d['symbol'] = {'chinese':u'合约代码', 'cellType':BasicCell}
|
||||
d['exchange'] = {'chinese':u'交易所', 'cellType':BasicCell}
|
||||
d['vtSymbol'] = {'chinese':u'vt系统代码', 'cellType':BasicCell}
|
||||
d['name'] = {'chinese':u'名称', 'cellType':BasicCell}
|
||||
d['productClass'] = {'chinese':u'合约类型', 'cellType':BasicCell}
|
||||
d['size'] = {'chinese':u'大小', 'cellType':BasicCell}
|
||||
d['priceTick'] = {'chinese':u'最小价格变动', 'cellType':BasicCell}
|
||||
#d['strikePrice'] = {'chinese':u'期权行权价', 'cellType':BasicCell}
|
||||
#d['underlyingSymbol'] = {'chinese':u'期权标的物', 'cellType':BasicCell}
|
||||
#d['optionType'] = {'chinese':u'期权类型', 'cellType':BasicCell}
|
||||
self.setHeaderDict(d)
|
||||
|
||||
self.initUi()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def initUi(self):
|
||||
"""初始化界面"""
|
||||
self.setWindowTitle(u'合约查询')
|
||||
self.setMinimumSize(800, 800)
|
||||
self.setFont(BASIC_FONT)
|
||||
self.initTable()
|
||||
self.initMenu()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def showAllContracts(self):
|
||||
"""显示所有合约数据"""
|
||||
l = self.dataEngine.getAllContracts()
|
||||
d = {'.'.join([contract.exchange, contract.symbol]):contract for contract in l}
|
||||
l2 = d.keys()
|
||||
l2.sort(reverse=True)
|
||||
|
||||
self.setRowCount(len(l2))
|
||||
row = 0
|
||||
|
||||
for key in l2:
|
||||
contract = d[key]
|
||||
|
||||
for n, header in enumerate(self.headerList):
|
||||
content = safeUnicode(contract.__getattribute__(header))
|
||||
cellType = self.headerDict[header]['cellType']
|
||||
cell = cellType(content)
|
||||
|
||||
if self.font:
|
||||
cell.setFont(self.font) # 如果设置了特殊字体,则进行单元格设置
|
||||
|
||||
self.setItem(row, n, cell)
|
||||
|
||||
row = row + 1
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def refresh(self):
|
||||
"""刷新"""
|
||||
self.clearContents()
|
||||
self.setRowCount(0)
|
||||
self.showAllContracts()
|
||||
self.menu.close() # 关闭菜单
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def initMenu(self):
|
||||
"""初始化右键菜单"""
|
||||
refreshAction = QtGui.QAction(u'刷新', self)
|
||||
refreshAction.triggered.connect(self.refresh)
|
||||
|
||||
self.menu = QtGui.QMenu(self)
|
||||
self.menu.addAction(refreshAction)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def contextMenuEvent(self, event):
|
||||
"""右键点击事件"""
|
||||
self.menu.popup(QtGui.QCursor.pos())
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def show(self):
|
||||
"""显示"""
|
||||
super(ContractMonitor, self).show()
|
||||
self.refresh()
|
||||
|
||||
|
248
vn.trader/uiCtaWidget.py
Normal file
248
vn.trader/uiCtaWidget.py
Normal file
@ -0,0 +1,248 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
'''CTA模块相关的GUI控制组件'''
|
||||
|
||||
from uiBasicWidget import QtGui, QtCore, BasicCell
|
||||
from eventEngine import *
|
||||
|
||||
|
||||
########################################################################
|
||||
class ValueMonitor(QtGui.QTableWidget):
|
||||
"""数值监控"""
|
||||
signal = QtCore.pyqtSignal()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self, parent=None):
|
||||
"""Constructor"""
|
||||
super(ValueMonitor , self).__init__(parent)
|
||||
|
||||
self.keyCellDict = {}
|
||||
self.row = 0
|
||||
self.data = None
|
||||
|
||||
self.initUi()
|
||||
self.signal.connect(self.updateTable)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def initUi(self):
|
||||
"""初始化界面"""
|
||||
self.setColumnCount(2)
|
||||
|
||||
self.verticalHeader().setVisible(False)
|
||||
self.horizontalHeader().setVisible(False)
|
||||
|
||||
self.setEditTriggers(self.NoEditTriggers)
|
||||
self.setAlternatingRowColors(True)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def updateData(self, data):
|
||||
"""更新数据"""
|
||||
self.data = data
|
||||
self.signal.emit()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def updateTable(self):
|
||||
"""更新表格"""
|
||||
for key, value in self.data.items():
|
||||
if key in self.keyCellDict:
|
||||
cell = self.keyCellDict[key]
|
||||
cell.setText(unicode(value))
|
||||
else:
|
||||
# 创建并保存单元格
|
||||
keyCell = BasicCell(unicode(key))
|
||||
cell = BasicCell(unicode(value))
|
||||
self.keyCellDict[key] = cell
|
||||
|
||||
# 移动到下一行
|
||||
self.insertRow(self.row)
|
||||
self.setItem(self.row, 0, keyCell)
|
||||
self.setItem(self.row, 1, cell)
|
||||
self.row += 1
|
||||
|
||||
|
||||
########################################################################
|
||||
class CtaStrategyManager(QtGui.QGroupBox):
|
||||
"""策略管理组件"""
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self, ctaEngine, eventEngine, name, parent=None):
|
||||
"""Constructor"""
|
||||
super(CtaStrategyManager, self).__init__(parent)
|
||||
|
||||
self.ctaEngine = ctaEngine
|
||||
self.eventEngine = eventEngine
|
||||
self.name = name
|
||||
|
||||
self.initUi()
|
||||
self.updateMonitor()
|
||||
self.registerEvent()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def initUi(self):
|
||||
"""初始化界面"""
|
||||
self.setTitle(self.name)
|
||||
|
||||
paramLabel = QtGui.QLabel(u'参数')
|
||||
varLabel = QtGui.QLabel(u'变量')
|
||||
|
||||
self.paramMonitor = ValueMonitor(self)
|
||||
self.varMonitor = ValueMonitor(self)
|
||||
|
||||
buttonStart = QtGui.QPushButton(u'启动')
|
||||
buttonStop = QtGui.QPushButton(u'停止')
|
||||
buttonStart.clicked.connect(self.start)
|
||||
buttonStop.clicked.connect(self.stop)
|
||||
|
||||
hbox = QtGui.QHBoxLayout()
|
||||
hbox.addWidget(buttonStart)
|
||||
hbox.addWidget(buttonStop)
|
||||
hbox.addStretch()
|
||||
|
||||
vbox = QtGui.QVBoxLayout()
|
||||
vbox.addLayout(hbox)
|
||||
vbox.addWidget(paramLabel)
|
||||
vbox.addWidget(self.paramMonitor)
|
||||
vbox.addWidget(varLabel)
|
||||
vbox.addWidget(self.varMonitor)
|
||||
self.setLayout(vbox)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def updateMonitor(self, event=None):
|
||||
"""显示策略最新状态"""
|
||||
paramDict = self.ctaEngine.getStrategyParam(self.name)
|
||||
if paramDict:
|
||||
self.paramMonitor.updateData(paramDict)
|
||||
|
||||
varDict = self.ctaEngine.getStrategyVar(self.name)
|
||||
if varDict:
|
||||
self.varMonitor.updateData(varDict)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def registerEvent(self):
|
||||
"""注册事件监听"""
|
||||
self.eventEngine.register(EVENT_TIMER, self.updateMonitor)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def start(self):
|
||||
"""启动策略"""
|
||||
self.ctaEngine.startStrategy(self.name)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def stop(self):
|
||||
"""停止策略"""
|
||||
self.ctaEngine.stopStrategy(self.name)
|
||||
|
||||
|
||||
|
||||
########################################################################
|
||||
class CtaEngineManager(QtGui.QWidget):
|
||||
"""CTA引擎管理组件"""
|
||||
signal = QtCore.pyqtSignal(type(Event()))
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self, ctaEngine, eventEngine, parent=None):
|
||||
"""Constructor"""
|
||||
super(CtaEngineManager, self).__init__(parent)
|
||||
|
||||
self.ctaEngine = ctaEngine
|
||||
self.eventEngine = eventEngine
|
||||
|
||||
self.strategyLoaded = False
|
||||
|
||||
self.initUi()
|
||||
self.registerEvent()
|
||||
|
||||
# 记录日志
|
||||
self.ctaEngine.writeCtaLog(u'CTA引擎启动成功')
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def initUi(self):
|
||||
"""初始化界面"""
|
||||
self.setWindowTitle(u'CTA策略')
|
||||
|
||||
# 按钮
|
||||
loadButton = QtGui.QPushButton(u'加载策略')
|
||||
startAllButton = QtGui.QPushButton(u'全部启动')
|
||||
stopAllButton = QtGui.QPushButton(u'全部停止')
|
||||
|
||||
loadButton.clicked.connect(self.load)
|
||||
startAllButton.clicked.connect(self.startAll)
|
||||
stopAllButton.clicked.connect(self.stopAll)
|
||||
|
||||
# 滚动区域,放置所有的CtaStrategyManager
|
||||
self.scrollArea = QtGui.QScrollArea()
|
||||
|
||||
# CTA组件的日志监控
|
||||
self.ctaLogMonitor = QtGui.QTextEdit()
|
||||
self.ctaLogMonitor.setReadOnly(True)
|
||||
|
||||
# 设置布局
|
||||
hbox2 = QtGui.QHBoxLayout()
|
||||
hbox2.addWidget(loadButton)
|
||||
hbox2.addWidget(startAllButton)
|
||||
hbox2.addWidget(stopAllButton)
|
||||
hbox2.addStretch()
|
||||
|
||||
vbox = QtGui.QVBoxLayout()
|
||||
vbox.addLayout(hbox2)
|
||||
vbox.addWidget(self.scrollArea)
|
||||
vbox.addWidget(self.ctaLogMonitor)
|
||||
self.setLayout(vbox)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def initStrategyManager(self):
|
||||
"""初始化策略管理组件界面"""
|
||||
w = QtGui.QWidget()
|
||||
hbox = QtGui.QHBoxLayout()
|
||||
|
||||
for name in self.ctaEngine.strategyDict.keys():
|
||||
strategyManager = CtaStrategyManager(self.ctaEngine, self.eventEngine, name)
|
||||
hbox.addWidget(strategyManager)
|
||||
|
||||
w.setLayout(hbox)
|
||||
self.scrollArea.setWidget(w)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def startAll(self):
|
||||
"""全部启动"""
|
||||
for name in self.ctaEngine.strategyDict.keys():
|
||||
self.ctaEngine.startStrategy(name)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def stopAll(self):
|
||||
"""全部停止"""
|
||||
for name in self.ctaEngine.strategyDict.keys():
|
||||
self.ctaEngine.stopStrategy(name)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def load(self):
|
||||
"""加载策略"""
|
||||
if not self.strategyLoaded:
|
||||
self.ctaEngine.loadStrategySetting()
|
||||
self.initStrategyManager()
|
||||
self.strategyLoaded = True
|
||||
self.ctaEngine.writeCtaLog(u'策略加载成功')
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def updateCtaLog(self, event):
|
||||
"""更新CTA相关日志"""
|
||||
log = event.dict_['data']
|
||||
content = '\t'.join([log.logTime, log.logContent])
|
||||
self.ctaLogMonitor.append(content)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def registerEvent(self):
|
||||
"""注册事件监听"""
|
||||
self.signal.connect(self.updateCtaLog)
|
||||
self.eventEngine.register(EVENT_CTA_LOG, self.signal.emit)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
289
vn.trader/uiMainWindow.py
Normal file
289
vn.trader/uiMainWindow.py
Normal file
@ -0,0 +1,289 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
import psutil
|
||||
|
||||
from uiBasicWidget import *
|
||||
from uiCtaWidget import CtaEngineManager
|
||||
|
||||
########################################################################
|
||||
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)
|
||||
|
||||
ctaAction = QtGui.QAction(u'CTA策略', self)
|
||||
ctaAction.triggered.connect(self.openCta)
|
||||
|
||||
# 创建菜单
|
||||
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)
|
||||
functionMenu.addAction(ctaAction)
|
||||
|
||||
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 openCta(self):
|
||||
"""打开CTA组件"""
|
||||
try:
|
||||
self.ctaM.show()
|
||||
except AttributeError:
|
||||
self.ctaM = CtaEngineManager(self.mainEngine.ctaEngine, self.eventEngine)
|
||||
self.ctaM.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的交易员
|
||||
|
||||
License:MIT
|
||||
|
||||
主页:vnpy.org
|
||||
|
||||
Github:github.com/vnpy/vnpy
|
||||
|
||||
QQ交流群:262656087
|
||||
|
||||
|
||||
|
||||
|
||||
开发环境
|
||||
|
||||
操作系统:Windows 7 专业版 64位
|
||||
|
||||
Python发行版:Python 2.7.6 (Anaconda 1.9.2 Win-32)
|
||||
|
||||
CTP:vn.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
BIN
vn.trader/vnltsmd.pyd
Normal file
Binary file not shown.
BIN
vn.trader/vnltsqry.pyd
Normal file
BIN
vn.trader/vnltsqry.pyd
Normal file
Binary file not shown.
BIN
vn.trader/vnltstd.pyd
Normal file
BIN
vn.trader/vnltstd.pyd
Normal file
Binary file not shown.
BIN
vn.trader/vnpy.ico
Normal file
BIN
vn.trader/vnpy.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
57
vn.trader/vtConstant.py
Normal file
57
vn.trader/vtConstant.py
Normal 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 = '' # 空交易所
|
@ -1,8 +1,17 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
import shelve
|
||||
|
||||
from pymongo import MongoClient
|
||||
from pymongo.errors import ConnectionFailure
|
||||
|
||||
from eventEngine import *
|
||||
from ctpGateway import CtpGateway
|
||||
from ltsGateway import LtsGateway
|
||||
from windGateway import WindGateway
|
||||
from vtGateway import *
|
||||
import uiBasicWidget
|
||||
from ctaEngine import CtaEngine
|
||||
|
||||
|
||||
########################################################################
|
||||
@ -16,12 +25,28 @@ class MainEngine(object):
|
||||
self.eventEngine = EventEngine()
|
||||
self.eventEngine.start()
|
||||
|
||||
# 创建数据引擎
|
||||
self.dataEngine = DataEngine(self.eventEngine)
|
||||
uiBasicWidget.NameCell.setDataEngine(uiBasicWidget.NameCell, self.dataEngine) # 将数据引擎对象传给NameCell
|
||||
|
||||
# 用来保存接口对象的字典
|
||||
self.gatewayDict = {}
|
||||
|
||||
# 创建我们想要接入的接口对象
|
||||
self.addGateway(CtpGateway, 'CTP')
|
||||
self.gatewayDict['CTP'].setQryEnabled(True)
|
||||
|
||||
self.addGateway(LtsGateway, 'LTS')
|
||||
self.gatewayDict['LTS'].setQryEnabled(True)
|
||||
|
||||
self.addGateway(WindGateway, 'Wind') # 没有Wind的请注释掉这一行
|
||||
|
||||
# MongoDB数据库相关
|
||||
self.dbClient = None # MongoDB客户端对象
|
||||
|
||||
# CTA引擎
|
||||
self.ctaEngine = CtaEngine(self, self.eventEngine, self.dataEngine)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def addGateway(self, gateway, gatewayName=None):
|
||||
"""创建接口"""
|
||||
@ -30,45 +55,203 @@ class MainEngine(object):
|
||||
#----------------------------------------------------------------------
|
||||
def connect(self, gatewayName):
|
||||
"""连接特定名称的接口"""
|
||||
gateway = self.gatewayDict[gatewayName]
|
||||
gateway.connect()
|
||||
if gatewayName in self.gatewayDict:
|
||||
gateway = self.gatewayDict[gatewayName]
|
||||
gateway.connect()
|
||||
else:
|
||||
self.writeLog(u'接口不存在:%s' %gatewayName)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def subscribe(self, subscribeReq, gatewayName):
|
||||
"""订阅特定接口的行情"""
|
||||
gateway = self.gatewayDict[gatewayName]
|
||||
gateway.subscribe(subscribeReq)
|
||||
if gatewayName in self.gatewayDict:
|
||||
gateway = self.gatewayDict[gatewayName]
|
||||
gateway.subscribe(subscribeReq)
|
||||
else:
|
||||
self.writeLog(u'接口不存在:%s' %gatewayName)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def sendOrder(self, orderReq, gatewayName):
|
||||
"""对特定接口发单"""
|
||||
gateway = self.gatewayDict[gatewayName]
|
||||
return gateway.sendOrder(orderReq)
|
||||
if gatewayName in self.gatewayDict:
|
||||
gateway = self.gatewayDict[gatewayName]
|
||||
return gateway.sendOrder(orderReq)
|
||||
else:
|
||||
self.writeLog(u'接口不存在:%s' %gatewayName)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def cancelOrder(self, cancelOrderReq, gatewayName):
|
||||
"""对特定接口撤单"""
|
||||
gateway = self.gatewayDict[gatewayName]
|
||||
gateway.cancelOrder(cancelOrderReq)
|
||||
if gatewayName in self.gatewayDict:
|
||||
gateway = self.gatewayDict[gatewayName]
|
||||
gateway.cancelOrder(cancelOrderReq)
|
||||
else:
|
||||
self.writeLog(u'接口不存在:%s' %gatewayName)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def getAccont(self, gatewayName):
|
||||
"""查询特定接口的账户"""
|
||||
gateway = self.gatewayDict[gatewayName]
|
||||
gateway.getAccount()
|
||||
if gatewayName in self.gatewayDict:
|
||||
gateway = self.gatewayDict[gatewayName]
|
||||
gateway.getAccount()
|
||||
else:
|
||||
self.writeLog(u'接口不存在:%s' %gatewayName)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def getPosition(self, gatewayName):
|
||||
"""查询特定接口的持仓"""
|
||||
gateway = self.gatewayDict[gatewayName]
|
||||
gateway.getPosition()
|
||||
if gatewayName in self.gatewayDict:
|
||||
gateway = self.gatewayDict[gatewayName]
|
||||
gateway.getPosition()
|
||||
else:
|
||||
self.writeLog(u'接口不存在:%s' %gatewayName)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def exit(self):
|
||||
"""退出程序前调用,保证正常退出"""
|
||||
# 停止事件引擎
|
||||
self.eventEngine.stop()
|
||||
|
||||
"""退出程序前调用,保证正常退出"""
|
||||
# 安全关闭所有接口
|
||||
for gateway in self.gatewayDict.values():
|
||||
gateway.close()
|
||||
for gateway in self.gatewayDict.values():
|
||||
gateway.close()
|
||||
|
||||
# 停止事件引擎
|
||||
self.eventEngine.stop()
|
||||
|
||||
# 保存数据引擎里的合约数据到硬盘
|
||||
self.dataEngine.saveContracts()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def writeLog(self, content):
|
||||
"""快速发出日志事件"""
|
||||
log = VtLogData()
|
||||
log.logContent = content
|
||||
event = Event(type_=EVENT_LOG)
|
||||
event.dict_['data'] = log
|
||||
self.eventEngine.put(event)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def dbConnect(self):
|
||||
"""连接MongoDB数据库"""
|
||||
if not self.dbClient:
|
||||
try:
|
||||
self.dbClient = MongoClient()
|
||||
self.writeLog(u'MongoDB连接成功')
|
||||
except ConnectionFailure:
|
||||
self.writeLog(u'MongoDB连接失败')
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def dbInsert(self, dbName, collectionName, d):
|
||||
"""向MongoDB中插入数据,d是具体数据"""
|
||||
if self.dbClient:
|
||||
db = self.dbClient[dbName]
|
||||
collection = db[collectionName]
|
||||
collection.insert(d)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def dbQuery(self, dbName, collectionName, d):
|
||||
"""从MongoDB中读取数据,d是查询要求,返回的是数据库查询的指针"""
|
||||
if self.dbClient:
|
||||
db = self.dbClient[dbName]
|
||||
collection = db[collectionName]
|
||||
cursor = collection.find(d)
|
||||
return cursor
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
########################################################################
|
||||
class DataEngine(object):
|
||||
"""数据引擎"""
|
||||
contractFileName = 'ContractData.vt'
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self, eventEngine):
|
||||
"""Constructor"""
|
||||
self.eventEngine = eventEngine
|
||||
|
||||
# 保存合约详细信息的字典
|
||||
self.contractDict = {}
|
||||
|
||||
# 保存委托数据的字典
|
||||
self.orderDict = {}
|
||||
|
||||
# 保存活动委托数据的字典(即可撤销)
|
||||
self.workingOrderDict = {}
|
||||
|
||||
# 读取保存在硬盘的合约数据
|
||||
self.loadContracts()
|
||||
|
||||
# 注册事件监听
|
||||
self.registerEvent()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def updateContract(self, event):
|
||||
"""更新合约数据"""
|
||||
contract = event.dict_['data']
|
||||
self.contractDict[contract.vtSymbol] = contract
|
||||
self.contractDict[contract.symbol] = contract # 使用常规代码(不包括交易所)可能导致重复
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def getContract(self, vtSymbol):
|
||||
"""查询合约对象"""
|
||||
try:
|
||||
return self.contractDict[vtSymbol]
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def getAllContracts(self):
|
||||
"""查询所有合约对象(返回列表)"""
|
||||
return self.contractDict.values()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def saveContracts(self):
|
||||
"""保存所有合约对象到硬盘"""
|
||||
f = shelve.open(self.contractFileName)
|
||||
f['data'] = self.contractDict
|
||||
f.close()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def loadContracts(self):
|
||||
"""从硬盘读取合约对象"""
|
||||
f = shelve.open(self.contractFileName)
|
||||
if 'data' in f:
|
||||
d = f['data']
|
||||
for key, value in d.items():
|
||||
self.contractDict[key] = value
|
||||
f.close()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def updateOrder(self, event):
|
||||
"""更新委托数据"""
|
||||
order = event.dict_['data']
|
||||
self.orderDict[order.vtOrderID] = order
|
||||
|
||||
# 如果订单的状态是全部成交或者撤销,则需要从workingOrderDict中移除
|
||||
if order.status == STATUS_ALLTRADED or order.status == STATUS_CANCELLED:
|
||||
if order.vtOrderID in self.workingOrderDict:
|
||||
del self.workingOrderDict[order.vtOrderID]
|
||||
# 否则则更新字典中的数据
|
||||
else:
|
||||
self.workingOrderDict[order.vtOrderID] = order
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def getOrder(self, vtOrderID):
|
||||
"""查询委托"""
|
||||
try:
|
||||
return self.orderDict[vtOrderID]
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def getAllWorkingOrders(self):
|
||||
"""查询所有活动委托(返回列表)"""
|
||||
return self.workingOrderDict.values()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def registerEvent(self):
|
||||
"""注册事件监听"""
|
||||
self.eventEngine.register(EVENT_CONTRACT, self.updateContract)
|
||||
self.eventEngine.register(EVENT_ORDER, self.updateOrder)
|
||||
|
||||
|
||||
|
26
vn.trader/vtFunction.py
Normal file
26
vn.trader/vtFunction.py
Normal 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)
|
@ -4,48 +4,7 @@ import time
|
||||
|
||||
from eventEngine import *
|
||||
|
||||
# 默认空值
|
||||
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'组合'
|
||||
|
||||
# 价格类型常量
|
||||
PRICETYPE_LIMITPRICE = u'限价'
|
||||
PRICETYPE_MARKETPRICE = u'市价'
|
||||
|
||||
# 期权类型
|
||||
OPTION_CALL = u'看涨期权'
|
||||
OPTION_PUT = u'看跌期权'
|
||||
from vtConstant import *
|
||||
|
||||
|
||||
########################################################################
|
||||
@ -200,18 +159,24 @@ class VtTickData(VtBaseData):
|
||||
|
||||
# 代码相关
|
||||
self.symbol = EMPTY_STRING # 合约代码
|
||||
self.vtSymbol = EMPTY_STRING # 合约在vt系统中的唯一代码,通常是 Gateway名.合约代码
|
||||
self.exchange = EMPTY_STRING # 交易所代码
|
||||
self.vtSymbol = EMPTY_STRING # 合约在vt系统中的唯一代码,通常是 合约代码.交易所代码
|
||||
|
||||
# 成交数据
|
||||
self.lastPrice = EMPTY_FLOAT # 最新成交价
|
||||
self.volume = EMPTY_INT # 最新成交量
|
||||
self.openInterest = EMPTY_INT # 持仓量
|
||||
self.tickTime = EMPTY_STRING # 更新时间
|
||||
self.time = EMPTY_STRING # 时间 11:20:56.5
|
||||
self.date = EMPTY_STRING # 日期 20151009
|
||||
|
||||
# 常规行情
|
||||
self.openPrice = EMPTY_FLOAT # 今日开盘价
|
||||
self.highPrice = EMPTY_FLOAT # 今日最高价
|
||||
self.lowPrice = EMPTY_FLOAT # 今日最低价
|
||||
self.preClosePrice = EMPTY_FLOAT
|
||||
|
||||
self.upperLimit = EMPTY_FLOAT # 涨停价
|
||||
self.lowerLimit = EMPTY_FLOAT # 跌停价
|
||||
|
||||
# 五档行情
|
||||
self.bidPrice1 = EMPTY_FLOAT
|
||||
@ -250,7 +215,8 @@ class VtTradeData(VtBaseData):
|
||||
|
||||
# 代码编号相关
|
||||
self.symbol = EMPTY_STRING # 合约代码
|
||||
self.vtSymbol = EMPTY_STRING # 合约在vt系统中的唯一代码,通常是 Gateway名.合约代码
|
||||
self.exchange = EMPTY_STRING # 交易所代码
|
||||
self.vtSymbol = EMPTY_STRING # 合约在vt系统中的唯一代码,通常是 合约代码.交易所代码
|
||||
|
||||
self.tradeID = EMPTY_STRING # 成交编号
|
||||
self.vtTradeID = EMPTY_STRING # 成交在vt系统中的唯一编号,通常是 Gateway名.成交编号
|
||||
@ -277,7 +243,8 @@ class VtOrderData(VtBaseData):
|
||||
|
||||
# 代码编号相关
|
||||
self.symbol = EMPTY_STRING # 合约代码
|
||||
self.vtSymbol = EMPTY_STRING # 合约在vt系统中的唯一代码,通常是 Gateway名.合约代码
|
||||
self.exchange = EMPTY_STRING # 交易所代码
|
||||
self.vtSymbol = EMPTY_STRING # 合约在vt系统中的唯一代码,通常是 合约代码.交易所代码
|
||||
|
||||
self.orderID = EMPTY_STRING # 订单编号
|
||||
self.vtOrderID = EMPTY_STRING # 订单在vt系统中的唯一编号,通常是 Gateway名.订单编号
|
||||
@ -309,7 +276,8 @@ class VtPositionData(VtBaseData):
|
||||
|
||||
# 代码编号相关
|
||||
self.symbol = EMPTY_STRING # 合约代码
|
||||
self.vtSymbol = EMPTY_STRING # 合约在vt系统中的唯一代码,通常是 Gateway名.合约代码
|
||||
self.exchange = EMPTY_STRING # 交易所代码
|
||||
self.vtSymbol = EMPTY_STRING # 合约在vt系统中的唯一代码,合约代码.交易所代码
|
||||
|
||||
# 持仓相关
|
||||
self.direction = EMPTY_STRING # 持仓方向
|
||||
@ -353,6 +321,7 @@ class VtErrorData(VtBaseData):
|
||||
|
||||
self.errorID = EMPTY_STRING # 错误代码
|
||||
self.errorMsg = EMPTY_UNICODE # 错误信息
|
||||
self.additionalInfo = EMPTY_UNICODE # 补充信息
|
||||
|
||||
|
||||
########################################################################
|
||||
@ -378,7 +347,8 @@ class VtContractData(VtBaseData):
|
||||
super(VtBaseData, self).__init__()
|
||||
|
||||
self.symbol = EMPTY_STRING # 代码
|
||||
self.vtSymbol = EMPTY_STRING # 合约在vt系统中的唯一代码,通常是 Gateway名.合约代码
|
||||
self.exchange = EMPTY_STRING # 交易所代码
|
||||
self.vtSymbol = EMPTY_STRING # 合约在vt系统中的唯一代码,通常是 合约代码.交易所代码
|
||||
self.name = EMPTY_UNICODE # 合约中文名
|
||||
|
||||
self.productClass = EMPTY_UNICODE # 合约类型
|
||||
@ -409,13 +379,14 @@ class VtOrderReq:
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self):
|
||||
"""Constructor"""
|
||||
self.symbol = EMPTY_STRING
|
||||
self.price = EMPTY_FLOAT
|
||||
self.volume = EMPTY_INT
|
||||
|
||||
self.priceType = EMPTY_STRING
|
||||
self.direction = EMPTY_STRING
|
||||
self.offset = EMPTY_STRING
|
||||
self.symbol = EMPTY_STRING # 代码
|
||||
self.exchange = EMPTY_STRING # 交易所
|
||||
self.price = EMPTY_FLOAT # 价格
|
||||
self.volume = EMPTY_INT # 数量
|
||||
|
||||
self.priceType = EMPTY_STRING # 价格类型
|
||||
self.direction = EMPTY_STRING # 买卖
|
||||
self.offset = EMPTY_STRING # 开平
|
||||
|
||||
|
||||
########################################################################
|
||||
@ -425,13 +396,13 @@ class VtCancelOrderReq:
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self):
|
||||
"""Constructor"""
|
||||
self.symbol = EMPTY_STRING
|
||||
self.exchange = EMPTY_STRING
|
||||
self.symbol = EMPTY_STRING # 代码
|
||||
self.exchange = EMPTY_STRING # 交易所
|
||||
|
||||
# 以下字段主要和CTP、LTS类接口相关
|
||||
self.orderRef = EMPTY_STRING
|
||||
self.frontID = EMPTY_STRING
|
||||
self.sessionID = EMPTY_STRING
|
||||
self.orderID = EMPTY_STRING # 报单号
|
||||
self.frontID = EMPTY_STRING # 前置机号
|
||||
self.sessionID = EMPTY_STRING # 会话号
|
||||
|
||||
|
||||
|
||||
|
26
vn.trader/vtMain.py
Normal file
26
vn.trader/vtMain.py
Normal 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
183
vn.trader/windGateway.py
Normal 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)
|
Loading…
Reference in New Issue
Block a user