[Add]新增AlgoTrading算法交易模块

This commit is contained in:
vn.py 2018-06-05 21:23:56 +08:00
parent 08e836b070
commit 8b1bc4e2ea
5 changed files with 876 additions and 0 deletions

View File

@ -0,0 +1,12 @@
# encoding: UTF-8
import os
from .algoEngine import AlgoEngine
from .uiAlgoManager import AlgoManager
appName = 'AlgoTrading'
appDisplayName = u'算法交易'
appEngine = AlgoEngine
appWidget = AlgoManager
appIco = 'ct.ico'

View File

@ -0,0 +1,221 @@
# encoding: UTF-8
'''
'''
from __future__ import division
from vnpy.event import Event
from vnpy.trader.vtEvent import EVENT_TIMER, EVENT_TICK, EVENT_ORDER, EVENT_TRADE
from vnpy.trader.vtConstant import (DIRECTION_LONG, DIRECTION_SHORT, PRICETYPE_LIMITPRICE,
OFFSET_OPEN, OFFSET_CLOSE,
OFFSET_CLOSETODAY, OFFSET_CLOSEYESTERDAY)
from vnpy.trader.vtObject import VtSubscribeReq, VtOrderReq, VtCancelOrderReq, VtLogData
from .twapAlgo import TwapAlgo
EVENT_ALGO_LOG = 'eAlgoLog' # 算法日志事件
EVENT_ALGO_PARAM = 'eAlgoParam' # 算法参数事件
EVENT_ALGO_VAR = 'eAlgoVar' # 算法变量事件
ALGO_DICT = {
TwapAlgo.name: TwapAlgo
}
########################################################################
class AlgoEngine(object):
"""算法交易引擎"""
#----------------------------------------------------------------------
def __init__(self, mainEngine, eventEngine):
""""""
self.mainEngine = mainEngine
self.eventEngine = eventEngine
self.algoDict = {} # algoName:algo
self.orderAlgoDict = {} # vtOrderID:algo
self.symbolAlgoDict = {} # vtSymbol:algo set
self.registerEvent()
#----------------------------------------------------------------------
def registerEvent(self):
"""注册事件监听"""
self.eventEngine.register(EVENT_TICK, self.processTickEvent)
self.eventEngine.register(EVENT_TIMER, self.processTimerEvent)
self.eventEngine.register(EVENT_ORDER, self.processOrderEvent)
self.eventEngine.register(EVENT_TRADE, self.processTradeEvent)
#----------------------------------------------------------------------
def stop(self):
"""停止"""
pass
#----------------------------------------------------------------------
def processTickEvent(self, event):
"""行情事件"""
tick = event.dict_['data']
l = self.symbolAlgoDict.get(tick.vtSymbol, None)
if l:
for algo in l:
algo.updateTick(tick)
#----------------------------------------------------------------------
def processOrderEvent(self, event):
"""委托事件"""
order = event.dict_['data']
algo = self.orderAlgoDict.get(order.vtOrderID, None)
if algo:
algo.updateOrder(order)
#----------------------------------------------------------------------
def processTradeEvent(self, event):
"""成交事件"""
trade = event.dict_['data']
algo = self.orderAlgoDict.get(trade.vtOrderID, None)
if algo:
algo.updateTrade(trade)
#----------------------------------------------------------------------
def processTimerEvent(self, event):
"""定时事件"""
for algo in self.algoDict.values():
algo.updateTimer()
#----------------------------------------------------------------------
def addAlgo(self, name, algoSetting):
"""新增算法"""
algoClass = ALGO_DICT[name]
algo = algoClass.new(self, algoSetting)
self.algoDict[algo.algoName] = algo
return algo.algoName
#----------------------------------------------------------------------
def stopAlgo(self, algoName):
"""停止算法"""
self.algoDict[algoName].stop()
del self.algoDict[algoName]
#----------------------------------------------------------------------
def stopAll(self):
"""全部停止"""
l = self.algoDict.keys()
for algoName in l:
self.stopAlgo(algoName)
#----------------------------------------------------------------------
def subscribe(self, algo, vtSymbol):
""""""
contract = self.mainEngine.getContract(vtSymbol)
if not contract:
self.writeLog(u'%s订阅行情失败,找不到合约%s' %(algo.algoName, vtSymbol))
return
# 如果vtSymbol已存在于字典说明已经订阅过
if vtSymbol in self.symbolAlgoDict:
s = self.symbolAlgoDict[vtSymbol]
s.add(algo)
return
# 否则需要添加到字典中并执行订阅
else:
s = set()
self.symbolAlgoDict[vtSymbol] = s
s.add(algo)
req = VtSubscribeReq()
req.symbol = contract.symbol
req.exchange = contract.exchange
self.mainEngine.subscribe(req, contract.gatewayName)
#----------------------------------------------------------------------
def sendOrder(self, algo, vtSymbol, direction, price, volume):
"""发单"""
contract = self.mainEngine.getContract(vtSymbol)
if not contract:
self.writeLog(u'%s委托下单失败,找不到合约:%s' %(algo.algoName, vtSymbol))
req = VtOrderReq()
req.vtSymbol = vtSymbol
req.symbol = contract.symbol
req.exchange = contract.exchange
req.direction = direction
req.priceType = PRICETYPE_LIMITPRICE
req.offset = OFFSET_CLOSETODAY
req.price = price
req.volume = volume
vtOrderID = self.mainEngine.sendOrder(req, contract.gatewayName)
return vtOrderID
#----------------------------------------------------------------------
def buy(self, algo, vtSymbol, price, volume):
"""买入"""
return self.sendOrder(algo, vtSymbol, DIRECTION_LONG, price, volume)
#----------------------------------------------------------------------
def sell(self, algo, vtSymbol, price, volume):
"""卖出"""
return self.sendOrder(algo, vtSymbol, DIRECTION_SHORT, price, volume)
#----------------------------------------------------------------------
def cancelOrder(self, algo, vtOrderID):
"""撤单"""
order = self.mainEngine.getOrder(vtOrderID)
if not order:
self.writeLog(u'%s委托撤单失败,找不到委托:%s' %(algo.algoName, vtOrderID))
return
req = VtCancelOrderReq()
req.symbol = order.symbol
req.exchange = order.exchange
req.orderID = order.orderID
req.frontID = order.frontID
req.sessionID = order.sessionID
self.mainEngine.cancelOrder(req, order.gatewayName)
#----------------------------------------------------------------------
def writeLog(self, content, algo=None):
"""输出日志"""
log = VtLogData()
log.logContent = content
if algo:
log.gatewayName = algo.algoName
event = Event(EVENT_ALGO_LOG)
event.dict_['data'] = log
self.eventEngine.put(event)
#----------------------------------------------------------------------
def putVarEvent(self, algo, d):
"""更新变量"""
d['algoName'] = algo.algoName
event = Event(EVENT_ALGO_VAR)
event.dict_['data'] = d
self.eventEngine.put(event)
#----------------------------------------------------------------------
def putParamEvent(self, algo, d):
"""更新参数"""
d['algoName'] = algo.algoName
event = Event(EVENT_ALGO_PARAM)
event.dict_['data'] = d
self.eventEngine.put(event)
#----------------------------------------------------------------------
def getTick(self, algo, vtSymbol):
"""查询行情"""
tick = self.mainEngine.getTick(vtSymbol)
if not tick:
self.writeLog(u'%s查询行情失败,找不到报价:%s' %(algo.algoName, vtSymbol))
return
return tick

