增加行情数据记录组件,通过json文件配置使用,默认不启动。

This commit is contained in:
chenxy123 2016-04-15 20:20:49 +08:00
parent cb675f7887
commit d28909d443
8 changed files with 477 additions and 3 deletions

View 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"
}
}

View File

View 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

View 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为具体的合约代码如IF1604value为主力合约代码如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)

View 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))

View File

@ -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接口请求连接事件

View File

@ -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):

View File

@ -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):