2015-10-16 08:50:44 +00:00
|
|
|
|
# encoding: UTF-8
|
|
|
|
|
|
|
|
|
|
import psutil
|
|
|
|
|
|
|
|
|
|
from uiBasicWidget import *
|
2016-02-04 12:41:37 +00:00
|
|
|
|
from ctaAlgo.uiCtaWidget import CtaEngineManager
|
2016-04-15 12:20:49 +00:00
|
|
|
|
from dataRecorder.uiDrWidget import DrEngineManager
|
2016-05-03 12:35:59 +00:00
|
|
|
|
from riskManager.uiRmWidget import RmEngineManager
|
2015-11-20 06:11:29 +00:00
|
|
|
|
|
2015-10-16 08:50:44 +00:00
|
|
|
|
########################################################################
|
|
|
|
|
class MainWindow(QtGui.QMainWindow):
|
|
|
|
|
"""主窗口"""
|
2016-06-11 03:30:03 +00:00
|
|
|
|
signalStatusBar = QtCore.pyqtSignal(type(Event()))
|
2015-10-16 08:50:44 +00:00
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
2015-12-30 14:01:17 +00:00
|
|
|
|
def __init__(self, mainEngine, eventEngine):
|
2015-10-16 08:50:44 +00:00
|
|
|
|
"""Constructor"""
|
|
|
|
|
super(MainWindow, self).__init__()
|
|
|
|
|
|
|
|
|
|
self.mainEngine = mainEngine
|
|
|
|
|
self.eventEngine = eventEngine
|
|
|
|
|
|
2015-11-20 06:11:29 +00:00
|
|
|
|
self.widgetDict = {} # 用来保存子窗口的字典
|
|
|
|
|
|
2015-10-16 08:50:44 +00:00
|
|
|
|
self.initUi()
|
2016-10-19 15:45:49 +00:00
|
|
|
|
self.loadWindowSettings('custom')
|
2015-10-16 08:50:44 +00:00
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def initUi(self):
|
|
|
|
|
"""初始化界面"""
|
|
|
|
|
self.setWindowTitle('VnTrader')
|
|
|
|
|
self.initCentral()
|
|
|
|
|
self.initMenu()
|
|
|
|
|
self.initStatusBar()
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def initCentral(self):
|
|
|
|
|
"""初始化中心区域"""
|
2016-04-28 15:03:36 +00:00
|
|
|
|
widgetMarketM, dockMarketM = self.createDock(MarketMonitor, u'行情', QtCore.Qt.RightDockWidgetArea)
|
|
|
|
|
widgetLogM, dockLogM = self.createDock(LogMonitor, u'日志', QtCore.Qt.BottomDockWidgetArea)
|
|
|
|
|
widgetErrorM, dockErrorM = self.createDock(ErrorMonitor, u'错误', QtCore.Qt.BottomDockWidgetArea)
|
|
|
|
|
widgetTradeM, dockTradeM = self.createDock(TradeMonitor, u'成交', QtCore.Qt.BottomDockWidgetArea)
|
|
|
|
|
widgetOrderM, dockOrderM = self.createDock(OrderMonitor, u'委托', QtCore.Qt.RightDockWidgetArea)
|
|
|
|
|
widgetPositionM, dockPositionM = self.createDock(PositionMonitor, u'持仓', QtCore.Qt.BottomDockWidgetArea)
|
|
|
|
|
widgetAccountM, dockAccountM = self.createDock(AccountMonitor, u'资金', QtCore.Qt.BottomDockWidgetArea)
|
|
|
|
|
widgetTradingW, dockTradingW = self.createDock(TradingWidget, u'交易', QtCore.Qt.LeftDockWidgetArea)
|
|
|
|
|
|
|
|
|
|
self.tabifyDockWidget(dockTradeM, dockErrorM)
|
|
|
|
|
self.tabifyDockWidget(dockTradeM, dockLogM)
|
|
|
|
|
self.tabifyDockWidget(dockPositionM, dockAccountM)
|
|
|
|
|
|
|
|
|
|
dockTradeM.raise_()
|
|
|
|
|
dockPositionM.raise_()
|
2015-10-16 08:50:44 +00:00
|
|
|
|
|
2016-04-20 15:14:21 +00:00
|
|
|
|
# 连接组件之间的信号
|
2016-04-28 15:03:36 +00:00
|
|
|
|
widgetPositionM.itemDoubleClicked.connect(widgetTradingW.closePosition)
|
2016-04-20 15:14:21 +00:00
|
|
|
|
|
2016-10-19 15:45:49 +00:00
|
|
|
|
# 保存默认设置
|
|
|
|
|
self.saveWindowSettings('default')
|
|
|
|
|
|
2015-10-16 08:50:44 +00:00
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def initMenu(self):
|
|
|
|
|
"""初始化菜单"""
|
|
|
|
|
# 创建菜单
|
|
|
|
|
menubar = self.menuBar()
|
|
|
|
|
|
2016-05-14 09:13:33 +00:00
|
|
|
|
# 设计为只显示存在的接口
|
2015-10-16 08:50:44 +00:00
|
|
|
|
sysMenu = menubar.addMenu(u'系统')
|
2017-01-04 13:52:20 +00:00
|
|
|
|
self.addConnectAction(sysMenu, 'CTP')
|
|
|
|
|
self.addConnectAction(sysMenu, 'LTS')
|
|
|
|
|
self.addConnectAction(sysMenu, 'XTP')
|
|
|
|
|
self.addConnectAction(sysMenu, 'FEMAS', u'飞马')
|
|
|
|
|
self.addConnectAction(sysMenu, 'XSPEED', u'飞创')
|
|
|
|
|
self.addConnectAction(sysMenu, 'QDP')
|
|
|
|
|
self.addConnectAction(sysMenu, 'KSOTP', u'金仕达期权')
|
|
|
|
|
self.addConnectAction(sysMenu, 'KSGOLD', u'金仕达黄金')
|
|
|
|
|
self.addConnectAction(sysMenu, 'SGIT', u'飞鼠')
|
2016-03-06 10:09:15 +00:00
|
|
|
|
sysMenu.addSeparator()
|
2017-01-04 13:52:20 +00:00
|
|
|
|
self.addConnectAction(sysMenu, 'IB')
|
|
|
|
|
self.addConnectAction(sysMenu, 'SHZD', u'直达')
|
|
|
|
|
self.addConnectAction(sysMenu, 'OANDA')
|
|
|
|
|
self.addConnectAction(sysMenu, 'OKCOIN')
|
2015-12-23 06:15:41 +00:00
|
|
|
|
sysMenu.addSeparator()
|
2017-01-04 13:52:20 +00:00
|
|
|
|
self.addConnectAction(sysMenu, 'Wind')
|
|
|
|
|
|
2015-11-20 06:11:29 +00:00
|
|
|
|
sysMenu.addSeparator()
|
2017-01-04 13:52:20 +00:00
|
|
|
|
sysMenu.addAction(self.createAction(u'连接数据库', self.mainEngine.dbConnect))
|
2015-11-20 06:11:29 +00:00
|
|
|
|
sysMenu.addSeparator()
|
2017-01-04 13:52:20 +00:00
|
|
|
|
sysMenu.addAction(self.createAction(u'退出', self.close))
|
2015-10-16 08:50:44 +00:00
|
|
|
|
|
|
|
|
|
functionMenu = menubar.addMenu(u'功能')
|
2017-01-04 13:52:20 +00:00
|
|
|
|
functionMenu.addAction(self.createAction(u'查询合约', self.openContract))
|
|
|
|
|
functionMenu.addAction(self.createAction(u'行情记录', self.openDr))
|
|
|
|
|
functionMenu.addAction(self.createAction(u'风控管理', self.openRm))
|
2015-10-16 08:50:44 +00:00
|
|
|
|
|
2015-11-20 06:11:29 +00:00
|
|
|
|
# 算法相关
|
|
|
|
|
algoMenu = menubar.addMenu(u'算法')
|
2017-01-04 13:52:20 +00:00
|
|
|
|
algoMenu.addAction(self.createAction(u'CTA策略', self.openCta))
|
2015-11-20 06:11:29 +00:00
|
|
|
|
|
|
|
|
|
# 帮助
|
2015-10-16 08:50:44 +00:00
|
|
|
|
helpMenu = menubar.addMenu(u'帮助')
|
2017-01-04 13:52:20 +00:00
|
|
|
|
helpMenu.addAction(self.createAction(u'还原', self.restoreWindow))
|
|
|
|
|
helpMenu.addAction(self.createAction(u'关于', self.openAbout))
|
|
|
|
|
helpMenu.addAction(self.createAction(u'测试', self.test))
|
2015-10-16 08:50:44 +00:00
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def initStatusBar(self):
|
|
|
|
|
"""初始化状态栏"""
|
|
|
|
|
self.statusLabel = QtGui.QLabel()
|
|
|
|
|
self.statusLabel.setAlignment(QtCore.Qt.AlignLeft)
|
|
|
|
|
|
|
|
|
|
self.statusBar().addPermanentWidget(self.statusLabel)
|
|
|
|
|
self.statusLabel.setText(self.getCpuMemory())
|
|
|
|
|
|
|
|
|
|
self.sbCount = 0
|
|
|
|
|
self.sbTrigger = 10 # 10秒刷新一次
|
2016-06-11 03:30:03 +00:00
|
|
|
|
self.signalStatusBar.connect(self.updateStatusBar)
|
|
|
|
|
self.eventEngine.register(EVENT_TIMER, self.signalStatusBar.emit)
|
2015-10-16 08:50:44 +00:00
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def updateStatusBar(self, event):
|
|
|
|
|
"""在状态栏更新CPU和内存信息"""
|
|
|
|
|
self.sbCount += 1
|
|
|
|
|
|
|
|
|
|
if self.sbCount == self.sbTrigger:
|
|
|
|
|
self.sbCount = 0
|
|
|
|
|
self.statusLabel.setText(self.getCpuMemory())
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def getCpuMemory(self):
|
|
|
|
|
"""获取CPU和内存状态信息"""
|
|
|
|
|
cpuPercent = psutil.cpu_percent()
|
|
|
|
|
memoryPercent = psutil.virtual_memory().percent
|
|
|
|
|
return u'CPU使用率:%d%% 内存使用率:%d%%' % (cpuPercent, memoryPercent)
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
2017-01-04 13:52:20 +00:00
|
|
|
|
def addConnectAction(self, menu, gatewayName, displayName=''):
|
|
|
|
|
"""增加连接功能"""
|
|
|
|
|
if gatewayName not in self.mainEngine.getAllGatewayNames():
|
|
|
|
|
return
|
2015-10-16 08:50:44 +00:00
|
|
|
|
|
2017-01-04 13:52:20 +00:00
|
|
|
|
def connect():
|
|
|
|
|
self.mainEngine.connect(gatewayName)
|
2015-12-17 02:15:12 +00:00
|
|
|
|
|
2017-01-04 13:52:20 +00:00
|
|
|
|
if not displayName:
|
|
|
|
|
displayName = gatewayName
|
|
|
|
|
actionName = u'连接' + displayName
|
2016-12-11 09:36:13 +00:00
|
|
|
|
|
2017-01-04 13:52:20 +00:00
|
|
|
|
menu.addAction(self.createAction(actionName, connect))
|
2016-03-06 10:09:15 +00:00
|
|
|
|
|
2016-07-12 15:38:26 +00:00
|
|
|
|
#----------------------------------------------------------------------
|
2017-01-04 13:52:20 +00:00
|
|
|
|
def createAction(self, actionName, function):
|
|
|
|
|
"""创建操作功能"""
|
|
|
|
|
action = QtGui.QAction(actionName, self)
|
|
|
|
|
action.triggered.connect(function)
|
|
|
|
|
return action
|
2016-07-12 15:38:26 +00:00
|
|
|
|
|
2015-10-16 08:50:44 +00:00
|
|
|
|
#----------------------------------------------------------------------
|
2015-12-23 06:15:41 +00:00
|
|
|
|
def test(self):
|
|
|
|
|
"""测试按钮用的函数"""
|
2016-04-16 12:48:54 +00:00
|
|
|
|
# 有需要使用手动触发的测试函数可以写在这里
|
|
|
|
|
pass
|
2015-12-23 06:15:41 +00:00
|
|
|
|
|
2015-10-16 08:50:44 +00:00
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def openAbout(self):
|
|
|
|
|
"""打开关于"""
|
|
|
|
|
try:
|
2015-11-20 06:11:29 +00:00
|
|
|
|
self.widgetDict['aboutW'].show()
|
|
|
|
|
except KeyError:
|
|
|
|
|
self.widgetDict['aboutW'] = AboutWidget(self)
|
|
|
|
|
self.widgetDict['aboutW'].show()
|
2015-10-16 08:50:44 +00:00
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def openContract(self):
|
|
|
|
|
"""打开合约查询"""
|
|
|
|
|
try:
|
2015-11-20 06:11:29 +00:00
|
|
|
|
self.widgetDict['contractM'].show()
|
|
|
|
|
except KeyError:
|
2015-12-30 14:01:17 +00:00
|
|
|
|
self.widgetDict['contractM'] = ContractMonitor(self.mainEngine)
|
2015-11-20 06:11:29 +00:00
|
|
|
|
self.widgetDict['contractM'].show()
|
2015-10-16 08:50:44 +00:00
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def openCta(self):
|
|
|
|
|
"""打开CTA组件"""
|
|
|
|
|
try:
|
2016-04-28 15:03:36 +00:00
|
|
|
|
self.widgetDict['ctaM'].showMaximized()
|
2015-11-20 06:11:29 +00:00
|
|
|
|
except KeyError:
|
|
|
|
|
self.widgetDict['ctaM'] = CtaEngineManager(self.mainEngine.ctaEngine, self.eventEngine)
|
2016-04-28 15:03:36 +00:00
|
|
|
|
self.widgetDict['ctaM'].showMaximized()
|
2016-04-15 12:20:49 +00:00
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def openDr(self):
|
|
|
|
|
"""打开行情数据记录组件"""
|
|
|
|
|
try:
|
2016-04-28 15:03:36 +00:00
|
|
|
|
self.widgetDict['drM'].showMaximized()
|
2016-04-15 12:20:49 +00:00
|
|
|
|
except KeyError:
|
|
|
|
|
self.widgetDict['drM'] = DrEngineManager(self.mainEngine.drEngine, self.eventEngine)
|
2016-04-28 15:03:36 +00:00
|
|
|
|
self.widgetDict['drM'].showMaximized()
|
2016-05-03 12:35:59 +00:00
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def openRm(self):
|
|
|
|
|
"""打开组件"""
|
|
|
|
|
try:
|
|
|
|
|
self.widgetDict['rmM'].show()
|
|
|
|
|
except KeyError:
|
|
|
|
|
self.widgetDict['rmM'] = RmEngineManager(self.mainEngine.rmEngine, self.eventEngine)
|
|
|
|
|
self.widgetDict['rmM'].show()
|
2015-10-16 08:50:44 +00:00
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def closeEvent(self, event):
|
|
|
|
|
"""关闭事件"""
|
|
|
|
|
reply = QtGui.QMessageBox.question(self, u'退出',
|
|
|
|
|
u'确认退出?', QtGui.QMessageBox.Yes |
|
|
|
|
|
QtGui.QMessageBox.No, QtGui.QMessageBox.No)
|
|
|
|
|
|
2015-11-20 06:11:29 +00:00
|
|
|
|
if reply == QtGui.QMessageBox.Yes:
|
|
|
|
|
for widget in self.widgetDict.values():
|
|
|
|
|
widget.close()
|
2016-10-19 15:45:49 +00:00
|
|
|
|
self.saveWindowSettings('custom')
|
2016-04-30 02:11:52 +00:00
|
|
|
|
|
2015-10-16 08:50:44 +00:00
|
|
|
|
self.mainEngine.exit()
|
|
|
|
|
event.accept()
|
|
|
|
|
else:
|
|
|
|
|
event.ignore()
|
2016-04-28 15:03:36 +00:00
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def createDock(self, widgetClass, widgetName, widgetArea):
|
|
|
|
|
"""创建停靠组件"""
|
|
|
|
|
widget = widgetClass(self.mainEngine, self.eventEngine)
|
|
|
|
|
dock = QtGui.QDockWidget(widgetName)
|
|
|
|
|
dock.setWidget(widget)
|
|
|
|
|
dock.setObjectName(widgetName)
|
|
|
|
|
dock.setFeatures(dock.DockWidgetFloatable|dock.DockWidgetMovable)
|
|
|
|
|
self.addDockWidget(widgetArea, dock)
|
|
|
|
|
return widget, dock
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
2016-10-19 15:45:49 +00:00
|
|
|
|
def saveWindowSettings(self, settingName):
|
2016-04-28 15:03:36 +00:00
|
|
|
|
"""保存窗口设置"""
|
2016-10-19 15:45:49 +00:00
|
|
|
|
settings = QtCore.QSettings('vn.trader', settingName)
|
2016-04-28 15:03:36 +00:00
|
|
|
|
settings.setValue('state', self.saveState())
|
|
|
|
|
settings.setValue('geometry', self.saveGeometry())
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
2016-10-19 15:45:49 +00:00
|
|
|
|
def loadWindowSettings(self, settingName):
|
2016-04-28 15:03:36 +00:00
|
|
|
|
"""载入窗口设置"""
|
2016-10-19 15:45:49 +00:00
|
|
|
|
settings = QtCore.QSettings('vn.trader', settingName)
|
2016-06-01 15:28:11 +00:00
|
|
|
|
# 这里由于PyQt4的版本不同,settings.value('state')调用返回的结果可能是:
|
|
|
|
|
# 1. None(初次调用,注册表里无相应记录,因此为空)
|
|
|
|
|
# 2. QByteArray(比较新的PyQt4)
|
|
|
|
|
# 3. QVariant(以下代码正确执行所需的返回结果)
|
|
|
|
|
# 所以为了兼容考虑,这里加了一个try...except,如果是1、2的情况就pass
|
|
|
|
|
# 可能导致主界面的设置无法载入(每次退出时的保存其实是成功了)
|
|
|
|
|
try:
|
|
|
|
|
self.restoreState(settings.value('state').toByteArray())
|
|
|
|
|
self.restoreGeometry(settings.value('geometry').toByteArray())
|
|
|
|
|
except AttributeError:
|
|
|
|
|
pass
|
2016-10-19 15:45:49 +00:00
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def restoreWindow(self):
|
|
|
|
|
"""还原默认窗口设置(还原停靠组件位置)"""
|
|
|
|
|
self.loadWindowSettings('default')
|
|
|
|
|
self.showMaximized()
|
2015-10-16 08:50:44 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
########################################################################
|
|
|
|
|
class AboutWidget(QtGui.QDialog):
|
|
|
|
|
"""显示关于信息"""
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def __init__(self, parent=None):
|
|
|
|
|
"""Constructor"""
|
|
|
|
|
super(AboutWidget, self).__init__(parent)
|
|
|
|
|
|
|
|
|
|
self.initUi()
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
def initUi(self):
|
|
|
|
|
""""""
|
2016-04-16 12:48:54 +00:00
|
|
|
|
self.setWindowTitle(u'关于VnTrader')
|
2015-10-16 08:50:44 +00:00
|
|
|
|
|
|
|
|
|
text = u"""
|
2017-01-04 13:52:20 +00:00
|
|
|
|
Developed by Traders, for Traders.
|
2015-10-16 08:50:44 +00:00
|
|
|
|
|
|
|
|
|
License:MIT
|
|
|
|
|
|
2016-04-16 12:48:54 +00:00
|
|
|
|
Website:www.vnpy.org
|
|
|
|
|
|
|
|
|
|
Github:www.github.com/vnpy/vnpy
|
2015-10-16 08:50:44 +00:00
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
label = QtGui.QLabel()
|
|
|
|
|
label.setText(text)
|
|
|
|
|
label.setMinimumWidth(500)
|
|
|
|
|
|
|
|
|
|
vbox = QtGui.QVBoxLayout()
|
|
|
|
|
vbox.addWidget(label)
|
|
|
|
|
|
|
|
|
|
self.setLayout(vbox)
|
|
|
|
|
|