diff --git a/vnpy/trader/app/spreadTrading/st.ico b/vnpy/trader/app/spreadTrading/st.ico index a66c245e..4d0ad199 100644 Binary files a/vnpy/trader/app/spreadTrading/st.ico and b/vnpy/trader/app/spreadTrading/st.ico differ diff --git a/vnpy/trader/app/spreadTrading/stAlgo.py b/vnpy/trader/app/spreadTrading/stAlgo.py index e3903407..3cb24c70 100644 --- a/vnpy/trader/app/spreadTrading/stAlgo.py +++ b/vnpy/trader/app/spreadTrading/stAlgo.py @@ -12,9 +12,9 @@ from vnpy.trader.vtConstant import (EMPTY_INT, EMPTY_FLOAT, ######################################################################## class StAlgoTemplate(object): """价差算法交易模板""" - MODE_LONGSHORT = 1 - MODE_LONGONLY = 2 - MODE_SHORTONLY = 3 + MODE_LONGSHORT = u'双向' + MODE_LONGONLY = u'做多' + MODE_SHORTONLY = u'做空' SPREAD_LONG = 1 SPREAD_SHORT = 2 @@ -65,7 +65,7 @@ class StAlgoTemplate(object): raise NotImplementedError #---------------------------------------------------------------------- - def start(self, mode=MODE_LONGSHORT): + def start(self): """""" raise NotImplementedError @@ -74,7 +74,79 @@ class StAlgoTemplate(object): """""" raise NotImplementedError + #---------------------------------------------------------------------- + def setBuyPrice(self, buyPrice): + """设置买开的价格""" + self.buyPrice = buyPrice + + #---------------------------------------------------------------------- + def setSellPrice(self, sellPrice): + """设置卖平的价格""" + self.sellPrice = sellPrice + + #---------------------------------------------------------------------- + def setShortPrice(self, shortPrice): + """设置卖开的价格""" + self.shortPrice = shortPrice + + #---------------------------------------------------------------------- + def setCoverPrice(self, coverPrice): + """设置买平的价格""" + self.coverPrice = coverPrice + + #---------------------------------------------------------------------- + def setMode(self, mode): + """设置算法交易方向""" + self.mode = mode + + #---------------------------------------------------------------------- + def setMaxOrderSize(self, maxOrderSize): + """设置最大单笔委托数量""" + self.maxOrderSize = maxOrderSize + + #---------------------------------------------------------------------- + def setMaxPosSize(self, maxPosSize): + """设置最大持仓数量""" + self.maxPosSize = maxPosSize + + #---------------------------------------------------------------------- + def putEvent(self): + """发出算法更新事件""" + self.algoEngine.putAlgoEvent(self) + + #---------------------------------------------------------------------- + def writeLog(self, content): + """输出算法日志""" + content = ':'.join([self.spreadName, content]) + self.algoEngine.writeLog(content) + #---------------------------------------------------------------------- + def getAlgoParams(self): + """获取算法参数""" + d = { + "spreadName": self.spreadName, + "algoName": self.algoName, + "buyPrice": self.buyPrice, + "sellPrice": self.sellPrice, + "shortPrice": self.shortPrice, + "coverPrice": self.coverPrice, + "maxOrderSize": self.maxOrderSize, + "maxPosSize": self.maxPosSize, + "mode": self.mode + } + return d + + #---------------------------------------------------------------------- + def setAlgoParams(self, d): + """设置算法参数""" + self.buyPrice = d.get('buyPrice', EMPTY_FLOAT) + self.sellPrice = d.get('sellPrice', EMPTY_FLOAT) + self.shortPrice = d.get('shortPrice', EMPTY_FLOAT) + self.coverPrice = d.get('coverPrice', EMPTY_FLOAT) + self.maxOrderSize = d.get('maxOrderSize', EMPTY_INT) + self.maxPosSize = d.get('maxPosSize', EMPTY_INT) + self.mode = d.get('mode', self.MODE_LONGSHORT) + ######################################################################## class SniperAlgo(StAlgoTemplate): @@ -82,9 +154,9 @@ class SniperAlgo(StAlgoTemplate): FINISHED_STATUS = [STATUS_ALLTRADED, STATUS_CANCELLED, STATUS_REJECTED] #---------------------------------------------------------------------- - def __init__(self, algoEngine): + def __init__(self, algoEngine, spread): """Constructor""" - super(SniperAlgo, self).__init__(algoEngine) + super(SniperAlgo, self).__init__(algoEngine, spread) self.algoName = u'Sniper' self.quoteInterval = 2 # 主动腿报价撤单再发前等待的时间 @@ -125,11 +197,13 @@ class SniperAlgo(StAlgoTemplate): spread.netPos < self.maxPosSize and spread.askPrice <= self.buyPrice): self.quoteActiveLeg(self.SPREAD_LONG) + self.writeLog(u'买入开仓') # 卖出 elif (spread.netPos > 0 and spread.bidPrice >= self.sellPrice): self.quoteActiveLeg(self.SPREAD_SHORT) + self.writeLog(u'卖出平仓') # 允许做空 if self.mode == self.MODE_LONGSHORT or self.mode == self.MODE_SHORTONLY: @@ -138,11 +212,13 @@ class SniperAlgo(StAlgoTemplate): spread.netPos > -self.maxPosSize and spread.bidPrice >= self.shortPrice): self.quoteActiveLeg(self.SPREAD_SHORT) + self.writeLog(u'卖出开仓') # 平空 elif (spread.netPos < 0 and spread.askPrice <= self.coverPrice): self.quoteActiveLeg(self.SPREAD_LONG) + self.writeLog(u'买入平仓') #---------------------------------------------------------------------- def updateSpreadPos(self, spread): @@ -180,10 +256,14 @@ class SniperAlgo(StAlgoTemplate): # 检查若是被动腿,且已经没有未完成委托,则执行对冲 if not orderList and vtSymbol in self.passiveVtSymbols: self.hedgePassiveLeg(vtSymbol) + self.writeLog(u'发出新的被动腿%s对冲单' %vtSymbol) #---------------------------------------------------------------------- def updateTimer(self): """计时更新""" + if not self.active: + return + self.quoteCount += 1 self.hedgeCount += 1 @@ -200,10 +280,14 @@ class SniperAlgo(StAlgoTemplate): self.hedgeCount = 0 #---------------------------------------------------------------------- - def start(self, mode=MODE_LONGSHORT): + def start(self): """启动""" - self.mode = mode self.active = True + + self.quoteCount = 0 + self.hedgeCount = 0 + + return self.active #---------------------------------------------------------------------- def stop(self): @@ -212,6 +296,8 @@ class SniperAlgo(StAlgoTemplate): self.hedgingTaskDict.clear() self.cancelAllOrders() + + return self.active #---------------------------------------------------------------------- def sendLegOrder(self, leg, legVolume): @@ -310,6 +396,9 @@ class SniperAlgo(StAlgoTemplate): self.hedgingTaskDict[leg.vtSymbol] = newHedgingTask else: self.hedgingTaskDict[leg.vtSymbol] += newHedgingTask + + # 输出日志 + self.writeLog(u'主动腿%s成交,方向%s,数量%s' %(trade.vtSymbol, trade.direction, trade.volume)) #---------------------------------------------------------------------- def newPassiveLegTrade(self, trade): @@ -328,6 +417,9 @@ class SniperAlgo(StAlgoTemplate): if not self.hedgingTaskDict[trade.vtSymbol]: del self.hedgingTaskDict[trade.vtSymbol] + # 输出日志 + self.writeLog(u'被动腿%s成交,方向%s,数量%s' %(trade.vtSymbol, trade.direction, trade.volume)) + #---------------------------------------------------------------------- def cancelLegOrder(self, vtSymbol): """撤销某条腿的委托""" @@ -339,15 +431,27 @@ class SniperAlgo(StAlgoTemplate): for vtOrderID in orderList: self.algoEngine.cancelOrder(vtOrderID) + self.writeLog(u'撤单%s的所有委托' %vtSymbol) + #---------------------------------------------------------------------- def cancelAllOrders(self): """撤销全部委托""" for orderList in self.legOrderDict.values(): for vtOrderID in orderList: self.algoEngine.cancelOrder(vtOrderID) + + self.writeLog(u'全部撤单') #---------------------------------------------------------------------- def cancelAllPassiveLegOrders(self): """撤销全部被动腿委托""" + cancelPassive = False + for vtSymbol in self.passiveVtSymbols: - self.cancelLegOrder(vtSymbol) \ No newline at end of file + if self.legOrderDict[vtSymbol]: + self.cancelLegOrder(vtSymbol) + cancelPassive = True + + # 只有确实发出撤单委托时,才输出信息 + if cancelPassive: + self.writeLog(u'被动腿全撤') diff --git a/vnpy/trader/app/spreadTrading/stBase.py b/vnpy/trader/app/spreadTrading/stBase.py index 64f0f00f..71f25f15 100644 --- a/vnpy/trader/app/spreadTrading/stBase.py +++ b/vnpy/trader/app/spreadTrading/stBase.py @@ -13,6 +13,8 @@ from vnpy.trader.vtConstant import (EMPTY_INT, EMPTY_FLOAT, EVENT_SPREADTRADING_TICK = 'eSpreadTradingTick.' EVENT_SPREADTRADING_POS = 'eSpreadTradingPos.' EVENT_SPREADTRADING_LOG = 'eSpreadTradingLog' +EVENT_SPREADTRADING_ALGO = 'eSpreadTradingAlgo.' +EVENT_SPREADTRADING_ALGOLOG = 'eSpreadTradingAlgoLog' diff --git a/vnpy/trader/app/spreadTrading/stEngine.py b/vnpy/trader/app/spreadTrading/stEngine.py index c93bb3cd..71e89c31 100644 --- a/vnpy/trader/app/spreadTrading/stEngine.py +++ b/vnpy/trader/app/spreadTrading/stEngine.py @@ -2,10 +2,10 @@ import json import traceback -from copy import copy +import shelve from vnpy.event import Event -from vnpy.trader.vtFunction import getJsonPath +from vnpy.trader.vtFunction import getJsonPath, getTempPath from vnpy.trader.vtEvent import (EVENT_TICK, EVENT_TRADE, EVENT_POSITION, EVENT_TIMER, EVENT_ORDER) from vnpy.trader.vtObject import (VtSubscribeReq, VtOrderReq, @@ -14,7 +14,9 @@ from vnpy.trader.vtConstant import (DIRECTION_LONG, DIRECTION_SHORT, OFFSET_OPEN, OFFSET_CLOSE) from .stBase import (StLeg, StSpread, EVENT_SPREADTRADING_TICK, - EVENT_SPREADTRADING_POS, EVENT_SPREADTRADING_LOG) + EVENT_SPREADTRADING_POS, EVENT_SPREADTRADING_LOG, + EVENT_SPREADTRADING_ALGO, EVENT_SPREADTRADING_ALGOLOG) +from .stAlgo import SniperAlgo ######################################################################## @@ -121,6 +123,9 @@ class StDataEngine(object): # 初始化价差 spread.initSpread() + self.putSpreadTickEvent(spread) + self.putSpreadPosEvent(spread) + # 返回结果 result = True msg = u'%s价差创建成功' %spread.name @@ -145,7 +150,12 @@ class StDataEngine(object): spread = self.vtSymbolSpreadDict[tick.vtSymbol] spread.calculatePrice() - # 推送价差行情更新 + # 发出事件 + self.putSpreadTickEvent(spread) + + #---------------------------------------------------------------------- + def putSpreadTickEvent(self, spread): + """发出价差行情更新事件""" event1 = Event(EVENT_SPREADTRADING_TICK+spread.name) event1.dict_['data'] = spread self.eventEngine.put(event1) @@ -215,13 +225,19 @@ class StDataEngine(object): spread.calculatePos() # 推送价差持仓更新 + self.putSpreadPosEvent(spread) + + #---------------------------------------------------------------------- + def putSpreadPosEvent(self, spread): + """发出价差持仓事件""" event1 = Event(EVENT_SPREADTRADING_POS+spread.name) event1.dict_['data'] = spread self.eventEngine.put(event1) - + event2 = Event(EVENT_SPREADTRADING_POS) event2.dict_['data'] = spread - self.eventEngine.put(event2) + self.eventEngine.put(event2) + #---------------------------------------------------------------------- def registerEvent(self): @@ -236,6 +252,7 @@ class StDataEngine(object): contract = self.mainEngine.getContract(vtSymbol) if not contract: self.writeLog(u'订阅行情失败,找不到该合约%s' %vtSymbol) + return req = VtSubscribeReq() req.symbol = contract.symbol @@ -254,18 +271,21 @@ class StDataEngine(object): self.eventEngine.put(event) #---------------------------------------------------------------------- - def stop(self): - """停止""" - pass + def getAllSpreads(self): + """获取所有的价差""" + return self.spreadDict.values() ######################################################################## class StAlgoEngine(object): """价差算法交易引擎""" + algoFileName = 'SpreadTradingAlgo.vt' + algoFilePath = getTempPath(algoFileName) #---------------------------------------------------------------------- - def __init__(self, mainEngine, eventEngine): + def __init__(self, dataEngine, mainEngine, eventEngine): """Constructor""" + self.dataEngine = dataEngine self.mainEngine = mainEngine self.eventEngine = eventEngine @@ -387,6 +407,122 @@ class StAlgoEngine(object): vtOrderID = self.sendOrder(vtSymbol, DIRECTION_LONG, OFFSET_CLOSE, price, volume, payup) return [vtOrderID] + #---------------------------------------------------------------------- + def putAlgoEvent(self, algo): + """发出算法状态更新事件""" + event = Event(EVENT_SPREADTRADING_ALGO+algo.name) + self.eventEngine.put(event) + + #---------------------------------------------------------------------- + def writeLog(self, content): + """输出日志""" + log = VtLogData() + log.logContent = content + + event = Event(EVENT_SPREADTRADING_ALGOLOG) + event.dict_['data'] = log + + self.eventEngine.put(event) + + #---------------------------------------------------------------------- + def saveSetting(self): + """保存算法配置""" + setting = {} + for algo in self.algoDict.values(): + setting[algo.spreadName] = algo.getAlgoParams() + + f = shelve.open(self.algoFilePath) + f['setting'] = setting + f.close() + + #---------------------------------------------------------------------- + def loadSetting(self): + """加载算法配置""" + # 创建算法对象 + l = self.dataEngine.getAllSpreads() + for spread in l: + self.algoDict[spread.name] = SniperAlgo(self, spread) + + # 加载配置 + f = shelve.open(self.algoFilePath) + setting = f.get('setting', None) + f.close() + + if not setting: + return + + for algo in self.algoDict.values(): + if algo.spreadName in setting: + d = setting[algo.spreadName] + algo.setAlgoParams(d) + + #---------------------------------------------------------------------- + def stopAll(self): + """停止全部算法""" + for algo in self.algoDict.values(): + algo.stop() + + #---------------------------------------------------------------------- + def startAlgo(self, spreadName): + """启动算法""" + algo = self.algoDict[spreadName] + algoActive = algo.start() + return algoActive + + #---------------------------------------------------------------------- + def stopAlgo(self, spreadName): + """停止算法""" + algo = self.algoDict[spreadName] + algoActive = algo.stop() + return algoActive + + #---------------------------------------------------------------------- + def getAllAlgoParams(self): + """获取所有算法的参数""" + return [algo.getAlgoParams() for algo in self.algoDict.values()] + + #---------------------------------------------------------------------- + def setAlgoBuyPrice(self, spreadName, buyPrice): + """设置算法买开价格""" + algo = self.algoDict[spreadName] + algo.setBuyPrice(buyPrice) + + #---------------------------------------------------------------------- + def setAlgoSellPrice(self, spreadName, sellPrice): + """设置算法卖平价格""" + algo = self.algoDict[spreadName] + algo.setSellPrice(sellPrice) + + #---------------------------------------------------------------------- + def setAlgoShortPrice(self, spreadName, shortPrice): + """设置算法卖开价格""" + algo = self.algoDict[spreadName] + algo.setShortPrice(shortPrice) + + #---------------------------------------------------------------------- + def setAlgoCoverPrice(self, spreadName, coverPrice): + """设置算法买平价格""" + algo = self.algoDict[spreadName] + algo.setCoverPrice(coverPrice) + + #---------------------------------------------------------------------- + def setAlgoMode(self, spreadName, mode): + """设置算法工作模式""" + algo = self.algoDict[spreadName] + algo.setMode(mode) + + #---------------------------------------------------------------------- + def setAlgoMaxOrderSize(self, spreadName, maxOrderSize): + """设置算法单笔委托限制""" + algo = self.algoDict[spreadName] + algo.setMaxOrderSize(maxOrderSize) + + #---------------------------------------------------------------------- + def setAlgoMaxPosSize(self, spreadName, maxPosSize): + """设置算法持仓限制""" + algo = self.algoDict[spreadName] + algo.setMaxPosSize(maxPosSize) + ######################################################################## class StEngine(object): @@ -399,12 +535,21 @@ class StEngine(object): self.eventEngine = eventEngine self.dataEngine = StDataEngine(mainEngine, eventEngine) - self.algoEngine = StAlgoEngine(mainEngine, eventEngine) + self.algoEngine = StAlgoEngine(self.dataEngine, mainEngine, eventEngine) #---------------------------------------------------------------------- - def loadSetting(self): - """""" + def init(self): + """初始化""" self.dataEngine.loadSetting() + self.algoEngine.loadSetting() + + #---------------------------------------------------------------------- + def stop(self): + """停止""" + self.dataEngine.saveSetting() + + self.algoEngine.stopAll() + self.algoEngine.saveSetting() diff --git a/vnpy/trader/app/spreadTrading/uiStWidget.py b/vnpy/trader/app/spreadTrading/uiStWidget.py index 9244fc4d..76fd714e 100644 --- a/vnpy/trader/app/spreadTrading/uiStWidget.py +++ b/vnpy/trader/app/spreadTrading/uiStWidget.py @@ -2,14 +2,20 @@ from collections import OrderedDict -from vnpy.trader.uiQt import QtWidgets +from vnpy.event import Event +from vnpy.trader.uiQt import QtWidgets, QtCore from vnpy.trader.uiBasicWidget import (BasicMonitor, BasicCell, PnlCell, AskCell, BidCell, BASIC_FONT) from .stBase import (EVENT_SPREADTRADING_TICK, EVENT_SPREADTRADING_POS, - EVENT_SPREADTRADING_LOG) + EVENT_SPREADTRADING_LOG, EVENT_SPREADTRADING_ALGO, + EVENT_SPREADTRADING_ALGOLOG) +from .stAlgo import StAlgoTemplate +STYLESHEET_START = 'background-color: rgb(111,255,244); color: black' +STYLESHEET_STOP = 'background-color: rgb(255,201,111); color: black' + ######################################################################## class StTickMonitor(BasicMonitor): @@ -64,25 +70,365 @@ class StPosMonitor(BasicMonitor): ######################################################################## -class StLogMonitor(BasicMonitor): +class StLogMonitor(QtWidgets.QTextEdit): + """价差日志监控""" + signal = QtCore.pyqtSignal(type(Event())) + + #---------------------------------------------------------------------- + def __init__(self, mainEngine, eventEngine, parent=None): + """Constructor""" + super(StLogMonitor, self).__init__(parent) + + self.eventEngine = eventEngine + + self.registerEvent() + + #---------------------------------------------------------------------- + def processLogEvent(self, event): + """处理日志事件""" + log = event.dict_['data'] + content = '%s:%s' %(log.logTime, log.logContent) + self.append(content) + + #---------------------------------------------------------------------- + def registerEvent(self): + """注册事件监听""" + self.signal.connect(self.processLogEvent) + + self.eventEngine.register(EVENT_SPREADTRADING_LOG, self.signal.emit) + + +######################################################################## +class StAlgoLogMonitor(BasicMonitor): """价差日志监控""" #---------------------------------------------------------------------- def __init__(self, mainEngine, eventEngine, parent=None): """Constructor""" - super(StLogMonitor, self).__init__(mainEngine, eventEngine, parent) + super(StAlgoLogMonitor, self).__init__(mainEngine, eventEngine, parent) d = OrderedDict() d['logTime'] = {'chinese':u'时间', 'cellType':BasicCell} - d['logContent'] = {'chinese':u'日志', 'cellType':BasicCell} + d['logContent'] = {'chinese':u'信息', 'cellType':BasicCell} self.setHeaderDict(d) - self.setEventType(EVENT_SPREADTRADING_LOG) + self.setEventType(EVENT_SPREADTRADING_ALGOLOG) self.setFont(BASIC_FONT) self.initTable() - self.registerEvent() + self.registerEvent() + +######################################################################## +class StBuyPriceSpinBox(QtWidgets.QDoubleSpinBox): + """""" + + #---------------------------------------------------------------------- + def __init__(self, algoEngine, spreadName, price, parent=None): + """Constructor""" + super(StBuyPriceSpinBox, self).__init__(parent) + + self.algoEngine = algoEngine + self.spreadName = spreadName + + self.setDecimals(4) + self.setRange(-10000, 10000) + self.setValue(price) + + self.valueChanged.connect(self.setPrice) + + #---------------------------------------------------------------------- + def setPrice(self, value): + """设置价格""" + self.algoEngine.setAlgoBuyPrice(self.spreadName, value) + + +######################################################################## +class StSellPriceSpinBox(QtWidgets.QDoubleSpinBox): + """""" + + #---------------------------------------------------------------------- + def __init__(self, algoEngine, spreadName, price, parent=None): + """Constructor""" + super(StSellPriceSpinBox, self).__init__(parent) + + self.algoEngine = algoEngine + self.spreadName = spreadName + + self.setDecimals(4) + self.setRange(-10000, 10000) + self.setValue(price) + + self.valueChanged.connect(self.setPrice) + + #---------------------------------------------------------------------- + def setPrice(self, value): + """设置价格""" + self.algoEngine.setAlgoSellPrice(self.spreadName, value) + + +######################################################################## +class StShortPriceSpinBox(QtWidgets.QDoubleSpinBox): + """""" + + #---------------------------------------------------------------------- + def __init__(self, algoEngine, spreadName, price, parent=None): + """Constructor""" + super(StShortPriceSpinBox, self).__init__(parent) + + self.algoEngine = algoEngine + self.spreadName = spreadName + + self.setDecimals(4) + self.setRange(-10000, 10000) + self.setValue(price) + + self.valueChanged.connect(self.setPrice) + + #---------------------------------------------------------------------- + def setPrice(self, value): + """设置价格""" + self.algoEngine.setAlgoShortPrice(self.spreadName, value) + + +######################################################################## +class StCoverPriceSpinBox(QtWidgets.QDoubleSpinBox): + """""" + + #---------------------------------------------------------------------- + def __init__(self, algoEngine, spreadName, price, parent=None): + """Constructor""" + super(StCoverPriceSpinBox, self).__init__(parent) + + self.algoEngine = algoEngine + self.spreadName = spreadName + + self.setDecimals(4) + self.setRange(-10000, 10000) + self.setValue(price) + + self.valueChanged.connect(self.setPrice) + + #---------------------------------------------------------------------- + def setPrice(self, value): + """设置价格""" + self.algoEngine.setAlgoCoverPrice(self.spreadName, value) + + +######################################################################## +class StMaxPosSizeSpinBox(QtWidgets.QSpinBox): + """""" + + #---------------------------------------------------------------------- + def __init__(self, algoEngine, spreadName, size, parent=None): + """Constructor""" + super(StMaxPosSizeSpinBox, self).__init__(parent) + + self.algoEngine = algoEngine + self.spreadName = spreadName + + self.setRange(-10000, 10000) + self.setValue(size) + + self.valueChanged.connect(self.setSize) + + #---------------------------------------------------------------------- + def setSize(self, size): + """设置价格""" + self.algoEngine.setAlgoMaxPosSize(self.spreadName, size) + + +######################################################################## +class StMaxOrderSizeSpinBox(QtWidgets.QSpinBox): + """""" + + #---------------------------------------------------------------------- + def __init__(self, algoEngine, spreadName, size, parent=None): + """Constructor""" + super(StMaxOrderSizeSpinBox, self).__init__(parent) + + self.algoEngine = algoEngine + self.spreadName = spreadName + + self.setRange(-10000, 10000) + self.setValue(size) + + self.valueChanged.connect(self.setSize) + + #---------------------------------------------------------------------- + def setSize(self, size): + """设置价格""" + self.algoEngine.setAlgoMaxOrderSize(self.spreadName, size) + + +######################################################################## +class StModeComboBox(QtWidgets.QComboBox): + """""" + + #---------------------------------------------------------------------- + def __init__(self, algoEngine, spreadName, mode, parent=None): + """Constructor""" + super(StModeComboBox, self).__init__(parent) + + self.algoEngine = algoEngine + self.spreadName = spreadName + + l = [StAlgoTemplate.MODE_LONGSHORT, + StAlgoTemplate.MODE_LONGONLY, + StAlgoTemplate.MODE_SHORTONLY] + self.addItems(l) + self.setCurrentIndex(l.index(mode)) + + self.currentIndexChanged.connect(self.setMode) + + #---------------------------------------------------------------------- + def setMode(self): + """设置模式""" + mode = unicode(self.currentText()) + self.algoEngine.setAlgoMode(self.spreadName, mode) + + #---------------------------------------------------------------------- + def algoActiveChanged(self, active): + """算法运行状态改变""" + # 只允许算法停止时修改运行模式 + if active: + self.setEnabled(False) + else: + self.setEnabled(True) + + +######################################################################## +class StActiveButton(QtWidgets.QPushButton): + """""" + signalActive = QtCore.pyqtSignal(bool) + + #---------------------------------------------------------------------- + def __init__(self, algoEngine, spreadName, parent=None): + """Constructor""" + super(StActiveButton, self).__init__(parent) + + self.algoEngine = algoEngine + self.spreadName = spreadName + + self.active = False + self.setStopped() + + self.clicked.connect(self.buttonClicked) + + #---------------------------------------------------------------------- + def buttonClicked(self): + """改变运行模式""" + if self.active: + algoActive = self.algoEngine.stopAlgo(self.spreadName) + + if not algoActive: + self.setStopped() + else: + algoActive = self.algoEngine.startAlgo(self.spreadName) + + if algoActive: + self.setStarted() + + #---------------------------------------------------------------------- + def setStarted(self): + """算法启动""" + self.setText(u'运行中') + self.setStyleSheet(STYLESHEET_START) + + self.active = True + self.signalActive.emit(self.active) + + #---------------------------------------------------------------------- + def setStopped(self): + """算法停止""" + self.setText(u'已停止') + self.setStyleSheet(STYLESHEET_STOP) + + self.active = False + self.signalActive.emit(self.active) + + +######################################################################## +class StAlgoManager(QtWidgets.QTableWidget): + """""" + + #---------------------------------------------------------------------- + def __init__(self, stEngine, parent=None): + """Constructor""" + super(StAlgoManager, self).__init__(parent) + + self.algoEngine = stEngine.algoEngine + + self.initUi() + + #---------------------------------------------------------------------- + def initUi(self): + """初始化表格""" + headers = [u'价差', + u'算法', + 'BuyPrice', + 'SellPrice', + 'CoverPrice', + 'ShortPrice', + u'委托上限', + u'持仓上限', + u'模式', + u'状态'] + self.setColumnCount(len(headers)) + self.setHorizontalHeaderLabels(headers) + self.horizontalHeader().setResizeMode(QtWidgets.QHeaderView.Stretch) + + self.verticalHeader().setVisible(False) + self.setEditTriggers(self.NoEditTriggers) + + #---------------------------------------------------------------------- + def initCells(self): + """初始化单元格""" + algoEngine = self.algoEngine + + l = self.algoEngine.getAllAlgoParams() + self.setRowCount(len(l)) + + for row, d in enumerate(l): + cellSpreadName = QtWidgets.QTableWidgetItem(d['spreadName']) + cellAlgoName = QtWidgets.QTableWidgetItem(d['algoName']) + spinBuyPrice = StBuyPriceSpinBox(algoEngine, d['spreadName'], d['buyPrice']) + spinSellPrice = StSellPriceSpinBox(algoEngine, d['spreadName'], d['sellPrice']) + spinShortPrice = StShortPriceSpinBox(algoEngine, d['spreadName'], d['shortPrice']) + spinCoverPrice = StCoverPriceSpinBox(algoEngine, d['spreadName'], d['coverPrice']) + spinMaxOrderSize = StMaxOrderSizeSpinBox(algoEngine, d['spreadName'], d['maxOrderSize']) + spinMaxPosSize = StMaxPosSizeSpinBox(algoEngine, d['spreadName'], d['maxPosSize']) + comboMode = StModeComboBox(algoEngine, d['spreadName'], d['mode']) + buttonActive = StActiveButton(algoEngine, d['spreadName']) + + self.setItem(row, 0, cellSpreadName) + self.setItem(row, 1, cellAlgoName) + self.setCellWidget(row, 2, spinBuyPrice) + self.setCellWidget(row, 3, spinSellPrice) + self.setCellWidget(row, 4, spinCoverPrice) + self.setCellWidget(row, 5, spinShortPrice) + self.setCellWidget(row, 6, spinMaxOrderSize) + self.setCellWidget(row, 7, spinMaxPosSize) + self.setCellWidget(row, 8, comboMode) + self.setCellWidget(row, 9, buttonActive) + + buttonActive.signalActive.connect(comboMode.algoActiveChanged) + + +######################################################################## +class StGroup(QtWidgets.QGroupBox): + """集合显示""" + + #---------------------------------------------------------------------- + def __init__(self, widget, title, parent=None): + """Constructor""" + super(StGroup, self).__init__(parent) + + self.setTitle(title) + vbox = QtWidgets.QVBoxLayout() + vbox.addWidget(widget) + self.setLayout(vbox) + ######################################################################## class StManager(QtWidgets.QWidget): @@ -105,37 +451,48 @@ class StManager(QtWidgets.QWidget): self.setWindowTitle(u'价差交易') # 创建按钮 - buttonLoadSetting = QtWidgets.QPushButton(u'加载配置') - - buttonLoadSetting.clicked.connect(self.stEngine.loadSetting) + buttonInit = QtWidgets.QPushButton(u'初始化') + buttonInit.clicked.connect(self.init) # 创建组件 tickMonitor = StTickMonitor(self.mainEngine, self.eventEngine) posMonitor = StPosMonitor(self.mainEngine, self.eventEngine) logMonitor = StLogMonitor(self.mainEngine, self.eventEngine) + self.algoManager = StAlgoManager(self.stEngine) + algoLogMonitor = StAlgoLogMonitor(self.mainEngine, self.eventEngine) + + # 创建集合 + groupTick = StGroup(tickMonitor, u'价差行情') + groupPos = StGroup(posMonitor, u'价差持仓') + groupLog = StGroup(logMonitor, u'日志信息') + groupAlgo = StGroup(self.algoManager, u'价差算法') + groupAlgoLog = StGroup(algoLogMonitor, u'算法信息') # 设置布局 - hbox1 = QtWidgets.QHBoxLayout() - hbox1.addWidget(buttonLoadSetting) - hbox1.addStretch() + hbox = QtWidgets.QHBoxLayout() + hbox.addWidget(buttonInit) + hbox.addStretch() - hbox2 = QtWidgets.QHBoxLayout() - hbox2.addWidget(tickMonitor) - hbox2.addWidget(posMonitor) - - vbox = QtWidgets.QVBoxLayout() - vbox.addLayout(hbox1) - vbox.addLayout(hbox2) - vbox.addWidget(logMonitor) - - self.setLayout(vbox) + grid = QtWidgets.QGridLayout() + grid.addLayout(hbox, 0, 0, 1, 2) + grid.addWidget(groupTick, 1, 0) + grid.addWidget(groupPos, 1, 1) + grid.addWidget(groupAlgo, 2, 0, 1, 2) + grid.addWidget(groupLog, 3, 0) + grid.addWidget(groupAlgoLog, 3, 1) + + self.setLayout(grid) #---------------------------------------------------------------------- def show(self): """重载显示""" self.showMaximized() - + #---------------------------------------------------------------------- + def init(self): + """初始化""" + self.stEngine.init() + self.algoManager.initCells()