diff --git a/vn.sgit/vnsgitmd/vnsgitmd/vnsgitmd.cpp b/vn.sgit/vnsgitmd/vnsgitmd/vnsgitmd.cpp index e1d7457e..8e49e8ae 100644 --- a/vn.sgit/vnsgitmd/vnsgitmd/vnsgitmd.cpp +++ b/vn.sgit/vnsgitmd/vnsgitmd/vnsgitmd.cpp @@ -114,7 +114,7 @@ void MdApi::OnFrontDisconnected(char *pErrMsg) if (pErrMsg) { - task.task_data = *pErrMsg; + task.task_data = string(pErrMsg); } else { diff --git a/vn.sgit/vnsgittd/vnsgittd/vnsgittd.cpp b/vn.sgit/vnsgittd/vnsgittd/vnsgittd.cpp index 7c6bfbad..708e9130 100644 --- a/vn.sgit/vnsgittd/vnsgittd/vnsgittd.cpp +++ b/vn.sgit/vnsgittd/vnsgittd/vnsgittd.cpp @@ -114,7 +114,7 @@ void TdApi::OnFrontDisconnected(char *pErrMsg) if (pErrMsg) { - task.task_data = *pErrMsg; + task.task_data = string(pErrMsg); } else { diff --git a/vn.trader/ibGateway/ibGateway.py b/vn.trader/ibGateway/ibGateway.py index ad832bda..2e992c23 100644 --- a/vn.trader/ibGateway/ibGateway.py +++ b/vn.trader/ibGateway/ibGateway.py @@ -323,7 +323,6 @@ class IbWrapper(EWrapper): #---------------------------------------------------------------------- def orderStatus(self, orderId, status, filled, remaining, avgFillPrice, permId, parentId, lastFillPrice, clientId, whyHeld): """报单成交回报""" - pass orderId = str(orderId) if orderId in self.orderDict: diff --git a/vn.trader/riskManager/RM_setting.json b/vn.trader/riskManager/RM_setting.json new file mode 100644 index 00000000..f687669b --- /dev/null +++ b/vn.trader/riskManager/RM_setting.json @@ -0,0 +1,8 @@ +{ + "orderFlowClear": 1, + "workingOrderLimit": 0, + "tradeLimit": 200, + "orderSizeLimit": 10, + "active": true, + "orderFlowLimit": 10 +} \ No newline at end of file diff --git a/vn.trader/riskManager/__init__.py b/vn.trader/riskManager/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/vn.trader/riskManager/rmEngine.py b/vn.trader/riskManager/rmEngine.py new file mode 100644 index 00000000..21e3965e --- /dev/null +++ b/vn.trader/riskManager/rmEngine.py @@ -0,0 +1,213 @@ +# encoding: UTF-8 + +''' +本文件中实现了风控引擎,用于提供一系列常用的风控功能: +1. 委托流控(单位时间内最大允许发出的委托数量) +2. 总成交限制(每日总成交数量限制) +3. 单笔委托的委托数量控制 +''' + +import json +import os +import winsound + +from eventEngine import * +from vtConstant import * +from vtGateway import VtLogData + + +######################################################################## +class RmEngine(object): + """风控引擎""" + settingFileName = 'RM_setting.json' + settingFileName = os.getcwd() + '/riskManager/' + settingFileName + + name = u'风控模块' + + #---------------------------------------------------------------------- + def __init__(self, mainEngine, eventEngine): + """Constructor""" + self.mainEngine = mainEngine + self.eventEngine = eventEngine + + # 是否启动风控 + self.active = False + + # 流控相关 + self.orderFlowCount = EMPTY_INT # 单位时间内委托计数 + self.orderFlowLimit = EMPTY_INT # 委托限制 + self.orderFlowClear = EMPTY_INT # 计数清空时间(秒) + self.orderFlowTimer = EMPTY_INT # 计数清空时间计时 + + # 单笔委托相关 + self.orderSizeLimit = EMPTY_INT # 单笔委托最大限制 + + # 成交统计相关 + self.tradeCount = EMPTY_INT # 当日成交合约数量统计 + self.tradeLimit = EMPTY_INT # 当日成交合约数量限制 + + # 活动合约相关 + self.workingOrderLimit = EMPTY_INT # 活动合约最大限制 + + self.loadSetting() + self.registerEvent() + + #---------------------------------------------------------------------- + def loadSetting(self): + """读取配置""" + with open(self.settingFileName) as f: + d = json.load(f) + + # 设置风控参数 + self.active = d['active'] + + self.orderFlowLimit = d['orderFlowLimit'] + self.orderFlowClear = d['orderFlowClear'] + + self.orderSizeLimit = d['orderSizeLimit'] + + self.tradeLimit = d['tradeLimit'] + + self.workingOrderLimit = d['workingOrderLimit'] + + #---------------------------------------------------------------------- + def saveSetting(self): + """保存风控参数""" + with open(self.settingFileName, 'w') as f: + # 保存风控参数 + d = {} + + d['active'] = self.active + + d['orderFlowLimit'] = self.orderFlowLimit + d['orderFlowClear'] = self.orderFlowClear + + d['orderSizeLimit'] = self.orderSizeLimit + + d['tradeLimit'] = self.tradeLimit + + d['workingOrderLimit'] = self.workingOrderLimit + + # 写入json + jsonD = json.dumps(d, indent=4) + f.write(jsonD) + + #---------------------------------------------------------------------- + def registerEvent(self): + """注册事件监听""" + self.eventEngine.register(EVENT_TRADE, self.updateTrade) + self.eventEngine.register(EVENT_TIMER, self.updateTimer) + + #---------------------------------------------------------------------- + def updateTrade(self, event): + """更新成交数据""" + trade = event.dict_['data'] + self.tradeCount += trade.volume + + #---------------------------------------------------------------------- + def updateTimer(self, event): + """更新定时器""" + self.orderFlowTimer += 1 + + # 如果计时超过了流控清空的时间间隔,则执行清空 + if self.orderFlowTimer >= self.orderFlowClear: + self.orderFlowCount = 0 + self.orderFlowTimer = 0 + + #---------------------------------------------------------------------- + def writeRiskLog(self, content): + """快速发出日志事件""" + # 发出报警提示音 + winsound.PlaySound("SystemHand", winsound.SND_ASYNC) + + # 发出日志事件 + log = VtLogData() + log.logContent = content + log.gatewayName = self.name + event = Event(type_=EVENT_LOG) + event.dict_['data'] = log + self.eventEngine.put(event) + + #---------------------------------------------------------------------- + def checkRisk(self, orderReq): + """检查风险""" + # 如果没有启动风控检查,则直接返回成功 + if not self.active: + return True + + # 检查委托数量 + if orderReq.volume > self.orderSizeLimit: + self.writeRiskLog(u'单笔委托数量%s,超过限制%s' + %(orderReq.volume, self.orderSizeLimit)) + return False + + # 检查成交合约量 + if self.tradeCount >= self.tradeLimit: + self.writeRiskLog(u'今日总成交合约数量%s,超过限制%s' + %(self.tradeCount, self.tradeLimit)) + return False + + # 检查流控 + if self.orderFlowCount >= self.orderFlowLimit: + self.writeRiskLog(u'委托流数量%s,超过限制每%s秒%s' + %(self.orderFlowCount, self.orderFlowClear, self.orderFlowLimit)) + return False + + # 检查总活动合约 + workingOrderCount = len(self.mainEngine.getAllWorkingOrders) + if workingOrderCount >= self.workingOrderLimit: + self.writeRiskLog(u'当前活动委托数量%s,超过限制%s' + %(workingOrderCount, self.workingOrderLimit)) + return False + + # 对于通过风控的委托,增加流控计数 + self.orderFlowCount += 1 + + return True + + #---------------------------------------------------------------------- + def clearOrderFlowCount(self): + """清空流控计数""" + self.orderFlowCount = 0 + self.writeRiskLog(u'清空流控计数') + + #---------------------------------------------------------------------- + def clearTradeCount(self): + """清空成交数量计数""" + self.tradeCount = 0 + self.writeRiskLog(u'清空总成交计数') + + #---------------------------------------------------------------------- + def setOrderFlowLimit(self, n): + """设置流控限制""" + self.orderFlowLimit = n + + #---------------------------------------------------------------------- + def setOrderFlowClear(self, n): + """设置流控清空时间""" + self.orderFlowClear = n + + #---------------------------------------------------------------------- + def setOrderSizeLimit(self, n): + """设置委托最大限制""" + self.orderSizeLimit = n + + #---------------------------------------------------------------------- + def setTradeLimit(self, n): + """设置成交限制""" + self.tradeLimit = n + + #---------------------------------------------------------------------- + def setWorkingOrderLimit(self, n): + """设置活动合约限制""" + self.workingOrderLimit = n + + #---------------------------------------------------------------------- + def switchEngineStatus(self): + """开关风控引擎""" + self.active = not self.active + + if self.active: + self.writeRiskLog(u'风险管理功能启动') + else: + self.writeRiskLog(u'风险管理功能停止') \ No newline at end of file diff --git a/vn.trader/riskManager/uiRmWidget.py b/vn.trader/riskManager/uiRmWidget.py new file mode 100644 index 00000000..251d5efa --- /dev/null +++ b/vn.trader/riskManager/uiRmWidget.py @@ -0,0 +1,133 @@ +# encoding: UTF-8 + +''' +风控模块相关的GUI控制组件 +''' + + +from uiBasicWidget import QtGui, QtCore +from eventEngine import * + + +######################################################################## +class RmSpinBox(QtGui.QSpinBox): + """调整参数用的数值框""" + + #---------------------------------------------------------------------- + def __init__(self, value): + """Constructor""" + super(RmSpinBox, self).__init__() + self.setValue(value) + + self.setMinimum(0) + self.setMaximum(1000000) + + + + +######################################################################## +class RmLine(QtGui.QFrame): + """水平分割线""" + + #---------------------------------------------------------------------- + def __init__(self): + """Constructor""" + super(RmLine, self).__init__() + self.setFrameShape(self.HLine) + self.setFrameShadow(self.Sunken) + + + + +######################################################################## +class RmEngineManager(QtGui.QWidget): + """风控引擎的管理组件""" + + #---------------------------------------------------------------------- + def __init__(self, rmEngine, eventEngine, parent=None): + """Constructor""" + super(RmEngineManager, self).__init__(parent) + + self.rmEngine = rmEngine + self.eventEngine = eventEngine + + self.initUi() + self.updateEngineStatus() + + #---------------------------------------------------------------------- + def initUi(self): + """初始化界面""" + self.setWindowTitle(u'风险管理') + + # 设置界面 + self.buttonSwitchEngineStatus = QtGui.QPushButton(u'风控模块未启动') + + self.spinOrderFlowLimit = RmSpinBox(self.rmEngine.orderFlowLimit) + self.spinOrderFlowClear = RmSpinBox(self.rmEngine.orderFlowClear) + self.spinOrderSizeLimit = RmSpinBox(self.rmEngine.orderSizeLimit) + self.spinTradeLimit = RmSpinBox(self.rmEngine.tradeLimit) + self.spinWorkingOrderLimit = RmSpinBox(self.rmEngine.workingOrderLimit) + + buttonClearOrderFlowCount = QtGui.QPushButton(u'清空流控计数') + buttonClearTradeCount = QtGui.QPushButton(u'清空总成交计数') + buttonSaveSetting = QtGui.QPushButton(u'保存设置') + + Label = QtGui.QLabel + grid = QtGui.QGridLayout() + grid.addWidget(Label(u'工作状态'), 0, 0) + grid.addWidget(self.buttonSwitchEngineStatus, 0, 1) + grid.addWidget(RmLine(), 1, 0, 1, 2) + grid.addWidget(Label(u'流控上限'), 2, 0) + grid.addWidget(self.spinOrderFlowLimit, 2, 1) + grid.addWidget(Label(u'流控清空(秒)'), 3, 0) + grid.addWidget(self.spinOrderFlowClear, 3, 1) + grid.addWidget(RmLine(), 4, 0, 1, 2) + grid.addWidget(Label(u'单笔委托上限'), 5, 0) + grid.addWidget(self.spinOrderSizeLimit, 5, 1) + grid.addWidget(RmLine(), 6, 0, 1, 2) + grid.addWidget(Label(u'总成交上限'), 7, 0) + grid.addWidget(self.spinTradeLimit, 7, 1) + grid.addWidget(RmLine(), 8, 0, 1, 2) + grid.addWidget(Label(u'活动订单上限'), 9, 0) + grid.addWidget(self.spinWorkingOrderLimit, 9, 1) + + hbox = QtGui.QHBoxLayout() + hbox.addWidget(buttonClearOrderFlowCount) + hbox.addWidget(buttonClearTradeCount) + hbox.addStretch() + hbox.addWidget(buttonSaveSetting) + + vbox = QtGui.QVBoxLayout() + vbox.addLayout(grid) + vbox.addLayout(hbox) + self.setLayout(vbox) + + # 连接组件信号 + self.spinOrderFlowLimit.valueChanged.connect(self.rmEngine.setOrderFlowLimit) + self.spinOrderFlowClear.valueChanged.connect(self.rmEngine.setOrderFlowClear) + self.spinOrderSizeLimit.valueChanged.connect(self.rmEngine.setOrderSizeLimit) + self.spinTradeLimit.valueChanged.connect(self.rmEngine.setTradeLimit) + + self.buttonSwitchEngineStatus.clicked.connect(self.switchEngineSatus) + buttonClearOrderFlowCount.clicked.connect(self.rmEngine.clearOrderFlowCount) + buttonClearTradeCount.clicked.connect(self.rmEngine.clearTradeCount) + buttonSaveSetting.clicked.connect(self.rmEngine.saveSetting) + + # 设为固定大小 + self.setFixedSize(self.sizeHint()) + + #---------------------------------------------------------------------- + def switchEngineSatus(self): + """控制风控引擎开关""" + self.rmEngine.switchEngineStatus() + self.updateEngineStatus() + + #---------------------------------------------------------------------- + def updateEngineStatus(self): + """更新引擎状态""" + if self.rmEngine.active: + self.buttonSwitchEngineStatus.setText(u'风控模块运行中') + else: + self.buttonSwitchEngineStatus.setText(u'风控模块未启动') + + \ No newline at end of file diff --git a/vn.trader/uiMainWindow.py b/vn.trader/uiMainWindow.py index 1624d651..92400c27 100644 --- a/vn.trader/uiMainWindow.py +++ b/vn.trader/uiMainWindow.py @@ -5,7 +5,7 @@ import psutil from uiBasicWidget import * from ctaAlgo.uiCtaWidget import CtaEngineManager from dataRecorder.uiDrWidget import DrEngineManager - +from riskManager.uiRmWidget import RmEngineManager ######################################################################## class MainWindow(QtGui.QMainWindow): @@ -109,6 +109,9 @@ class MainWindow(QtGui.QMainWindow): ctaAction = QtGui.QAction(u'CTA策略', self) ctaAction.triggered.connect(self.openCta) + rmAction = QtGui.QAction(u'风险管理', self) + rmAction.triggered.connect(self.openRm) + # 创建菜单 menubar = self.menuBar() @@ -133,6 +136,7 @@ class MainWindow(QtGui.QMainWindow): functionMenu = menubar.addMenu(u'功能') functionMenu.addAction(contractAction) functionMenu.addAction(drAction) + functionMenu.addAction(rmAction) # 算法相关 algoMenu = menubar.addMenu(u'算法') @@ -263,6 +267,15 @@ class MainWindow(QtGui.QMainWindow): except KeyError: self.widgetDict['drM'] = DrEngineManager(self.mainEngine.drEngine, self.eventEngine) self.widgetDict['drM'].showMaximized() + + #---------------------------------------------------------------------- + def openRm(self): + """打开组件""" + try: + self.widgetDict['rmM'].show() + except KeyError: + self.widgetDict['rmM'] = RmEngineManager(self.mainEngine.rmEngine, self.eventEngine) + self.widgetDict['rmM'].show() #---------------------------------------------------------------------- def closeEvent(self, event): diff --git a/vn.trader/vtEngine.py b/vn.trader/vtEngine.py index c5f353c1..df52d756 100644 --- a/vn.trader/vtEngine.py +++ b/vn.trader/vtEngine.py @@ -12,6 +12,7 @@ from vtFunction import loadMongoSetting from ctaAlgo.ctaEngine import CtaEngine from dataRecorder.drEngine import DrEngine +from riskManager.rmEngine import RmEngine ######################################################################## @@ -37,6 +38,7 @@ class MainEngine(object): # 扩展模块 self.ctaEngine = CtaEngine(self, self.eventEngine) self.drEngine = DrEngine(self, self.eventEngine) + self.rmEngine = RmEngine(self, self.eventEngine) #---------------------------------------------------------------------- def initGateway(self): @@ -148,6 +150,10 @@ class MainEngine(object): #---------------------------------------------------------------------- def cancelOrder(self, cancelOrderReq, gatewayName): """对特定接口撤单""" + # 如果风控检查失败则不发单 + if not self.rmEngine.checkRisk(orderReq): + return '' + if gatewayName in self.gatewayDict: gateway = self.gatewayDict[gatewayName] gateway.cancelOrder(cancelOrderReq)