增加:

1、自动根据持仓合约,订阅tick消息。
2、风控增加亏损比例监控(暂时还没有确定的强制平仓处理)
This commit is contained in:
msincenselee 2016-11-30 14:28:30 +08:00
parent f37440e6bb
commit c7f6e1e9a5
8 changed files with 175 additions and 83 deletions

View File

@ -11,6 +11,8 @@ import os
import copy
from collections import OrderedDict
from datetime import datetime, timedelta
from Queue import Queue
from threading import Thread
from eventEngine import *
from vtGateway import VtSubscribeReq, VtLogData
@ -43,6 +45,11 @@ class DrEngine(object):
# K线对象字典
self.barDict = {}
# 负责执行数据库插入的单独线程相关
self.active = False # 工作状态
self.queue = Queue() # 队列
self.thread = Thread(target=self.run) # 线程
# 载入设置,订阅行情
self.loadSetting()
@ -112,6 +119,9 @@ class DrEngine(object):
for activeSymbol, vtSymbol in d.items():
self.activeSymbolDict[vtSymbol] = activeSymbol
# 启动数据插入线程
self.start()
# 注册事件监听
self.registerEvent()
@ -187,7 +197,29 @@ class DrEngine(object):
#----------------------------------------------------------------------
def insertData(self, dbName, collectionName, data):
"""插入数据到数据库这里的data可以是CtaTickData或者CtaBarData"""
self.mainEngine.dbInsert(dbName, collectionName, data.__dict__)
self.queue.put((dbName, collectionName, data.__dict__))
#----------------------------------------------------------------------
def run(self):
"""运行插入线程"""
while self.active:
try:
dbName, collectionName, d = self.queue.get(block=True, timeout=1)
self.mainEngine.dbInsert(dbName, collectionName, d)
except Empty:
pass
#----------------------------------------------------------------------
def start(self):
"""启动"""
self.active = True
self.thread.start()
#----------------------------------------------------------------------
def stop(self):
"""退出"""
if self.active:
self.active = False
self.thread.join()
#----------------------------------------------------------------------
def writeDrLog(self, content):

View File

@ -19,9 +19,9 @@ EVENT_LOG = 'eLog' # 日志事件,全局通用
EVENT_TICK = 'eTick.' # TICK行情事件可后接具体的vtSymbol
EVENT_TRADE = 'eTrade.' # 成交回报事件
EVENT_ORDER = 'eOrder.' # 报单回报事件
EVENT_ERRRTNORDERINSERT = 'eErrRtnOrderInsert' # 报单录入错误回报事件
EVENT_POSITION = 'ePosition.' # 持仓回报事件
EVENT_ACCOUNT = 'eAccount.' # 账户回报事件
EVENT_ACCOUNT_LOSS = 'eAccountLoss' # 账户亏损事件
EVENT_CONTRACT = 'eContract.' # 合约基础信息回报事件
EVENT_ERROR = 'eError.' # 错误回报事件

View File

@ -1,9 +1,10 @@
{
"orderFlowClear": 10,
"percentLimit": 90,
"percentLimit": 80,
"workingOrderLimit": 200,
"tradeLimit": 1000,
"tradeLimit": 20000,
"orderSizeLimit": 100,
"active": true,
"orderFlowLimit": 1000
"lossPercentLimit": 11,
"orderFlowLimit": 20000
}

View File

