增加行情数据记录组件,通过json文件配置使用,默认不启动。
This commit is contained in:
parent
cb675f7887
commit
d28909d443
30
vn.trader/dataRecorder/DR_setting.json
Normal file
30
vn.trader/dataRecorder/DR_setting.json
Normal file
@ -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"
|
||||
}
|
||||
}
|
0
vn.trader/dataRecorder/__init__.py
Normal file
0
vn.trader/dataRecorder/__init__.py
Normal file
97
vn.trader/dataRecorder/drBase.py
Normal file
97
vn.trader/dataRecorder/drBase.py
Normal file
@ -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
|
172
vn.trader/dataRecorder/drEngine.py
Normal file
172
vn.trader/dataRecorder/drEngine.py
Normal file
@ -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)
|
||||
|
155
vn.trader/dataRecorder/uiDrWidget.py
Normal file
155
vn.trader/dataRecorder/uiDrWidget.py
Normal file
@ -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))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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接口请求连接事件
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
Loading…
Reference in New Issue
Block a user