View File

@ -0,0 +1,168 @@
# encoding: UTF-8
from __future__ import division
from vnpy.trader.vtConstant import STATUS_NOTTRADED, STATUS_PARTTRADED, STATUS_UNKNOWN
# 活动委托状态
STATUS_ACTIVE = [STATUS_NOTTRADED, STATUS_PARTTRADED, STATUS_UNKNOWN]
########################################################################
class AlgoTemplate(object):
"""算法模板"""
name = 'AlgoTemplate'
count = 0
@classmethod
#----------------------------------------------------------------------
def new(cls, engine, setting):
"""创建新对象"""
cls.count += 1
algoName = '%s_%s' %(cls.name, cls.count)
algo = cls(engine, setting, algoName)
return algo
#----------------------------------------------------------------------
def __init__(self, engine, setting, algoName):
"""Constructor"""
self.engine = engine
self.active = True
self.algoName = algoName
self.activeOrderDict = {} # vtOrderID:order
#----------------------------------------------------------------------
def updateTick(self, tick):
""""""
if not self.active:
return
self.onTick(tick)
#----------------------------------------------------------------------
def updateTrade(self, trade):
""""""
if not self.active:
return
self.onTrade(trade)
#----------------------------------------------------------------------
def updateOrder(self, order):
""""""
if not self.active:
return
# 活动委托需要缓存
if order.status in STATUS_ACTIVE:
self.activeOrderDict[order.vtOrderID] = order
# 结束委托需要移除
elif order.vtOrderID in self.activeOrderDict:
del self.activeOrderDict[order.vtOrderID]
self.onOrder(order)
#----------------------------------------------------------------------
def updateTimer(self):
""""""
if not self.active:
return
self.onTimer()
#----------------------------------------------------------------------
def stop(self):
""""""
self.active = False
self.cancelAll()
self.onStop()
#----------------------------------------------------------------------
def onTick(self, tick):
""""""
pass
#----------------------------------------------------------------------
def onTrade(self, trade):
""""""
pass
#----------------------------------------------------------------------
def onOrder(self, order):
""""""
pass
#----------------------------------------------------------------------
def onTimer(self):
""""""
pass
#----------------------------------------------------------------------
def onStop(self):
""""""
pass
#----------------------------------------------------------------------
def subscribe(self, vtSymbol):
""""""
self.engine.subscribe(self, vtSymbol)
#----------------------------------------------------------------------
def buy(self, vtSymbol, price, volume):
""""""
return self.engine.buy(self, vtSymbol, price, volume)
#----------------------------------------------------------------------
def sell(self, vtSymbol, price, volume):
""""""
return self.engine.sell(self, vtSymbol, price, volume)
#----------------------------------------------------------------------
def cancelOrder(self, vtOrderID):
""""""
self.engine.cancelOrder(self, vtOrderID)
#----------------------------------------------------------------------
def cancelAll(self):
""""""
if not self.activeOrderDict:
return False
for order in self.activeOrderDict.values():
self.cancelOrder(order.vtOrderID)
return True
#----------------------------------------------------------------------
def getTick(self, vtSymbol):
""""""
return self.engine.getTick(self, vtSymbol)
#----------------------------------------------------------------------
def roundValue(self, value, change):
"""标准化价格或者数量"""
if not change:
return value
n = value / change
v = round(n, 0) * change
return v
#----------------------------------------------------------------------
def putVarEvent(self, d):
"""更新变量"""
self.engine.putVarEvent(self, d)
#----------------------------------------------------------------------
def putParamEvent(self, d):
"""更新参数"""
self.engine.putParamEvent(self, d)
#----------------------------------------------------------------------
def writeLog(self, content):
"""输出日志"""
self.engine.writeLog(content, self)