@ -23,9 +23,6 @@ class RmSpinBox(QtGui.QSpinBox):
self.setValue(value)
########################################################################
class RmLine(QtGui.QFrame):
"""水平分割线"""
@ -38,8 +35,6 @@ class RmLine(QtGui.QFrame):
self.setFrameShadow(self.Sunken)
########################################################################
class RmEngineManager(QtGui.QWidget):
"""风控引擎的管理组件"""
@ -72,6 +67,9 @@ class RmEngineManager(QtGui.QWidget):
# 最大开仓比例
self.spinPercentLimit = RmSpinBox(self.rmEngine.percentLimit)
# 最大净值止损比例,满足后强制止损
self.spinLossPercentLimit = RmSpinBox(self.rmEngine.lossPercentLimit)
buttonClearOrderFlowCount = QtGui.QPushButton(u'清空流控计数')
buttonClearTradeCount = QtGui.QPushButton(u'清空总成交计数')
buttonSaveSetting = QtGui.QPushButton(u'保存设置')
@ -98,6 +96,8 @@ class RmEngineManager(QtGui.QWidget):
grid.addWidget(RmLine(), 10, 0, 1, 2)
grid.addWidget(Label(u'仓位上限(1~100)'), 11, 0)
grid.addWidget(self.spinPercentLimit, 11, 1)
grid.addWidget(Label(u'强制止损比例'), 12, 0)
grid.addWidget(self.spinLossPercentLimit, 12, 1)
hbox = QtGui.QHBoxLayout()
hbox.addWidget(buttonClearOrderFlowCount)
@ -117,6 +117,7 @@ class RmEngineManager(QtGui.QWidget):
self.spinTradeLimit.valueChanged.connect(self.rmEngine.setTradeLimit)
self.spinWorkingOrderLimit.valueChanged.connect(self.rmEngine.setWorkingOrderLimit)
self.spinPercentLimit.valueChanged.connect(self.rmEngine.setAccountPercentLimit)
self.spinLossPercentLimit.valueChanged.connect(self.rmEngine.setLossPercentLimit)
self.buttonSwitchEngineStatus.clicked.connect(self.switchEngineSatus)
buttonClearOrderFlowCount.clicked.connect(self.rmEngine.clearOrderFlowCount)

View File

@ -610,8 +610,6 @@ class PositionMonitor(BasicMonitor):
self.initTable()
self.registerEvent()
########################################################################
class AccountMonitor(BasicMonitor):
"""账户监控"""
@ -639,7 +637,6 @@ class AccountMonitor(BasicMonitor):
self.initTable()
self.registerEvent()
########################################################################
class TradingWidget(QtGui.QFrame):
"""简单交易组件"""
@ -745,7 +742,6 @@ class TradingWidget(QtGui.QFrame):
self.comboPriceType.addItems(self.priceTypeList)
self.comboExchange = QtGui.QComboBox()
self.comboExchange.addItems(self.exchangeList)
self.comboCurrency = QtGui.QComboBox()

View File

@ -23,8 +23,10 @@ class MainWindow(QtGui.QMainWindow):
self.widgetDict = {} # 用来保存子窗口的字典
self.connectGatewayDict = {}
self.initUi()
self.loadWindowSettings()
#self.loadWindowSettings()
self.connected = False
self.autoDisConnect = False
@ -32,11 +34,13 @@ class MainWindow(QtGui.QMainWindow):
self.orderSaveDate = EMPTY_STRING
self.barSaveDate = EMPTY_STRING
self.connectGatewayDict = {}
# ----------------------------------------------------------------------
def initUi(self):
"""初始化界面"""
self.setWindowTitle('VnTrader')
path = os.getcwd().rsplit('\\')[-1]
self.setWindowTitle(path)
self.initCentral()
self.initMenu()
self.initStatusBar()
@ -196,9 +200,15 @@ class MainWindow(QtGui.QMainWindow):
self.sbTrigger = 10 # 10秒刷新一次
self.eventEngine.register(EVENT_TIMER, self.updateStatusBar)
# ----------------------------------------------------------------------
def updateStatusBar(self, event):
"""在状态栏更新CPU和内存信息"""
"""1、在状态栏更新CPU和内存信息"""
# 2、定时断开服务器连接
# 3、定时重连服务器
# 4、定时保存每日的委托单
# 5、定时执行策略的保存事件
self.sbCount += 1
# 更新任务栏
@ -211,7 +221,7 @@ class MainWindow(QtGui.QMainWindow):
self.statusLabel.setText(info)
if len(self.connectGatewayDict) > 0:
if self.connectGatewayDict:
s = u''.join(str(e) for e in self.connectGatewayDict.values())
if not self.connected:
@ -266,7 +276,8 @@ class MainWindow(QtGui.QMainWindow):
# 调用各策略保存数据
if ((dt.hour == 15 and dt.minute == 1) or (dt.hour == 2 and dt.minute == 31)) \
and len(self.connectGatewayDict) > 0 \
and today != self.barSaveDate:
and today != self.barSaveDate \
and self.connected:
self.barSaveDate = today
self.mainEngine.writeLog(u'调用各策略保存数据')
self.mainEngine.saveData()
@ -491,7 +502,6 @@ class MainWindow(QtGui.QMainWindow):
pass
def disconnect(self):
""""断开底层gateway的连接"""
self.mainEngine.disconnect()

