增加:

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

View File

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

View File

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

View File

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

View File

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

View File

@ -23,8 +23,10 @@ class MainWindow(QtGui.QMainWindow):
self.widgetDict = {} # 用来保存子窗口的字典 self.widgetDict = {} # 用来保存子窗口的字典
self.connectGatewayDict = {}
self.initUi() self.initUi()
self.loadWindowSettings() #self.loadWindowSettings()
self.connected = False self.connected = False
self.autoDisConnect = False self.autoDisConnect = False
@ -32,11 +34,13 @@ class MainWindow(QtGui.QMainWindow):
self.orderSaveDate = EMPTY_STRING self.orderSaveDate = EMPTY_STRING
self.barSaveDate = EMPTY_STRING self.barSaveDate = EMPTY_STRING
self.connectGatewayDict = {}
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
def initUi(self): def initUi(self):
"""初始化界面""" """初始化界面"""
self.setWindowTitle('VnTrader') path = os.getcwd().rsplit('\\')[-1]
self.setWindowTitle(path)
self.initCentral() self.initCentral()
self.initMenu() self.initMenu()
self.initStatusBar() self.initStatusBar()
@ -196,9 +200,15 @@ class MainWindow(QtGui.QMainWindow):
self.sbTrigger = 10 # 10秒刷新一次 self.sbTrigger = 10 # 10秒刷新一次
self.eventEngine.register(EVENT_TIMER, self.updateStatusBar) self.eventEngine.register(EVENT_TIMER, self.updateStatusBar)
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
def updateStatusBar(self, event): def updateStatusBar(self, event):
"""在状态栏更新CPU和内存信息""" """1、在状态栏更新CPU和内存信息"""
# 2、定时断开服务器连接
# 3、定时重连服务器
# 4、定时保存每日的委托单
# 5、定时执行策略的保存事件
self.sbCount += 1 self.sbCount += 1
# 更新任务栏 # 更新任务栏
@ -211,68 +221,69 @@ class MainWindow(QtGui.QMainWindow):
self.statusLabel.setText(info) 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:
s = s + u' [已断开]'
self.setWindowTitle(s)
# 定时断开
if self.connected and self.trade_off() and self.autoDisConnect:
self.disconnect()
self.mainEngine.writeLog(u'断开连接{0}'.format(self.connectGatewayDict.values()))
self.mainEngine.writeLog(u'清空数据引擎')
self.mainEngine.clearData()
self.mainEngine.writeLog(u'清空委托列表')
self.widgetOrderM.clearData()
self.mainEngine.writeLog(u'清空交易列表')
self.widgetTradeM.clearData()
# 定时重连
if not self.connected \
and self.autoDisConnect \
and not self.trade_off()\
and len(self.connectGatewayDict) > 0:
self.mainEngine.writeLog(u'清空数据引擎')
self.mainEngine.clearData()
self.mainEngine.writeLog(u'清空委托列表')
self.widgetOrderM.clearData()
self.mainEngine.writeLog(u'清空交易列表')
self.widgetTradeM.clearData()
s = u''.join(str(e) for e in self.connectGatewayDict.values()) s = u''.join(str(e) for e in self.connectGatewayDict.values())
self.mainEngine.writeLog(u'重新连接{0}'.format(s))
for key in self.connectGatewayDict.keys(): if not self.connected:
self.mainEngine.connect(key) s = s + u' [已断开]'
self.connected = True
# 交易日收盘后保存所有委托记录, self.setWindowTitle(s)
dt = datetime.now()
today = datetime.now().strftime('%y%m%d')
if dt.hour == 15 and dt.minute == 1 and len(self.connectGatewayDict) > 0 and today!=self.orderSaveDate:
self.orderSaveDate = today
self.mainEngine.writeLog(u'保存所有委托记录')
orderfile = os.getcwd() +'/orders/{0}.csv'.format(self.orderSaveDate)
if os.path.exists(orderfile):
return
else:
self.widgetOrderM.saveToCsv(path=orderfile)
# 调用各策略保存数据 # 定时断开
if ((dt.hour == 15 and dt.minute == 1) or (dt.hour == 2 and dt.minute == 31)) \ if self.connected and self.trade_off() and self.autoDisConnect:
and len(self.connectGatewayDict) > 0 \ self.disconnect()
and today != self.barSaveDate: self.mainEngine.writeLog(u'断开连接{0}'.format(self.connectGatewayDict.values()))
self.barSaveDate = today self.mainEngine.writeLog(u'清空数据引擎')
self.mainEngine.writeLog(u'调用各策略保存数据') self.mainEngine.clearData()
self.mainEngine.saveData() self.mainEngine.writeLog(u'清空委托列表')
self.widgetOrderM.clearData()
self.mainEngine.writeLog(u'清空交易列表')
self.widgetTradeM.clearData()
if not (dt.hour == 15 or dt.hour == 2): # 定时重连
self.barSaveDate = EMPTY_STRING if not self.connected \
and self.autoDisConnect \
and not self.trade_off()\
and len(self.connectGatewayDict) > 0:
self.mainEngine.writeLog(u'清空数据引擎')
self.mainEngine.clearData()
self.mainEngine.writeLog(u'清空委托列表')
self.widgetOrderM.clearData()
self.mainEngine.writeLog(u'清空交易列表')
self.widgetTradeM.clearData()
s = u''.join(str(e) for e in self.connectGatewayDict.values())
self.mainEngine.writeLog(u'重新连接{0}'.format(s))
for key in self.connectGatewayDict.keys():
self.mainEngine.connect(key)
self.connected = True
# 交易日收盘后保存所有委托记录,
dt = datetime.now()
today = datetime.now().strftime('%y%m%d')
if dt.hour == 15 and dt.minute == 1 and len(self.connectGatewayDict) > 0 and today!=self.orderSaveDate:
self.orderSaveDate = today
self.mainEngine.writeLog(u'保存所有委托记录')
orderfile = os.getcwd() +'/orders/{0}.csv'.format(self.orderSaveDate)
if os.path.exists(orderfile):
return
else:
self.widgetOrderM.saveToCsv(path=orderfile)
# 调用各策略保存数据
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 self.connected:
self.barSaveDate = today
self.mainEngine.writeLog(u'调用各策略保存数据')
self.mainEngine.saveData()
if not (dt.hour == 15 or dt.hour == 2):
self.barSaveDate = EMPTY_STRING
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
def getCpuMemory(self): def getCpuMemory(self):
@ -491,7 +502,6 @@ class MainWindow(QtGui.QMainWindow):
pass pass
def disconnect(self): def disconnect(self):
""""断开底层gateway的连接""" """"断开底层gateway的连接"""
self.mainEngine.disconnect() self.mainEngine.disconnect()