View File

@ -0,0 +1,232 @@
# encoding: UTF-8
from __future__ import division
from collections import OrderedDict
from vnpy.trader.vtConstant import (DIRECTION_LONG, DIRECTION_SHORT)
from vnpy.trader.uiQt import QtWidgets
from algoTemplate import AlgoTemplate
########################################################################
class TwapAlgo(AlgoTemplate):
"""TWAP算法"""
name = 'TWAP'
#----------------------------------------------------------------------
def __init__(self, engine, setting, algoName):
"""Constructor"""
super(TwapAlgo, self).__init__(engine, setting, algoName)
# 参数
self.vtSymbol = setting['vtSymbol'] # 合约代码
self.direction = setting['direction'] # 买卖
self.price = setting['price'] # 目标价格
self.totalVolume = setting['volume'] # 总数量
self.time = setting['time'] # 执行时间
self.interval = setting['interval'] # 执行间隔
self.minVolume = setting['minVolume'] # 最小委托数量
# 变量
self.orderSize = self.totalVolume / (self.time / self.interval)
self.orderSize = self.roundValue(self.orderSize, self.minVolume)
if self.minVolume >= 1:
self.orderSize = int(self.orderSize)
self.timerCount = 0
self.timerTotal = 0
self.tradedVolume = 0
self.subscribe(self.vtSymbol)
self.paramEvent()
self.varEvent()
#----------------------------------------------------------------------
def onTick(self, tick):
""""""
pass
#----------------------------------------------------------------------
def onTrade(self, trade):
""""""
self.tradedVolume += trade.volume
if self.tradedVolume >= self.totalVolume:
self.stop()
else:
self.varEvent()
#----------------------------------------------------------------------
def onOrder(self, order):
""""""
pass
#----------------------------------------------------------------------
def onTimer(self):
""""""
self.timerCount += 1
self.timerTotal += 1
# 总时间结束,停止算法
if self.timerTotal >= self.time:
self.stop()
return
# 每到间隔发一次委托
if self.timerCount >= self.interval:
self.timerCount = 0
tick = self.getTick(self.vtSymbol)
if not tick:
return
size = min(self.orderSize, self.totalVolume-self.tradedVolume)
# 买入
if self.direction == DIRECTION_LONG:
# 市场买1价小于目标买价
if tick.bidPrice1 < self.price:
self.buy(self.vtSymbol, self.price, size)
self.writeLog(u'委托买入%s,数量%,价格%s' %(self.vtSymbol, self.orderSize, self.price))
# 卖出
if self.direction == DIRECTION_SHORT:
# 市场卖1价大于目标价
if tick.askPrice1 > self.price:
self.sell(self.vtSymbol, self.price, size)
self.writeLog(u'委托卖出%s,数量%s,价格%s' %(self.vtSymbol, self.orderSize, self.price))
# 委托后等待到间隔一半的时间撤单
elif self.timerCount == round(self.interval/2, 0):
result = self.cancelAll()
if result:
self.writeLog(u'撤销之前的委托')
self.varEvent()
#----------------------------------------------------------------------
def onStop(self):
""""""
self.writeLog(u'运行时间已到,停止算法')
self.varEvent()
#----------------------------------------------------------------------
def varEvent(self):
"""更新变量"""
d = OrderedDict()
d[u'算法状态'] = self.active
d[u'成交数量'] = self.tradedVolume
d[u'单笔委托'] = self.orderSize
d[u'本轮读秒'] = self.timerCount
d[u'累计读秒'] = self.timerTotal
d['active'] = self.active
self.putVarEvent(d)
#----------------------------------------------------------------------
def paramEvent(self):
"""更新参数"""
d = OrderedDict()
d[u'代码'] = self.vtSymbol
d[u'方向'] = self.direction
d[u'目标价格'] = self.price
d[u'总数量'] = self.totalVolume
d[u'总时间(秒)'] = self.time
d[u'间隔(秒)'] = self.interval
self.putParamEvent(d)
########################################################################
class TwapWidget(QtWidgets.QWidget):
""""""
name = TwapAlgo.name
#----------------------------------------------------------------------
def __init__(self, algoEngine):
"""Constructor"""
super(TwapWidget, self).__init__()
self.algoEngine = algoEngine
self.initUi()
#----------------------------------------------------------------------
def initUi(self):
""""""
self.lineSymbol = QtWidgets.QLineEdit()
self.comboDirection = QtWidgets.QComboBox()
self.comboDirection.addItem(DIRECTION_LONG)
self.comboDirection.addItem(DIRECTION_SHORT)
self.comboDirection.setCurrentIndex(0)
self.spinPrice = QtWidgets.QDoubleSpinBox()
self.spinPrice.setMinimum(0)
self.spinPrice.setMaximum(1000000000)
self.spinPrice.setDecimals(8)
self.spinVolume = QtWidgets.QDoubleSpinBox()
self.spinVolume.setMinimum(0)
self.spinVolume.setMaximum(1000000000)
self.spinVolume.setDecimals(6)
self.spinTime = QtWidgets.QSpinBox()
self.spinTime.setMinimum(30)
self.spinTime.setMaximum(86400)
self.spinInterval = QtWidgets.QSpinBox()
self.spinInterval.setMinimum(10)
self.spinInterval.setMaximum(3600)
self.spinMinVolume = QtWidgets.QDoubleSpinBox()
self.spinMinVolume.setMinimum(0)
self.spinMinVolume.setMaximum(10000)
self.spinMinVolume.setDecimals(6)
self.spinMinVolume.setValue(1)
buttonStart = QtWidgets.QPushButton(u'启动')
buttonStart.clicked.connect(self.addAlgo)
buttonStart.setMinimumHeight(100)
Label = QtWidgets.QLabel
grid = QtWidgets.QGridLayout()
grid.addWidget(Label(u'交易代码'), 0, 0)
grid.addWidget(self.lineSymbol, 0, 1)
grid.addWidget(Label(u'方向'), 1, 0)
grid.addWidget(self.comboDirection, 1, 1)
grid.addWidget(Label(u'目标价格'), 2, 0)
grid.addWidget(self.spinPrice, 2, 1)
grid.addWidget(Label(u'总数量'), 3, 0)
grid.addWidget(self.spinVolume, 3, 1)
grid.addWidget(Label(u'总时间(秒)'), 4, 0)
grid.addWidget(self.spinTime, 4, 1)
grid.addWidget(Label(u'间隔(秒)'), 5, 0)
grid.addWidget(self.spinInterval, 5, 1)
grid.addWidget(Label(u'数量取整'), 6, 0)
grid.addWidget(self.spinMinVolume, 6, 1)
grid.addWidget(buttonStart, 7, 0, 1, 2)
self.setLayout(grid)
#----------------------------------------------------------------------
def addAlgo(self):
""""""
setting = {
'vtSymbol': str(self.lineSymbol.text()),
'direction': str(self.comboDirection.currentText()),
'price': float(self.spinPrice.value()),
'volume': float(self.spinVolume.value()),
'time': int(self.spinTime.value()),
'interval': int(self.spinInterval.value()),
'minVolume': float(self.spinMinVolume.value())
}
self.algoEngine.addAlgo(TwapAlgo.name, setting)

