[Add]初步完成OptionMaster手动交易模块

This commit is contained in:
vn.py 2017-11-30 22:32:09 +08:00
parent b2aa6c5c2b
commit e096455b44
6 changed files with 472 additions and 4 deletions

View File

@ -1 +1 @@
B7/DvEptJ5IBD1LIj9SEFg==
HqLpRXdyn/k8CK6bQvjTlg==

View File

@ -43,6 +43,10 @@ DX_TARGET = 0.00001
#----------------------------------------------------------------------
def calculatePrice(f, k, r, t, v, cp):
"""计算期权价格"""
# 如果波动率为0则直接返回期权空间价值
if v <= 0:
return max(0, cp * (f - k))
d1 = (log(f / k) + (0.5 * pow(v, 2) + r) * t) / (v * sqrt(t))
d2 = d1 - v * sqrt(t)
price = cp * (f * cdf(cp * d1) - k * cdf(cp * d2) * exp(-r * t))
@ -121,9 +125,13 @@ def calculateImpv(price, f, k, r, t, cp):
for i in range(50):
# 计算当前猜测波动率对应的期权价格和vega值
p = calculatePrice(f, k, r, t, v, cp)
#print 'calculating vega', f, k, r, t, v, cp
vega = calculateOriginalVega(f, k, r, t, v, cp)
# 如果vega过小接近0则直接返回
if not vega:
return v
# 计算误差
dx = (price - p) / vega

View File

@ -376,6 +376,7 @@ class OmPortfolio(object):
self.underlyingDict = OrderedDict()
self.chainDict = OrderedDict()
self.optionDict = {}
self.instrumentDict = {}
for underlying in underlyingList:
self.underlyingDict[underlying.symbol] = underlying
@ -385,6 +386,9 @@ class OmPortfolio(object):
self.optionDict.update(chain.callDict)
self.optionDict.update(chain.putDict)
self.instrumentDict.update(self.underlyingDict)
self.instrumentDict.update(self.optionDict)
# 持仓数据
self.longPos = EMPTY_INT
self.shortPos = EMPTY_INT

View File

@ -0,0 +1,41 @@
# encoding: UTF-8
from vnpy.trader.uiQt import QtGui, QtWidgets, QtCore
COLOR_BID = QtGui.QColor(255,174,201)
COLOR_ASK = QtGui.QColor(160,255,160)
COLOR_STRIKE = QtGui.QColor(0,0,160)
COLOR_POS = QtGui.QColor(225,255,255)
COLOR_SYMBOL = QtGui.QColor('white')
COLOR_BLACK = QtGui.QColor('black')
CALL_SUFFIX = '_call'
PUT_SUFFIX = '_put'
STYLESHEET_START = "background-color: rgb(111,255,244); color: black"
STYLESHEET_STOP = "background-color: rgb(255,201,111); color: black"
########################################################################
class OmCell(QtWidgets.QTableWidgetItem):
"""单元格"""
#----------------------------------------------------------------------
def __init__(self, text=None, background=None, foreground=None, data=None):
"""Constructor"""
super(OmCell, self).__init__()
self.data = data
self.background = None
if text:
self.setText(text)
if foreground:
self.setForeground(foreground)
if background:
self.setBackground(background)
self.background = background
self.setTextAlignment(QtCore.Qt.AlignCenter)

View File