View File

@ -78,6 +78,7 @@ EXCHANGE_GLOBEX = 'GLOBEX' # CME电子交易平台
EXCHANGE_IDEALPRO = 'IDEALPRO' # IB外汇ECN
EXCHANGE_OANDA = 'OANDA' # OANDA外汇做市商
EXCHANGE_OKCOIN = 'OKCOIN' # OKCOIN比特币交易所
# 货币类型
CURRENCY_USD = 'USD' # 美元

View File

@ -28,7 +28,7 @@ class MainEngine(object):
self.eventEngine.start()
# 创建数据引擎
self.dataEngine = DataEngine(self.eventEngine)
self.dataEngine = DataEngine(self, self.eventEngine)
# MongoDB数据库相关
self.dbClient = None # MongoDB客户端对象
@ -61,7 +61,7 @@ class MainEngine(object):
self.addGateway(CtpGateway, 'CTP_EBF')
self.gatewayDict['CTP_EBF'].setQryEnabled(True)
except Exception, e:
except Exception as e:
print e
"""
@ -125,8 +125,16 @@ class MainEngine(object):
self.gatewayDict['OANDA'].setQryEnabled(True)
except Exception, e:
print e
try:
from okcoinGateway.okcoinGateway import OkcoinGateway
self.addGateway(OkcoinGateway, 'OKCOIN')
self.gatewayDict['OKCOIN'].setQryEnabled(True)
except Exception, e:
print e
"""
# ----------------------------------------------------------------------
#----------------------------------------------------------------------
def addGateway(self, gateway, gatewayName=None):
"""创建接口"""
self.gatewayDict[gatewayName] = gateway(self.eventEngine, gatewayName)
@ -208,6 +216,9 @@ class MainEngine(object):
# 停止事件引擎
self.eventEngine.stop()
# 停止数据记录引擎
self.drEngine.stop()
# 保存数据引擎里的合约数据到硬盘
self.dataEngine.saveContracts()
@ -288,6 +299,7 @@ class MainEngine(object):
def clearData(self):
"""清空数据引擎的数据"""
self.dataEngine.clearData()
self.ctaEngine.clearData()
def saveData(self):
self.ctaEngine.saveStrategyData()
@ -298,8 +310,9 @@ class DataEngine(object):
contractFileName = 'ContractData.vt'
# ----------------------------------------------------------------------
def __init__(self, eventEngine):
def __init__(self, mainEngine, eventEngine):
"""Constructor"""
self.mainEngine = mainEngine
self.eventEngine = eventEngine
# 保存合约详细信息的字典
@ -317,6 +330,9 @@ class DataEngine(object):
# 注册事件监听
self.registerEvent()
# 已订阅合约代码
self.subscribedSymbols = set()
# ----------------------------------------------------------------------
def updateContract(self, event):
"""更新合约数据"""
@ -386,10 +402,45 @@ class DataEngine(object):
"""注册事件监听"""
self.eventEngine.register(EVENT_CONTRACT, self.updateContract)
self.eventEngine.register(EVENT_ORDER, self.updateOrder)
self.eventEngine.register(EVENT_POSITION, self.updatePosition)
def clearData(self):
"""清空数据"""
self.orderDict = {}
self.workingOrderDict = {}
self.subscribedSymbols.clear()
def updatePosition(self,event):
"""更新持仓信息"""
# 在获取更新持仓信息时自动订阅这个symbol
# 目的1、
position = event.dict_['data']
symbol = position.symbol
# 已存在,不做更新
if symbol in self.subscribedSymbols:
return
self.subscribedSymbols.add(symbol)
gatewayName = position.gatewayName
contract = self.mainEngine.getContract(symbol)
if not contract:
self.mainEngine.writeLog(u'找不到合约{0}信息'.format(symbol))
return
# 订阅合约
req = VtSubscribeReq()
req.symbol = symbol
req.exchange = contract.exchange
req.currency = ''
req.productClass = ''
self.mainEngine.subscribe(req, gatewayName)
self.mainEngine.writeLog(u'自动订阅合约{0}'.format(symbol))