增加了风控模块。
This commit is contained in:
parent
549f19be00
commit
c39d124639
@ -114,7 +114,7 @@ void MdApi::OnFrontDisconnected(char *pErrMsg)
|
||||
|
||||
if (pErrMsg)
|
||||
{
|
||||
task.task_data = *pErrMsg;
|
||||
task.task_data = string(pErrMsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -114,7 +114,7 @@ void TdApi::OnFrontDisconnected(char *pErrMsg)
|
||||
|
||||
if (pErrMsg)
|
||||
{
|
||||
task.task_data = *pErrMsg;
|
||||
task.task_data = string(pErrMsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -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:
|
||||
|
8
vn.trader/riskManager/RM_setting.json
Normal file
8
vn.trader/riskManager/RM_setting.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"orderFlowClear": 1,
|
||||
"workingOrderLimit": 0,
|
||||
"tradeLimit": 200,
|
||||
"orderSizeLimit": 10,
|
||||
"active": true,
|
||||
"orderFlowLimit": 10
|
||||
}
|
0
vn.trader/riskManager/__init__.py
Normal file
0
vn.trader/riskManager/__init__.py
Normal file
213
vn.trader/riskManager/rmEngine.py
Normal file
213
vn.trader/riskManager/rmEngine.py
Normal file
@ -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'风险管理功能停止')
|
133
vn.trader/riskManager/uiRmWidget.py
Normal file
133
vn.trader/riskManager/uiRmWidget.py
Normal file
@ -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'风控模块未启动')
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user