@ -0,0 +1,411 @@
# encoding: UTF-8
from vnpy.event import Event
from vnpy.trader.vtConstant import DIRECTION_LONG, DIRECTION_SHORT, OFFSET_OPEN, OFFSET_CLOSE, PRICETYPE_LIMITPRICE
from vnpy.trader.vtObject import VtOrderReq
from vnpy.trader.vtEvent import EVENT_TICK, EVENT_TRADE
from vnpy.trader.uiBasicWidget import WorkingOrderMonitor, PositionMonitor
from uiOmBase import *
########################################################################
class ChainMonitor(QtWidgets.QTableWidget):
"""期权链监控"""
headers = [
u'代码',
u'买价',
u'买量',
u'买隐波',
u'卖价',
u'卖量',
u'卖隐波',
u'净仓',
u'行权价',
u'净仓',
u'买价',
u'买量',
u'买隐波',
u'卖价',
u'卖量',
u'卖隐波',
u'代码'
]
signalTick = QtCore.pyqtSignal(type(Event()))
signalPos = QtCore.pyqtSignal(type(Event()))
signalTrade = QtCore.pyqtSignal(type(Event()))
#----------------------------------------------------------------------
def __init__(self, omEngine, eventEngine, parent=None):
"""Constructor"""
super(ChainMonitor, self).__init__(parent)
self.omEngine = omEngine
self.eventEngine = eventEngine
# 保存代码和持仓的字典
self.bidPriceDict = {}
self.bidVolumeDict = {}
self.bidImpvDict = {}
self.askPriceDict = {}
self.askVolumeDict = {}
self.askImpvDict = {}
self.posDict = {}
# 保存期权对象的字典
portfolio = omEngine.portfolio
self.instrumentDict = {}
self.instrumentDict.update(portfolio.optionDict)
self.instrumentDict.update(portfolio.underlyingDict)
# 初始化
self.initUi()
self.registerEvent()
#----------------------------------------------------------------------
def initUi(self):
"""初始化界面"""
portfolio = self.omEngine.portfolio
# 初始化表格
self.setColumnCount(len(self.headers))
self.setHorizontalHeaderLabels(self.headers)
rowCount = 0
rowCount += len(portfolio.underlyingDict)
rowCount += len(portfolio.chainDict)
for chain in portfolio.chainDict.values():
rowCount += len(chain.callDict)
self.setRowCount(rowCount)
self.verticalHeader().setVisible(False)
self.setEditTriggers(self.NoEditTriggers)
for i in range(self.columnCount()):
self.horizontalHeader().setResizeMode(i, QtWidgets.QHeaderView.Stretch)
self.horizontalHeader().setResizeMode(0, QtWidgets.QHeaderView.ResizeToContents)
self.horizontalHeader().setResizeMode(self.columnCount()-1, QtWidgets.QHeaderView.ResizeToContents)
# 初始化标的单元格
row = 0
for underlying in portfolio.underlyingDict.values():
symbol = underlying.symbol
cellSymbol = OmCell(symbol, COLOR_SYMBOL, COLOR_BLACK, underlying)
cellBidPrice = OmCell(str(underlying.bidPrice1), COLOR_BID, COLOR_BLACK, underlying)
cellBidVolume = OmCell(str(underlying.bidVolume1), COLOR_BID, COLOR_BLACK, underlying)
cellAskPrice = OmCell(str(underlying.askPrice1), COLOR_ASK, COLOR_BLACK, underlying)
cellAskVolume = OmCell(str(underlying.askVolume1), COLOR_ASK, COLOR_BLACK, underlying)
cellPos = OmCell(str(underlying.netPos), COLOR_POS, COLOR_BLACK, underlying)
self.setItem(row, 0, cellSymbol)
self.setItem(row, 1, cellBidPrice)
self.setItem(row, 2, cellBidVolume)
self.setItem(row, 4, cellAskPrice)
self.setItem(row, 5, cellAskVolume)
self.setItem(row, 7, cellPos)
self.bidPriceDict[symbol] = cellBidPrice
self.bidVolumeDict[symbol] = cellBidVolume
self.askPriceDict[symbol] = cellAskPrice
self.askVolumeDict[symbol] = cellAskVolume
self.posDict[symbol] = cellPos
row += 1
row += 1
# 初始化期权单元格
for chain in portfolio.chainDict.values():
# call
callRow = row
for option in chain.callDict.values():
cellSymbol = OmCell(option.symbol, COLOR_SYMBOL, COLOR_BLACK, option)
cellBidPrice = OmCell(str(option.bidPrice1), COLOR_BID, COLOR_BLACK, option)
cellBidVolume = OmCell(str(option.bidVolume1), COLOR_BID, COLOR_BLACK, option)
cellBidImpv = OmCell('%.1f' %(option.bidImpv*100), COLOR_BID, COLOR_BLACK, option)
cellAskPrice = OmCell(str(option.askPrice1), COLOR_ASK, COLOR_BLACK, option)
cellAskVolume = OmCell(str(option.askVolume1), COLOR_ASK, COLOR_BLACK, option)
cellAskImpv = OmCell('%.1f' %(option.askImpv*100), COLOR_ASK, COLOR_BLACK, option)
cellPos = OmCell(str(option.netPos), COLOR_POS, COLOR_BLACK, option)
cellStrike = OmCell(str(option.k), COLOR_STRIKE)
self.setItem(callRow, 0, cellSymbol)
self.setItem(callRow, 1, cellBidPrice)
self.setItem(callRow, 2, cellBidVolume)
self.setItem(callRow, 3, cellBidImpv)
self.setItem(callRow, 4, cellAskPrice)
self.setItem(callRow, 5, cellAskVolume)
self.setItem(callRow, 6, cellAskImpv)
self.setItem(callRow, 7, cellPos)
self.setItem(callRow, 8, cellStrike)
self.bidPriceDict[option.symbol] = cellBidPrice
self.bidVolumeDict[option.symbol] = cellBidVolume
self.bidImpvDict[option.symbol] = cellBidImpv
self.askPriceDict[option.symbol] = cellAskPrice
self.askVolumeDict[option.symbol] = cellAskVolume
self.askImpvDict[option.symbol] = cellAskImpv
self.posDict[option.symbol] = cellPos
callRow += 1
# put
putRow = row
for option in chain.putDict.values():
cellSymbol = OmCell(option.symbol, COLOR_SYMBOL, COLOR_BLACK, option)
cellBidPrice = OmCell(str(option.bidPrice1), COLOR_BID, COLOR_BLACK, option)
cellBidVolume = OmCell(str(option.bidVolume1), COLOR_BID, COLOR_BLACK, option)
cellBidImpv = OmCell('%.1f' %(option.bidImpv*100), COLOR_BID, COLOR_BLACK, option)
cellAskPrice = OmCell(str(option.askPrice1), COLOR_ASK, COLOR_BLACK, option)
cellAskVolume = OmCell(str(option.askVolume1), COLOR_ASK, COLOR_BLACK, option)
cellAskImpv = OmCell('%.1f' %(option.askImpv*100), COLOR_ASK, COLOR_BLACK, option)
cellPos = OmCell(str(option.netPos), COLOR_POS, COLOR_BLACK, option)
self.setItem(putRow, 9, cellPos)
self.setItem(putRow, 10, cellBidPrice)
self.setItem(putRow, 11, cellBidVolume)
self.setItem(putRow, 12, cellBidImpv)
self.setItem(putRow, 13, cellAskPrice)
self.setItem(putRow, 14, cellAskVolume)
self.setItem(putRow, 15, cellAskImpv)
self.setItem(putRow, 16, cellSymbol)
self.bidPriceDict[option.symbol] = cellBidPrice
self.bidVolumeDict[option.symbol] = cellBidVolume
self.bidImpvDict[option.symbol] = cellBidImpv
self.askPriceDict[option.symbol] = cellAskPrice
self.askVolumeDict[option.symbol] = cellAskVolume
self.askImpvDict[option.symbol] = cellAskImpv
self.posDict[option.symbol] = cellPos
putRow += 1
row = putRow + 1
#----------------------------------------------------------------------
def registerEvent(self):
"""注册事件监听"""
self.signalTick.connect(self.processTickEvent)
self.signalTrade.connect(self.processTradeEvent)
portfolio = self.omEngine.portfolio
for underlying in portfolio.underlyingDict.values():
self.eventEngine.register(EVENT_TICK + underlying.vtSymbol, self.signalTick.emit)
self.eventEngine.register(EVENT_TRADE + underlying.vtSymbol, self.signalTick.emit)
for chain in portfolio.chainDict.values():
for option in chain.optionDict.values():
self.eventEngine.register(EVENT_TICK + option.vtSymbol, self.signalTick.emit)
self.eventEngine.register(EVENT_TRADE + option.vtSymbol, self.signalTrade.emit)
#----------------------------------------------------------------------
def processTickEvent(self, event):
"""行情更新"""
tick = event.dict_['data']
symbol = tick.symbol
if symbol in self.bidImpvDict:
option = self.instrumentDict[symbol]
self.bidImpvDict[symbol].setText('%.1f' %(option.bidImpv*100))
self.askImpvDict[symbol].setText('%.1f' %(option.askImpv*100))
self.bidPriceDict[symbol].setText(str(tick.bidPrice1))
self.bidVolumeDict[symbol].setText(str(tick.bidVolume1))
self.askPriceDict[symbol].setText(str(tick.askPrice1))
self.askVolumeDict[symbol].setText(str(tick.askVolume1))
#----------------------------------------------------------------------
def processTradeEvent(self, event):
"""成交更新"""
trade = event.dict_['data']
symbol = trade.symbol
instrument = self.instrumentDict[symbol]
self.posDict[symbol].setText(str(instrument.netPos))
########################################################################
class TradingWidget(QtWidgets.QWidget):
"""交易组件"""
#----------------------------------------------------------------------
def __init__(self, omEngine, parent=None):
"""Constructor"""
super(TradingWidget, self).__init__(parent)
self.omEngine = omEngine
self.mainEngine = omEngine.mainEngine
self.portfolio = omEngine.portfolio
self.initUi()
#----------------------------------------------------------------------
def initUi(self):
"""初始化界面"""
self.setFixedWidth(200)
labelTradingWidget = QtWidgets.QLabel(u'期权交易')
labelSymbol = QtWidgets.QLabel(u'代码')
labelDirection = QtWidgets.QLabel(u'方向')
labelPrice = QtWidgets.QLabel(u'价格')
labelVolume = QtWidgets.QLabel(u'数量')
self.lineSymbol = QtWidgets.QLineEdit()
self.comboDirection = QtWidgets.QComboBox()
self.comboDirection.addItems([DIRECTION_LONG, DIRECTION_SHORT])
self.linePrice = QtWidgets.QLineEdit()
self.lineVolume = QtWidgets.QLineEdit()
self.buttonSendOrder = QtWidgets.QPushButton(u'发单')
self.buttonSendOrder.clicked.connect(self.sendOrder)
grid = QtWidgets.QGridLayout()
grid.addWidget(labelTradingWidget, 0, 0, 1, 2)
grid.addWidget(labelSymbol, 1, 0)
grid.addWidget(labelDirection, 2, 0)
grid.addWidget(labelPrice, 3, 0)
grid.addWidget(labelVolume, 4, 0)
grid.addWidget(self.lineSymbol, 1, 1)
grid.addWidget(self.comboDirection, 2, 1)
grid.addWidget(self.linePrice, 3, 1)
grid.addWidget(self.lineVolume, 4, 1)
grid.addWidget(self.buttonSendOrder, 5, 0, 1, 2)
self.setLayout(grid)
#----------------------------------------------------------------------
def sendOrder(self):
"""发送委托"""
try:
symbol = str(self.lineSymbol.text())
direction = str(self.comboDirection.currentText())
price = float(self.linePrice.text())
volume = int(self.lineVolume.text())
except:
return
instrument = self.portfolio.instrumentDict.get(symbol, None)
if not instrument:
return
# 做多
if direction == DIRECTION_LONG:
# 如果空头仓位大于等于买入量,则只需平
if instrument.shortPos >= volume:
self.fastTrade(symbol, DIRECTION_LONG, OFFSET_CLOSE, price, volume)
# 否则先平后开
else:
openVolume = volume - instrument.shortPos
if instrument.shortPos:
self.fastTrade(symbol, DIRECTION_LONG, OFFSET_CLOSE, price, instrument.shortPos)
self.fastTrade(symbol, DIRECTION_LONG, OFFSET_OPEN, price, openVolume)
# 做空
else:
if instrument.longPos >= volume:
self.fastTrade(symbol, DIRECTION_SHORT, OFFSET_CLOSE, price, volume)
else:
openVolume = volume - instrument.longPos
if instrument.longPos:
self.fastTrade(symbol, DIRECTION_SHORT, OFFSET_CLOSE, price, instrument.longPos)
self.fastTrade(symbol ,DIRECTION_SHORT, OFFSET_OPEN, price, openVolume)
#----------------------------------------------------------------------
def fastTrade(self, symbol, direction, offset, price, volume):
"""封装下单函数"""
contract = self.mainEngine.getContract(symbol)
if not contract:
return
req = VtOrderReq()
req.symbol = symbol
req.exchange = contract.exchange
req.direction = direction
req.offset = offset
req.price = price
req.volume = volume
req.priceType = PRICETYPE_LIMITPRICE
self.mainEngine.sendOrder(req, contract.gatewayName)
#----------------------------------------------------------------------
def updateWidget(self, item):
"""双击监控组件单元格后自动更新组件"""
instrument = item.data
if not instrument:
return
self.lineSymbol.setText(instrument.symbol)
# short
if item.background is COLOR_BID:
self.comboDirection.setCurrentIndex(1)
self.linePrice.setText(str(instrument.bidPrice1))
self.lineVolume.setText(str(instrument.bidVolume1))
# long
elif item.background is COLOR_ASK:
self.comboDirection.setCurrentIndex(0)
self.linePrice.setText(str(instrument.askPrice1))
self.lineVolume.setText(str(instrument.askVolume1))
########################################################################
class ManualTrader(QtWidgets.QWidget):
"""手动交易组件"""
#----------------------------------------------------------------------
def __init__(self, omEngine, parent=None):
"""Constructor"""
super(ManualTrader, self).__init__(parent)
self.omEngine = omEngine
self.mainEngine = omEngine.mainEngine
self.eventEngine = omEngine.eventEngine
self.initUi()
#----------------------------------------------------------------------
def initUi(self):
"""初始化界面"""
self.setWindowTitle(u'手动交易')
posMonitor = PositionMonitor(self.mainEngine, self.eventEngine)
for i in range(posMonitor.columnCount()):
posMonitor.horizontalHeader().setResizeMode(QtWidgets.QHeaderView.Stretch)
posMonitor.setSorting(False)
orderMonitor = WorkingOrderMonitor(self.mainEngine, self.eventEngine)
for i in range(orderMonitor.columnCount()):
orderMonitor.horizontalHeader().setResizeMode(QtWidgets.QHeaderView.Stretch)
orderMonitor.setSorting(False)
tradingWidget = TradingWidget(self.omEngine)
chainMonitor = ChainMonitor(self.omEngine, self.eventEngine)
chainMonitor.itemDoubleClicked.connect(tradingWidget.updateWidget)
vbox1 = QtWidgets.QVBoxLayout()
vbox1.addWidget(orderMonitor)
vbox1.addWidget(posMonitor)
vbox2 = QtWidgets.QVBoxLayout()
vbox2.addWidget(tradingWidget)
vbox2.addStretch()
hbox = QtWidgets.QHBoxLayout()
hbox.addLayout(vbox1)
hbox.addLayout(vbox2)
vbox3 = QtWidgets.QVBoxLayout()
vbox3.addWidget(chainMonitor)
vbox3.addLayout(hbox)
self.setLayout(vbox3)

