diff --git a/examples/CryptoTrader/uiCryptoWidget.py b/examples/CryptoTrader/uiCryptoWidget.py index d90a6bda..c4b8e08a 100644 --- a/examples/CryptoTrader/uiCryptoWidget.py +++ b/examples/CryptoTrader/uiCryptoWidget.py @@ -497,10 +497,11 @@ class LogMonitor(BasicMonitor): cellLogContent = BasicCell(logContent) cellGatewayName = BasicCell(error.gatewayName) - self.setItem(0, 0, cellLogTime) - self.setItem(0, 1, cellLogContent) - self.setItem(0, 2, cellGatewayName) - + self.setItem(0, 0, cellGatewayName) + self.setItem(0, 1, cellLogTime) + self.setItem(0, 2, cellLogContent) + + ######################################################################## class TradeMonitor(BasicMonitor): diff --git a/vnpy/api/coinbase/vncoinbase.py b/vnpy/api/coinbase/vncoinbase.py index 7efad269..04b3b4d2 100644 --- a/vnpy/api/coinbase/vncoinbase.py +++ b/vnpy/api/coinbase/vncoinbase.py @@ -199,18 +199,14 @@ class CoinbaseWebsocketApi(object): def run(self): """运行""" while self.active: - stream = self.ws.recv() - data = json.loads(stream) - self.onData(data) - - #try: - #stream = self.ws.recv() - #data = json.loads(stream) - #self.onData(data) - #except: - #msg = traceback.format_exc() - #self.onError(msg) - #self.reconnect() + try: + stream = self.ws.recv() + data = json.loads(stream) + self.onData(data) + except: + msg = traceback.format_exc() + self.onError(msg) + self.reconnect() #---------------------------------------------------------------------- def close(self): diff --git a/vnpy/trader/app/algoTrading/algo/arbitrageAlgo.py b/vnpy/trader/app/algoTrading/algo/arbitrageAlgo.py new file mode 100644 index 00000000..220358d2 --- /dev/null +++ b/vnpy/trader/app/algoTrading/algo/arbitrageAlgo.py @@ -0,0 +1,225 @@ +# encoding: UTF-8 + +from __future__ import division +from collections import OrderedDict + +from vnpy.trader.vtConstant import (DIRECTION_LONG, DIRECTION_SHORT, + OFFSET_OPEN, OFFSET_CLOSE, + STATUS_ALLTRADED, STATUS_CANCELLED, + STATUS_REJECTED) +from vnpy.trader.uiQt import QtGui + +from vnpy.trader.app.algoTrading.algoTemplate import AlgoTemplate +from vnpy.trader.app.algoTrading.uiAlgoWidget import AlgoWidget, QtWidgets + + +STATUS_FINISHED = set([STATUS_ALLTRADED, STATUS_CANCELLED, STATUS_REJECTED]) + + +######################################################################## +class ArbitrageAlgo(AlgoTemplate): + """Arbitrage算法,用于套利""" + + templateName = u'Arbitrage 套利' + + #---------------------------------------------------------------------- + def __init__(self, engine, setting, algoName): + """Constructor""" + super(ArbitrageAlgo, self).__init__(engine, setting, algoName) + + # 配置参数 + self.activeVtSymbol = str(setting['activeVtSymbol']) # 主动腿 + self.passiveVtSymbol = str(setting['passiveVtSymbol']) # 被动腿 + + self.spread = float(setting['spread']) # 价差 + self.volume = float(setting['volume']) # 数量 + self.interval = int(setting['interval']) # 间隔 + + self.activeOrderID = '' # 主动委托号 + self.passiveOrderID = '' # 被动委托号 + + self.netPos = 0 # 净持仓 + self.count = 0 # 运行计数 + + # 初始化 + self.subscribe(self.activeVtSymbol) + self.subscribe(self.passiveVtSymbol) + + self.paramEvent() + self.varEvent() + + #---------------------------------------------------------------------- + def onTick(self, tick): + """""" + pass + + #---------------------------------------------------------------------- + def onTrade(self, trade): + """""" + # 更新净持仓数量 + if trade.direction == DIRECTION_LONG: + self.netPos += trade.volume + else: + self.netPos -= trade.volume + + # 如果是主动腿成交则需要执行对冲 + if trade.vtSymbol == self.activeVtSymbol: + self.hedge() + + self.varEvent() + + #---------------------------------------------------------------------- + def onOrder(self, order): + """""" + if order.vtSymbol == self.activeVtSymbol: + if order.status in STATUS_FINISHED: + self.activeOrderID = '' + elif order.vtSymbol == self.passiveVtSymbol: + if order.status in STATUS_FINISHED: + self.passiveOrderID = '' + + self.varEvent() + + #---------------------------------------------------------------------- + def onTimer(self): + """""" + self.count += 1 + if self.count < self.interval: + return + + self.count = 0 + + # 撤单 + if self.activeOrderID or self.passiveOrderID: + self.cancelAll() + return + + # 如果有净仓位则执行对冲 + if self.netPos: + self.hedge() + return + + # 计算价差的bid/ask + activeTick = self.getTick(self.activeVtSymbol) + passiveTick = self.getTick(self.passiveVtSymbol) + + spreadBidPrice = activeTick.bidPrice1 - passiveTick.askPrice1 + spreadAskPrice = activeTick.askPrice1 - passiveTick.bidPrice1 + + spreadBidVolume = min(activeTick.bidVolume1, passiveTick.askVolume1) + spreadAskVolume = min(activeTick.askVolume1, passiveTick.bidVolume1) + + if spreadBidPrice > self.spread: + self.activeOrderID = self.sell(self.activeVtSymbol, activeTick.bidPrice1, spreadBidVolume) + elif spreadAskPrice < - self.spread: + self.activeOrderID = self.buy(self.activeVtSymbol, activeTick.askPrice1, spreadAskVolume) + + # 更新界面 + self.varEvent() + + #---------------------------------------------------------------------- + def onStop(self): + """""" + self.writeLog(u'算法停止') + + self.varEvent() + + #---------------------------------------------------------------------- + def varEvent(self): + """更新变量""" + d = OrderedDict() + d[u'算法状态'] = self.active + d[u'运行计数'] = self.count + d[u'净持仓'] = self.netPos + d[u'主动腿委托号'] = self.activeOrderID + d[u'被动腿委托号'] = self.passiveOrderID + self.putVarEvent(d) + + #---------------------------------------------------------------------- + def paramEvent(self): + """更新参数""" + d = OrderedDict() + d[u'主动腿代码'] = self.activeVtSymbol + d[u'被动腿代码'] = self.passiveVtSymbol + d[u'价差'] = self.spread + d[u'数量'] = self.volume + d[u'间隔'] = self.interval + self.putParamEvent(d) + + #---------------------------------------------------------------------- + def hedge(self): + """""" + tick = self.getTick(self.passiveVtSymbol) + volume = abs(self.netPos) + + if self.netPos > 0: + self.passiveOrderID = self.sell(self.passiveVtSymbol, + tick.bidPrice5, + volume) + elif self.netPos < 0: + self.passiveOrderID = self.buy(self.activeVtSymbol, + tick.askPrice5, + volume) + + + +######################################################################## +class ArbitrageWidget(AlgoWidget): + """""" + + #---------------------------------------------------------------------- + def __init__(self, algoEngine, parent=None): + """Constructor""" + super(ArbitrageWidget, self).__init__(algoEngine, parent) + + self.templateName = ArbitrageAlgo.templateName + + #---------------------------------------------------------------------- + def initAlgoLayout(self): + """""" + self.lineActiveVtSymbol = QtWidgets.QLineEdit() + self.linePassiveVtSymbol = QtWidgets.QLineEdit() + + validator = QtGui.QDoubleValidator() + validator.setBottom(0) + + self.lineSpread = QtWidgets.QLineEdit() + self.lineSpread.setValidator(validator) + + self.lineVolume = QtWidgets.QLineEdit() + self.lineVolume.setValidator(validator) + + intValidator = QtGui.QIntValidator() + intValidator.setBottom(10) + self.lineInterval = QtWidgets.QLineEdit() + self.lineInterval.setValidator(intValidator) + + Label = QtWidgets.QLabel + + grid = QtWidgets.QGridLayout() + grid.addWidget(Label(u'主动腿代码'), 0, 0) + grid.addWidget(self.lineActiveVtSymbol, 0, 1) + grid.addWidget(Label(u'被动腿代码'), 1, 0) + grid.addWidget(self.linePassiveVtSymbol, 1, 1) + grid.addWidget(Label(u'套利价差'), 2, 0) + grid.addWidget(self.lineSpread, 2, 1) + grid.addWidget(Label(u'委托数量'), 3, 0) + grid.addWidget(self.lineVolume, 3, 1) + grid.addWidget(Label(u'运行间隔'), 4, 0) + grid.addWidget(self.lineInterval, 4, 1) + + return grid + + #---------------------------------------------------------------------- + def getAlgoSetting(self): + """""" + setting = OrderedDict() + setting['templateName'] = self.templateName + setting['activeVtSymbol'] = str(self.lineActiveVtSymbol.text()) + setting['passiveVtSymbol'] = str(self.linePassiveVtSymbol.text()) + setting['spread'] = float(self.lineSpread.text()) + setting['volume'] = float(self.lineVolume.text()) + setting['interval'] = int(self.lineInterval.text()) + + return setting + diff --git a/vnpy/trader/app/algoTrading/algo/blAlgo.py b/vnpy/trader/app/algoTrading/algo/blAlgo.py index 70aba8e5..8a9bc154 100644 --- a/vnpy/trader/app/algoTrading/algo/blAlgo.py +++ b/vnpy/trader/app/algoTrading/algo/blAlgo.py @@ -188,7 +188,7 @@ class BlWidget(AlgoWidget): def getAlgoSetting(self): """""" setting = OrderedDict() - setting['templateName'] = StopAlgo.templateName + setting['templateName'] = self.templateName setting['vtSymbol'] = str(self.lineVtSymbol.text()) setting['direction'] = text_type(self.comboDirection.currentText()) setting['offset'] = text_type(self.comboOffset.currentText()) diff --git a/vnpy/trader/app/algoTrading/algo/icebergAlgo.py b/vnpy/trader/app/algoTrading/algo/icebergAlgo.py index 954d3459..027659a9 100644 --- a/vnpy/trader/app/algoTrading/algo/icebergAlgo.py +++ b/vnpy/trader/app/algoTrading/algo/icebergAlgo.py @@ -34,7 +34,7 @@ class IcebergAlgo(AlgoTemplate): self.price = float(setting['price']) # 价格 self.volume = float(setting['volume']) # 数量 self.display = float(setting['display']) # 挂出数量 - self.interval = text_type(setting['interval']) # 间隔 + self.interval = int(setting['interval']) # 间隔 self.offset = text_type(setting['offset']) # 开平 self.count = 0 # 执行计数 @@ -73,12 +73,18 @@ class IcebergAlgo(AlgoTemplate): def onTimer(self): """""" self.count += 1 + if self.count < self.interval: self.varEvent() return self.count = 0 + contract = self.getContract(self.vtSymbol) + if not contract: + self.writeLog(u'找不到合约%s' %self.vtSymbol) + return + if not self.vtOrderID: orderVolume = self.volume - self.tradedVolume orderVolume = min(orderVolume, self.display) @@ -193,7 +199,7 @@ class IcebergWidget(AlgoWidget): def getAlgoSetting(self): """""" setting = OrderedDict() - setting['templateName'] = StopAlgo.templateName + setting['templateName'] = self.templateName setting['vtSymbol'] = str(self.lineVtSymbol.text()) setting['direction'] = text_type(self.comboDirection.currentText()) setting['offset'] = text_type(self.comboOffset.currentText()) diff --git a/vnpy/trader/app/algoTrading/algo/sniperAlgo.py b/vnpy/trader/app/algoTrading/algo/sniperAlgo.py index 2bb396cc..2153f775 100644 --- a/vnpy/trader/app/algoTrading/algo/sniperAlgo.py +++ b/vnpy/trader/app/algoTrading/algo/sniperAlgo.py @@ -178,7 +178,7 @@ class SniperWidget(AlgoWidget): def getAlgoSetting(self): """""" setting = OrderedDict() - setting['templateName'] = StopAlgo.templateName + setting['templateName'] = self.templateName setting['vtSymbol'] = str(self.lineVtSymbol.text()) setting['direction'] = text_type(self.comboDirection.currentText()) setting['offset'] = text_type(self.comboOffset.currentText()) diff --git a/vnpy/trader/app/spreadTrading/stEngine.py b/vnpy/trader/app/spreadTrading/stEngine.py index 1473b06e..051168c3 100644 --- a/vnpy/trader/app/spreadTrading/stEngine.py +++ b/vnpy/trader/app/spreadTrading/stEngine.py @@ -3,6 +3,7 @@ import json import traceback import shelve +from collections import OrderedDict from vnpy.event import Event from vnpy.trader.vtFunction import getJsonPath, getTempPath @@ -289,8 +290,8 @@ class StAlgoEngine(object): self.mainEngine = mainEngine self.eventEngine = eventEngine - self.algoDict = {} # spreadName:algo - self.vtSymbolAlgoDict = {} # vtSymbol:algo + self.algoDict = OrderedDict() # spreadName:algo + self.vtSymbolAlgoDict = {} # vtSymbol:algo self.registerEvent() diff --git a/vnpy/trader/app/spreadTrading/uiStWidget.py b/vnpy/trader/app/spreadTrading/uiStWidget.py index 76b932fd..1b77c27d 100644 --- a/vnpy/trader/app/spreadTrading/uiStWidget.py +++ b/vnpy/trader/app/spreadTrading/uiStWidget.py @@ -415,6 +415,7 @@ class StActiveButton(QtWidgets.QPushButton): ######################################################################## class StAlgoManager(QtWidgets.QTableWidget): """价差算法管理组件""" + signalPos = QtCore.pyqtSignal(type(Event())) #---------------------------------------------------------------------- def __init__(self, stEngine, parent=None): @@ -422,16 +423,20 @@ class StAlgoManager(QtWidgets.QTableWidget): super(StAlgoManager, self).__init__(parent) self.algoEngine = stEngine.algoEngine + self.eventEngine = stEngine.eventEngine self.buttonActiveDict = {} # spreadName: buttonActive + self.posCellDict = {} # spreadName: cell self.initUi() + self.registerEvent() #---------------------------------------------------------------------- def initUi(self): """初始化表格""" headers = [u'价差', u'算法', + u'净持仓' 'BuyPrice', 'SellPrice', 'CoverPrice', @@ -462,6 +467,7 @@ class StAlgoManager(QtWidgets.QTableWidget): for row, d in enumerate(l): cellSpreadName = QtWidgets.QTableWidgetItem(d['spreadName']) cellAlgoName = QtWidgets.QTableWidgetItem(d['algoName']) + cellNetPos = QtWidgets.QTableWidgetItem('0') spinBuyPrice = StBuyPriceSpinBox(algoEngine, d['spreadName'], d['buyPrice']) spinSellPrice = StSellPriceSpinBox(algoEngine, d['spreadName'], d['sellPrice']) spinShortPrice = StShortPriceSpinBox(algoEngine, d['spreadName'], d['shortPrice']) @@ -473,14 +479,15 @@ class StAlgoManager(QtWidgets.QTableWidget): 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) + self.setItem(row, 2, cellNetPos) + self.setCellWidget(row, 3, spinBuyPrice) + self.setCellWidget(row, 4, spinSellPrice) + self.setCellWidget(row, 5, spinCoverPrice) + self.setCellWidget(row, 6, spinShortPrice) + self.setCellWidget(row, 7, spinMaxOrderSize) + self.setCellWidget(row, 8, spinMaxPosSize) + self.setCellWidget(row, 9, comboMode) + self.setCellWidget(row, 10, buttonActive) buttonActive.signalActive.connect(spinBuyPrice.algoActiveChanged) buttonActive.signalActive.connect(spinSellPrice.algoActiveChanged) @@ -491,12 +498,28 @@ class StAlgoManager(QtWidgets.QTableWidget): buttonActive.signalActive.connect(comboMode.algoActiveChanged) self.buttonActiveDict[d['spreadName']] = buttonActive + self.posCellDict[d['spreadName']] = cellNetPos #---------------------------------------------------------------------- def stopAll(self): """停止所有算法""" for button in self.buttonActiveDict.values(): button.stop() + + #---------------------------------------------------------------------- + def processStPosEvent(self, event): + """""" + pos = event.dict_['data'] + cell = self.posCellDict[pos.name] + cell.setText(str(pos.netPos)) + + #---------------------------------------------------------------------- + def registerEvent(self): + """""" + self.signalPos.connect(self.processStPosEvent) + + self.eventEngine.register(EVENT_SPREADTRADING_POS, self.signalPos.emit) + ######################################################################## diff --git a/vnpy/trader/gateway/bitmexGateway/bitmexGateway.py b/vnpy/trader/gateway/bitmexGateway/bitmexGateway.py index a0c87eb6..599ead71 100644 --- a/vnpy/trader/gateway/bitmexGateway/bitmexGateway.py +++ b/vnpy/trader/gateway/bitmexGateway/bitmexGateway.py @@ -87,9 +87,6 @@ class BitmexGateway(VtGateway): self.restApi.connect(apiKey, apiSecret, sessionCount) self.wsApi.connect(apiKey, apiSecret, symbols) - # 初始化并启动查询 - #self.initQuery() - #---------------------------------------------------------------------- def subscribe(self, subscribeReq): """订阅行情""" diff --git a/vnpy/trader/gateway/ccxtGateway/ccxtGateway.py b/vnpy/trader/gateway/ccxtGateway/ccxtGateway.py index 2df0e484..5461e304 100644 --- a/vnpy/trader/gateway/ccxtGateway/ccxtGateway.py +++ b/vnpy/trader/gateway/ccxtGateway/ccxtGateway.py @@ -488,7 +488,11 @@ class CcxtApi(object): tick.lowPrice = float(data['low']) tick.lastPrice = float(data['close']) tick.volume = float(data['quoteVolume']) - tick.datetime = datetime.fromtimestamp(data['timestamp']/1000) + + if data['timestamp']: + tick.datetime = datetime.fromtimestamp(data['timestamp']/1000) + else: + tick.datetime = datetime.now() tick.date = tick.datetime.strftime('%Y%m%d') tick.time = tick.datetime.strftime('%H:%M:%S') @@ -509,8 +513,11 @@ class CcxtApi(object): for n, ask in enumerate(data['asks'][:5]): tick.__setattr__('askPrice%s' %(n+1), float(ask[0])) tick.__setattr__('askVolume%s' %(n+1), float(ask[1])) - - tick.datetime = datetime.fromtimestamp(data['timestamp']/1000) + + if data['timestamp']: + tick.datetime = datetime.fromtimestamp(data['timestamp']/1000) + else: + tick.datetime = datetime.now() tick.date = tick.datetime.strftime('%Y%m%d') tick.time = tick.datetime.strftime('%H:%M:%S') diff --git a/vnpy/trader/gateway/fcoinGateway/fcoinGateway.py b/vnpy/trader/gateway/fcoinGateway/fcoinGateway.py index 33a002ad..e64f1b5d 100644 --- a/vnpy/trader/gateway/fcoinGateway/fcoinGateway.py +++ b/vnpy/trader/gateway/fcoinGateway/fcoinGateway.py @@ -526,7 +526,8 @@ class WebsocketApi(FcoinWebsocketApi): tick.lastPrice = ticker[0] tick.volume = ticker[9] - self.gateway.onTick(copy(tick)) + if tick.askPrice1: + self.gateway.onTick(copy(tick)) #---------------------------------------------------------------------- def onDepth(self, d): @@ -565,7 +566,8 @@ class WebsocketApi(FcoinWebsocketApi): tick.date = tick.datetime.strftime('%Y%m%d') tick.time = tick.datetime.strftime('%H:%M:%S') - self.gateway.onTick(copy(tick)) + if tick.lastPrice: + self.gateway.onTick(copy(tick)) #----------------------------------------------------------------------