From d28909d4435d22e89d61a593ecfffbb73ed72c36 Mon Sep 17 00:00:00 2001 From: chenxy123 Date: Fri, 15 Apr 2016 20:20:49 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=A1=8C=E6=83=85=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E8=AE=B0=E5=BD=95=E7=BB=84=E4=BB=B6=EF=BC=8C=E9=80=9A?= =?UTF-8?q?=E8=BF=87json=E6=96=87=E4=BB=B6=E9=85=8D=E7=BD=AE=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=EF=BC=8C=E9=BB=98=E8=AE=A4=E4=B8=8D=E5=90=AF=E5=8A=A8?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vn.trader/dataRecorder/DR_setting.json | 30 +++++ vn.trader/dataRecorder/__init__.py | 0 vn.trader/dataRecorder/drBase.py | 97 ++++++++++++++ vn.trader/dataRecorder/drEngine.py | 172 +++++++++++++++++++++++++ vn.trader/dataRecorder/uiDrWidget.py | 155 ++++++++++++++++++++++ vn.trader/eventType.py | 3 + vn.trader/uiMainWindow.py | 14 ++ vn.trader/vtEngine.py | 9 +- 8 files changed, 477 insertions(+), 3 deletions(-) create mode 100644 vn.trader/dataRecorder/DR_setting.json create mode 100644 vn.trader/dataRecorder/__init__.py create mode 100644 vn.trader/dataRecorder/drBase.py create mode 100644 vn.trader/dataRecorder/drEngine.py create mode 100644 vn.trader/dataRecorder/uiDrWidget.py diff --git a/vn.trader/dataRecorder/DR_setting.json b/vn.trader/dataRecorder/DR_setting.json new file mode 100644 index 00000000..d886d595 --- /dev/null +++ b/vn.trader/dataRecorder/DR_setting.json @@ -0,0 +1,30 @@ +{ + "working": false, + + "tick": + [ + ["IF1605", "SGIT"], + ["IF1606", "SGIT"], + ["IH1606", "SGIT"], + ["IH1606", "SGIT"], + ["IC1606", "SGIT"], + ["IC1606", "SGIT"] + ], + + "bar": + [ + ["IF1605", "SGIT"], + ["IF1606", "SGIT"], + ["IH1606", "SGIT"], + ["IH1606", "SGIT"], + ["IC1606", "SGIT"], + ["IC1606", "SGIT"] + ], + + "active": + { + "IF0000": "IF1605", + "IH0000": "IH1605", + "IC0000": "IC1605" + } +} \ No newline at end of file diff --git a/vn.trader/dataRecorder/__init__.py b/vn.trader/dataRecorder/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/vn.trader/dataRecorder/drBase.py b/vn.trader/dataRecorder/drBase.py new file mode 100644 index 00000000..85581d96 --- /dev/null +++ b/vn.trader/dataRecorder/drBase.py @@ -0,0 +1,97 @@ +# encoding: UTF-8 + +''' +本文件中包含的数据格式和CTA模块通用,用户有必要可以自行添加格式。 +''' + +from __future__ import division + + +# 把vn.trader根目录添加到python环境变量中 +import sys +sys.path.append('..') + + +# 数据库名称 +SETTING_DB_NAME = 'VnTrader_Setting_Db' +TICK_DB_NAME = 'VnTrader_Tick_Db' +DAILY_DB_NAME = 'VnTrader_Daily_Db' +MINUTE_DB_NAME = 'VnTrader_1Min_Db' + + +# CTA引擎中涉及的数据类定义 +from vtConstant import EMPTY_UNICODE, EMPTY_STRING, EMPTY_FLOAT, EMPTY_INT + + +######################################################################## +class DrBarData(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 DrTickData(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 \ No newline at end of file diff --git a/vn.trader/dataRecorder/drEngine.py b/vn.trader/dataRecorder/drEngine.py new file mode 100644 index 00000000..c9249970 --- /dev/null +++ b/vn.trader/dataRecorder/drEngine.py @@ -0,0 +1,172 @@ +# encoding: UTF-8 + +''' +本文件中实现了行情数据记录引擎,用于汇总TICK数据,并生成K线插入数据库。 + +使用DR_setting.json来配置需要收集的合约,以及主力合约代码。 +''' + +import json +import os +import copy +from collections import OrderedDict +from datetime import datetime, timedelta + +from eventEngine import * +from vtGateway import VtSubscribeReq, VtLogData +from drBase import * + + +######################################################################## +class DrEngine(object): + """数据记录引擎""" + + settingFileName = 'DR_setting.json' + settingFileName = os.getcwd() + '/dataRecorder/' + settingFileName + + #---------------------------------------------------------------------- + def __init__(self, mainEngine, eventEngine): + """Constructor""" + self.mainEngine = mainEngine + self.eventEngine = eventEngine + + # 当前日期 + self.today = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) + + # 主力合约代码映射字典,key为具体的合约代码(如IF1604),value为主力合约代码(如IF0000) + self.activeSymbolDict = {} + + # Tick对象字典 + self.tickDict = {} + + # K线对象字典 + self.barDict = {} + + # 载入设置,订阅行情 + self.loadSetting() + + #---------------------------------------------------------------------- + def loadSetting(self): + """载入设置""" + with open(self.settingFileName) as f: + setting = json.load(f) + + # 如果working设为False则不启动行情记录功能 + working = setting['working'] + if not working: + return + + if 'tick' in setting: + l = setting['tick'] + + for symbol, gatewayName in l: + drTick = DrTickData() # 该tick实例可以用于缓存部分数据(目前未使用) + self.tickDict[symbol] = drTick + + req = VtSubscribeReq() + req.symbol = symbol + self.mainEngine.subscribe(req, gatewayName) + + if 'bar' in setting: + l = setting['bar'] + + for symbol, gatewayName in l: + bar = DrBarData() + self.barDict[symbol] = bar + + req = VtSubscribeReq() + req.symbol = symbol + self.mainEngine.subscribe(req, gatewayName) + + if 'active' in setting: + d = setting['active'] + + for activeSymbol, symbol in d.items(): + self.activeSymbolDict[symbol] = activeSymbol + + # 注册事件监听 + self.registerEvent() + + #---------------------------------------------------------------------- + def procecssTickEvent(self, event): + """处理行情推送""" + tick = event.dict_['data'] + vtSymbol = tick.vtSymbol + + # 转化Tick格式 + drTick = DrTickData() + d = drTick.__dict__ + for key in d.keys(): + if key != 'datetime': + d[key] = tick.__getattribute__(key) + drTick.datetime = datetime.strptime(' '.join([tick.date, tick.time]), '%Y%m%d %H:%M:%S.%f') + + # 更新Tick数据 + if vtSymbol in self.tickDict: + self.insertData(TICK_DB_NAME, vtSymbol, drTick) + + if vtSymbol in self.activeSymbolDict: + activeSymbol = self.activeSymbolDict[vtSymbol] + self.insertData(TICK_DB_NAME, activeSymbol, drTick) + + # 发出日志 + self.writeDrLog(u'记录Tick数据%s,时间:%s, last:%s, bid:%s, ask:%s' + %(drTick.vtSymbol, drTick.time, drTick.lastPrice, drTick.bidPrice1, drTick.askPrice1)) + + # 更新分钟线数据 + if vtSymbol in self.barDict: + bar = self.barDict[vtSymbol] + + # 如果第一个TICK或者新的一分钟 + if not bar.datetime or bar.datetime.minute != drTick.datetime.minute: + if bar.vtSymbol: + newBar = copy.copy(bar) + self.insertData(MINUTE_DB_NAME, vtSymbol, newBar) + + if vtSymbol in self.activeSymbolDict: + activeSymbol = self.activeSymbolDict[vtSymbol] + self.insertData(MINUTE_DB_NAME, activeSymbol, newBar) + + self.writeDrLog(u'记录分钟线数据%s,时间:%s, O:%s, H:%s, L:%s, C:%s' + %(bar.vtSymbol, bar.time, bar.open, bar.high, + bar.low, bar.close)) + + bar.vtSymbol = drTick.vtSymbol + bar.symbol = drTick.symbol + bar.exchange = drTick.exchange + + bar.open = drTick.lastPrice + bar.high = drTick.lastPrice + bar.low = drTick.lastPrice + bar.close = drTick.lastPrice + + bar.date = drTick.date + bar.time = drTick.time + bar.datetime = drTick.datetime + bar.volume = drTick.volume + bar.openInterest = drTick.openInterest + # 否则继续累加新的K线 + else: + bar.high = max(bar.high, drTick.lastPrice) + bar.low = min(bar.low, drTick.lastPrice) + bar.close = drTick.lastPrice + + #---------------------------------------------------------------------- + def registerEvent(self): + """注册事件监听""" + self.eventEngine.register(EVENT_TICK, self.procecssTickEvent) + + #---------------------------------------------------------------------- + def insertData(self, dbName, collectionName, data): + """插入数据到数据库(这里的data可以是CtaTickData或者CtaBarData)""" + self.mainEngine.dbInsert(dbName, collectionName, data.__dict__) + + #---------------------------------------------------------------------- + def writeDrLog(self, content): + """快速发出日志事件""" + log = VtLogData() + log.logContent = content + event = Event(type_=EVENT_DATARECORDER_LOG) + event.dict_['data'] = log + self.eventEngine.put(event) + \ No newline at end of file diff --git a/vn.trader/dataRecorder/uiDrWidget.py b/vn.trader/dataRecorder/uiDrWidget.py new file mode 100644 index 00000000..26883175 --- /dev/null +++ b/vn.trader/dataRecorder/uiDrWidget.py @@ -0,0 +1,155 @@ +# encoding: UTF-8 + +''' +行情记录模块相关的GUI控制组件 +''' + +import json + +from uiBasicWidget import QtGui, QtCore +from eventEngine import * + + +######################################################################## +class TableCell(QtGui.QTableWidgetItem): + """居中的单元格""" + + #---------------------------------------------------------------------- + def __init__(self, text=None, mainEngine=None): + """Constructor""" + super(TableCell, self).__init__() + self.data = None + self.setTextAlignment(QtCore.Qt.AlignCenter) + if text: + self.setContent(text) + + #---------------------------------------------------------------------- + def setContent(self, text): + """设置内容""" + if text == '0' or text == '0.0': + self.setText('') + else: + self.setText(text) + + +######################################################################## +class DrEngineManager(QtGui.QWidget): + """行情数据记录引擎管理组件""" + signal = QtCore.pyqtSignal(type(Event())) + + #---------------------------------------------------------------------- + def __init__(self, drEngine, eventEngine, parent=None): + """Constructor""" + super(DrEngineManager, self).__init__(parent) + + self.drEngine = drEngine + self.eventEngine = eventEngine + + self.initUi() + self.updateSetting() + self.registerEvent() + + #---------------------------------------------------------------------- + def initUi(self): + """初始化界面""" + self.setWindowTitle(u'行情数据记录工具') + + # 记录合约配置监控 + tickLabel = QtGui.QLabel(u'Tick记录') + self.tickTable = QtGui.QTableWidget() + self.tickTable.setColumnCount(2) + self.tickTable.verticalHeader().setVisible(False) + self.tickTable.setEditTriggers(QtGui.QTableWidget.NoEditTriggers) + self.tickTable.horizontalHeader().setResizeMode(QtGui.QHeaderView.Stretch) + self.tickTable.setAlternatingRowColors(True) + self.tickTable.setHorizontalHeaderLabels([u'合约代码', u'接口']) + + barLabel = QtGui.QLabel(u'Bar记录') + self.barTable = QtGui.QTableWidget() + self.barTable.setColumnCount(2) + self.barTable.verticalHeader().setVisible(False) + self.barTable.setEditTriggers(QtGui.QTableWidget.NoEditTriggers) + self.barTable.horizontalHeader().setResizeMode(QtGui.QHeaderView.Stretch) + self.barTable.setAlternatingRowColors(True) + self.barTable.setHorizontalHeaderLabels([u'合约代码', u'接口']) + + activeLabel = QtGui.QLabel(u'主力合约') + self.activeTable = QtGui.QTableWidget() + self.activeTable.setColumnCount(2) + self.activeTable.verticalHeader().setVisible(False) + self.activeTable.setEditTriggers(QtGui.QTableWidget.NoEditTriggers) + self.activeTable.horizontalHeader().setResizeMode(QtGui.QHeaderView.Stretch) + self.activeTable.setAlternatingRowColors(True) + self.activeTable.setHorizontalHeaderLabels([u'主力代码', u'合约代码']) + + # 日志监控 + self.logMonitor = QtGui.QTextEdit() + self.logMonitor.setReadOnly(True) + self.logMonitor.setMinimumHeight(600) + + # 设置布局 + grid = QtGui.QGridLayout() + + grid.addWidget(tickLabel, 0, 0) + grid.addWidget(barLabel, 0, 1) + grid.addWidget(activeLabel, 0, 2) + grid.addWidget(self.tickTable, 1, 0) + grid.addWidget(self.barTable, 1, 1) + grid.addWidget(self.activeTable, 1, 2) + + vbox = QtGui.QVBoxLayout() + vbox.addLayout(grid) + vbox.addWidget(self.logMonitor) + self.setLayout(vbox) + + #---------------------------------------------------------------------- + def updateLog(self, event): + """更新日志""" + log = event.dict_['data'] + content = '\t'.join([log.logTime, log.logContent]) + self.logMonitor.append(content) + + #---------------------------------------------------------------------- + def registerEvent(self): + """注册事件监听""" + self.signal.connect(self.updateLog) + self.eventEngine.register(EVENT_DATARECORDER_LOG, self.signal.emit) + + #---------------------------------------------------------------------- + def updateSetting(self): + """显示引擎行情记录配置""" + with open(self.drEngine.settingFileName) as f: + setting = json.load(f) + + if 'tick' in setting: + l = setting['tick'] + + for symbol, gatewayName in l: + self.tickTable.insertRow(0) + self.tickTable.setItem(0, 0, TableCell(symbol)) + self.tickTable.setItem(0, 1, TableCell(gatewayName)) + + if 'bar' in setting: + l = setting['bar'] + + for symbol, gatewayName in l: + self.barTable.insertRow(0) + self.barTable.setItem(0, 0, TableCell(symbol)) + self.barTable.setItem(0, 1, TableCell(gatewayName)) + + if 'active' in setting: + d = setting['active'] + + for activeSymbol, symbol in d.items(): + self.activeTable.insertRow(0) + self.activeTable.setItem(0, 0, TableCell(activeSymbol)) + self.activeTable.setItem(0, 1, TableCell(symbol)) + + + + + + + + + \ No newline at end of file diff --git a/vn.trader/eventType.py b/vn.trader/eventType.py index 15f56cc6..1b3ccaa4 100644 --- a/vn.trader/eventType.py +++ b/vn.trader/eventType.py @@ -28,6 +28,9 @@ EVENT_ERROR = 'eError.' # 错误回报事件 EVENT_CTA_LOG = 'eCtaLog' # CTA相关的日志事件 EVENT_CTA_STRATEGY = 'eCtaStrategy.' # CTA策略状态变化事件 +# 行情记录模块相关 +EVENT_DATARECORDER_LOG = 'eDataRecorderLog' # 行情记录日志更新事件 + # Wind接口相关 EVENT_WIND_CONNECTREQ = 'eWindConnectReq' # Wind接口请求连接事件 diff --git a/vn.trader/uiMainWindow.py b/vn.trader/uiMainWindow.py index 91b88950..d93457db 100644 --- a/vn.trader/uiMainWindow.py +++ b/vn.trader/uiMainWindow.py @@ -4,6 +4,7 @@ import psutil from uiBasicWidget import * from ctaAlgo.uiCtaWidget import CtaEngineManager +from dataRecorder.uiDrWidget import DrEngineManager ######################################################################## @@ -115,6 +116,9 @@ class MainWindow(QtGui.QMainWindow): contractAction = QtGui.QAction(u'查询合约', self) contractAction.triggered.connect(self.openContract) + drAction = QtGui.QAction(u'行情数据记录', self) + drAction.triggered.connect(self.openDr) + ctaAction = QtGui.QAction(u'CTA策略', self) ctaAction.triggered.connect(self.openCta) @@ -141,6 +145,7 @@ class MainWindow(QtGui.QMainWindow): functionMenu = menubar.addMenu(u'功能') functionMenu.addAction(contractAction) + functionMenu.addAction(drAction) # 算法相关 algoMenu = menubar.addMenu(u'算法') @@ -264,6 +269,15 @@ class MainWindow(QtGui.QMainWindow): except KeyError: self.widgetDict['ctaM'] = CtaEngineManager(self.mainEngine.ctaEngine, self.eventEngine) self.widgetDict['ctaM'].show() + + #---------------------------------------------------------------------- + def openDr(self): + """打开行情数据记录组件""" + try: + self.widgetDict['drM'].show() + except KeyError: + self.widgetDict['drM'] = DrEngineManager(self.mainEngine.drEngine, self.eventEngine) + self.widgetDict['drM'].show() #---------------------------------------------------------------------- def closeEvent(self, event): diff --git a/vn.trader/vtEngine.py b/vn.trader/vtEngine.py index f8d4de5d..938d25b7 100644 --- a/vn.trader/vtEngine.py +++ b/vn.trader/vtEngine.py @@ -8,7 +8,9 @@ from pymongo.errors import ConnectionFailure from eventEngine import * from vtGateway import * + from ctaAlgo.ctaEngine import CtaEngine +from dataRecorder.drEngine import DrEngine ######################################################################## @@ -28,11 +30,12 @@ class MainEngine(object): # MongoDB数据库相关 self.dbClient = None # MongoDB客户端对象 - # CTA引擎 - self.ctaEngine = CtaEngine(self, self.eventEngine) - # 调用一个个初始化函数 self.initGateway() + + # 扩展模块 + self.ctaEngine = CtaEngine(self, self.eventEngine) + self.drEngine = DrEngine(self, self.eventEngine) #---------------------------------------------------------------------- def initGateway(self):