View File

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

View File

@ -28,7 +28,7 @@ class MainEngine(object):
self.eventEngine.start() self.eventEngine.start()
# 创建数据引擎 # 创建数据引擎
self.dataEngine = DataEngine(self.eventEngine) self.dataEngine = DataEngine(self, self.eventEngine)
# MongoDB数据库相关 # MongoDB数据库相关
self.dbClient = None # MongoDB客户端对象 self.dbClient = None # MongoDB客户端对象
@ -61,7 +61,7 @@ class MainEngine(object):
self.addGateway(CtpGateway, 'CTP_EBF') self.addGateway(CtpGateway, 'CTP_EBF')
self.gatewayDict['CTP_EBF'].setQryEnabled(True) self.gatewayDict['CTP_EBF'].setQryEnabled(True)
except Exception, e: except Exception as e:
print e print e
""" """
@ -125,8 +125,16 @@ class MainEngine(object):
self.gatewayDict['OANDA'].setQryEnabled(True) self.gatewayDict['OANDA'].setQryEnabled(True)
except Exception, e: except Exception, e:
print 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): def addGateway(self, gateway, gatewayName=None):
"""创建接口""" """创建接口"""
self.gatewayDict[gatewayName] = gateway(self.eventEngine, gatewayName) self.gatewayDict[gatewayName] = gateway(self.eventEngine, gatewayName)
@ -208,6 +216,9 @@ class MainEngine(object):
# 停止事件引擎 # 停止事件引擎
self.eventEngine.stop() self.eventEngine.stop()
# 停止数据记录引擎
self.drEngine.stop()
# 保存数据引擎里的合约数据到硬盘 # 保存数据引擎里的合约数据到硬盘
self.dataEngine.saveContracts() self.dataEngine.saveContracts()
@ -288,6 +299,7 @@ class MainEngine(object):
def clearData(self): def clearData(self):
"""清空数据引擎的数据""" """清空数据引擎的数据"""
self.dataEngine.clearData() self.dataEngine.clearData()
self.ctaEngine.clearData()
def saveData(self): def saveData(self):
self.ctaEngine.saveStrategyData() self.ctaEngine.saveStrategyData()
@ -298,8 +310,9 @@ class DataEngine(object):
contractFileName = 'ContractData.vt' contractFileName = 'ContractData.vt'
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
def __init__(self, eventEngine): def __init__(self, mainEngine, eventEngine):
"""Constructor""" """Constructor"""
self.mainEngine = mainEngine
self.eventEngine = eventEngine self.eventEngine = eventEngine
# 保存合约详细信息的字典 # 保存合约详细信息的字典
@ -317,6 +330,9 @@ class DataEngine(object):
# 注册事件监听 # 注册事件监听
self.registerEvent() self.registerEvent()
# 已订阅合约代码
self.subscribedSymbols = set()
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
def updateContract(self, event): def updateContract(self, event):
"""更新合约数据""" """更新合约数据"""
@ -386,10 +402,45 @@ class DataEngine(object):
"""注册事件监听""" """注册事件监听"""
self.eventEngine.register(EVENT_CONTRACT, self.updateContract) self.eventEngine.register(EVENT_CONTRACT, self.updateContract)
self.eventEngine.register(EVENT_ORDER, self.updateOrder) self.eventEngine.register(EVENT_ORDER, self.updateOrder)
self.eventEngine.register(EVENT_POSITION, self.updatePosition)
def clearData(self): def clearData(self):
"""清空数据""" """清空数据"""
self.orderDict = {} self.orderDict = {}
self.workingOrderDict = {} 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))