View File

@ -0,0 +1,243 @@
# encoding: UTF-8
from vnpy.event import Event
from vnpy.trader.uiQt import QtCore, QtWidgets
from .algoEngine import EVENT_ALGO_LOG, EVENT_ALGO_PARAM, EVENT_ALGO_VAR
from .twapAlgo import TwapWidget
########################################################################
class StopButton(QtWidgets.QPushButton):
""""""
#----------------------------------------------------------------------
def __init__(self, algoEngine, algoName=''):
"""Constructor"""
super(StopButton, self).__init__()
self.algoEngine = algoEngine
self.algoName = algoName
self.setStyleSheet("color:black;background-color:yellow")
if algoName:
self.setText(u'停止')
self.clicked.connect(self.stopAlgo)
else:
self.setText(u'全部停止')
self.clicked.connect(self.stopAll)
#----------------------------------------------------------------------
def stopAlgo(self):
""""""
self.algoEngine.stopAlgo(self.algoName)
self.disable()
#----------------------------------------------------------------------
def stopAll(self):
""""""
self.algoEngine.stopAll()
#----------------------------------------------------------------------
def disable(self):
""""""
self.setEnabled(False)
self.setStyleSheet("color:black;background-color:grey")
AlgoCell = QtWidgets.QTableWidgetItem
########################################################################
class AlgoStatusMonitor(QtWidgets.QTableWidget):
""""""
signalParam = QtCore.Signal(type(Event()))
signalVar = QtCore.Signal(type(Event()))
#----------------------------------------------------------------------
def __init__(self, algoEngine):
"""Constructor"""
super(AlgoStatusMonitor, self).__init__()
self.algoEngine = algoEngine
self.eventEngine = algoEngine.eventEngine
self.cellDict = {}
self.initUi()
self.registerEvent()
#----------------------------------------------------------------------
def initUi(self):
"""初始化界面"""
labels = [u'',
u'名称',
u'参数',
u'变量']
self.setColumnCount(len(labels))
self.setHorizontalHeaderLabels(labels)
self.setRowCount(0)
self.verticalHeader().setVisible(False)
self.setEditTriggers(self.NoEditTriggers)
#----------------------------------------------------------------------
def registerEvent(self):
"""注册事件监听"""
self.signalParam.connect(self.processParamEvent)
self.signalVar.connect(self.processVarEvent)
self.eventEngine.register(EVENT_ALGO_PARAM, self.signalParam.emit)
self.eventEngine.register(EVENT_ALGO_VAR, self.signalVar.emit)
#----------------------------------------------------------------------
def addAlgo(self, algoName):
"""新增算法"""
self.insertRow(0)
buttonStop = StopButton(self.algoEngine, algoName)
cellName = AlgoCell(algoName)
cellParam = AlgoCell()
cellVar = AlgoCell()
self.setCellWidget(0, 0, buttonStop)
self.setItem(0, 1, cellName)
self.setItem(0, 2, cellParam)
self.setItem(0, 3, cellVar)
self.cellDict[algoName] = {
'param': cellParam,
'var': cellVar,
'button': buttonStop
}
#----------------------------------------------------------------------
def processParamEvent(self, event):
""""""
d = event.dict_['data']
algoName = d.pop('algoName')
if algoName not in self.cellDict:
self.addAlgo(algoName)
l = []
for k, v in d.items():
msg = u'%s:%s' %(k, v)
l.append(msg)
text = ','.join(l)
cell = self.cellDict[algoName]['param']
cell.setText(text)
self.resizeColumnsToContents()
#----------------------------------------------------------------------
def processVarEvent(self, event):
""""""
d = event.dict_['data']
algoName = d.pop('algoName')
if algoName not in self.cellDict:
self.addAlgo(algoName)
if 'active' in d:
active = d.pop('active')
if not active:
button = self.cellDict[algoName]['button']
button.disable()
l = []
for k, v in d.items():
msg = u'%s:%s' %(k, v)
l.append(msg)
text = ','.join(l)
cell = self.cellDict[algoName]['var']
cell.setText(text)
self.resizeColumnsToContents()
########################################################################
class AlgoLogMonitor(QtWidgets.QTextEdit):
""""""
signal = QtCore.Signal(type(Event()))
#----------------------------------------------------------------------
def __init__(self, algoEngine):
"""Constructor"""
super(AlgoLogMonitor, self).__init__()
self.eventEngine = algoEngine.eventEngine
self.registerEvent()
#----------------------------------------------------------------------
def registerEvent(self):
""""""
self.signal.connect(self.processEvent)
self.eventEngine.register(EVENT_ALGO_LOG, self.signal.emit)
#----------------------------------------------------------------------
def processEvent(self, event):
""""""
log = event.dict_['data']
if not log.gatewayName:
log.gatewayName = u'算法引擎'
msg = u'%s\t%s%s' %(log.logTime, log.gatewayName, log.logContent)
self.append(msg)
########################################################################
class AlgoManager(QtWidgets.QWidget):
""""""
#----------------------------------------------------------------------
def __init__(self, algoEngine, eventEngine):
"""Constructor"""
super(AlgoManager, self).__init__()
self.algoEngine = algoEngine
self.eventEngine = eventEngine
self.initUi()
self.addAlgoWidget(TwapWidget)
#----------------------------------------------------------------------
def initUi(self):
""""""
self.setWindowTitle(u'算法交易')
self.statusMonitor = AlgoStatusMonitor(self.algoEngine)
self.logMonitor = AlgoLogMonitor(self.algoEngine)
self.tab = QtWidgets.QTabWidget()
self.buttonStop = StopButton(self.algoEngine)
self.tab.setMaximumWidth(400)
self.buttonStop.setMaximumWidth(400)
self.buttonStop.setFixedHeight(100)
vbox1 = QtWidgets.QVBoxLayout()
vbox1.addWidget(self.tab)
vbox1.addStretch()
vbox1.addWidget(self.buttonStop)
vbox2 = QtWidgets.QVBoxLayout()
vbox2.addWidget(self.statusMonitor)
vbox2.addWidget(self.logMonitor)
hbox = QtWidgets.QHBoxLayout()
hbox.addLayout(vbox1)
hbox.addLayout(vbox2)
self.setLayout(hbox)
#----------------------------------------------------------------------
def addAlgoWidget(self, widgetClass):
""""""
w = widgetClass(self.algoEngine)
self.tab.addTab(w, w.name)