View File

@ -9,6 +9,7 @@ from vnpy.event import Event
from vnpy.trader.uiQt import QtWidgets, QtCore
from .omBase import EVENT_OM_LOG
from .uiOmManualTrader import ManualTrader
########################################################################
@ -51,12 +52,16 @@ class OmManager(QtWidgets.QWidget):
self.buttonInit = QtWidgets.QPushButton(u'初始化')
self.buttonInit.clicked.connect(self.initOmEngine)
self.buttonManualTrader = QtWidgets.QPushButton(u'手动交易')
self.buttonManualTrader.clicked.connect(self.openManualTrader)
self.logMonitor = QtWidgets.QTextEdit()
self.logMonitor.setReadOnly(True)
hbox = QtWidgets.QHBoxLayout()
hbox.addWidget(self.comboSettingFile)
hbox.addWidget(self.buttonInit)
hbox.addWidget(self.buttonManualTrader)
hbox.addStretch()
hbox2 = QtWidgets.QHBoxLayout()
@ -99,7 +104,7 @@ class OmManager(QtWidgets.QWidget):
#----------------------------------------------------------------------
def openManualTrader(self):
"""打开手动交易"""
"""打开手动交易组件"""
try:
self.widgetDict['manualTrader'].showMaximized()
except KeyError:
@ -120,4 +125,3 @@ class OmManager(QtWidgets.QWidget):
self.signal.connect(self.processLogEvent)
self.eventEngine.register(EVENT_OM_LOG, self.signal.emit)