Merge branch 'dev' of https://github.com/vnpy/vnpy into dev
This commit is contained in:
commit
5dd17111d7
@ -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):
|
||||
|
@ -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):
|
||||
|
225
vnpy/trader/app/algoTrading/algo/arbitrageAlgo.py
Normal file
225
vnpy/trader/app/algoTrading/algo/arbitrageAlgo.py
Normal file
@ -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
|
||||
|
@ -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())
|
||||
|
@ -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())
|
||||
|
@ -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())
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
||||
########################################################################
|
||||
|
@ -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):
|
||||
"""订阅行情"""
|
||||
|
@ -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')
|
||||
|
||||
|
@ -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))
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
Loading…
Reference in New Issue
Block a user