[Add]初步完成OptionMaster核心定价部分的开发

This commit is contained in:
vn.py 2017-11-30 15:08:35 +08:00
parent a4394d2baa
commit 64bd2eb6d2
11 changed files with 269 additions and 87 deletions

View File

@ -1 +1 @@
PurwJxAEvTeQ9X8HMnmMRw== B7/DvEptJ5IBD1LIj9SEFg==

View File

@ -19,7 +19,7 @@ from vnpy.trader.uiMainWindow import MainWindow
from vnpy.trader.gateway import (secGateway) from vnpy.trader.gateway import (secGateway)
# 加载上层应用 # 加载上层应用
from vnpy.trader.app import (riskManager) from vnpy.trader.app import (riskManager, optionMaster)
#---------------------------------------------------------------------- #----------------------------------------------------------------------
@ -39,6 +39,7 @@ def main():
# 添加上层应用 # 添加上层应用
me.addApp(riskManager) me.addApp(riskManager)
me.addApp(optionMaster)
# 创建主窗口 # 创建主窗口
mw = MainWindow(me, ee) mw = MainWindow(me, ee)

0
vnpy/pricing/__init__.py Normal file
View File

View File

@ -23,6 +23,7 @@ theta当t变动1天时price的变动国内交易日每年240天
vega当v涨跌1个点时price的变动如从16%涨到17% vega当v涨跌1个点时price的变动如从16%涨到17%
''' '''
from __future__ import division
from scipy import stats from scipy import stats
from math import (log, pow, sqrt, exp) from math import (log, pow, sqrt, exp)
@ -74,9 +75,15 @@ def calculateTheta(f, k, r, t, v, cp):
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def calculateVega(f, k, r, t, v, cp): def calculateVega(f, k, r, t, v, cp):
"""计算Vega值""" """计算Vega值"""
vega = calculateVega(f, k, r, t, v, cp) / 100
return vega
#----------------------------------------------------------------------
def calculateOriginalVega(f, k, r, t, v, cp):
"""计算原始vega值"""
price1 = calculatePrice(f, k, r, t, v*STEP_UP, cp) price1 = calculatePrice(f, k, r, t, v*STEP_UP, cp)
price2 = calculatePrice(f, k, r, t, v*STEP_DOWN, cp) price2 = calculatePrice(f, k, r, t, v*STEP_DOWN, cp)
vega = (price1 - price2) / (v * STEP_DIFF * 100) vega = (price1 - price2) / (v * STEP_DIFF)
return vega return vega
#---------------------------------------------------------------------- #----------------------------------------------------------------------
@ -109,12 +116,13 @@ def calculateImpv(price, f, k, r, t, cp):
return 0 return 0
# 采用Newton Raphson方法计算隐含波动率 # 采用Newton Raphson方法计算隐含波动率
v = 0.2 # 初始波动率猜测 v = 0.3 # 初始波动率猜测
for i in range(50): for i in range(50):
# 计算当前猜测波动率对应的期权价格和vega值 # 计算当前猜测波动率对应的期权价格和vega值
p = calculatePrice(f, k, r, t, v, cp) p = calculatePrice(f, k, r, t, v, cp)
vega = calculateVega(f, k, r, t, v, cp) #print 'calculating vega', f, k, r, t, v, cp
vega = calculateOriginalVega(f, k, r, t, v, cp)
# 计算误差 # 计算误差
dx = (price - p) / vega dx = (price - p) / vega

View File

@ -1,10 +1,10 @@
# encoding: UTF-8 # encoding: UTF-8
#from rmEngine import RmEngine from .omEngine import OmEngine
#from uiRmWidget import RmEngineManager from .uiOmWidget import OmManager
appName = 'OptionMaster' appName = 'OptionMaster'
appDisplayName = u'OptionMaster' appDisplayName = u'OptionMaster'
appEngine = None appEngine = OmEngine
appWidget = None appWidget = OmManager
appIco = 'om.ico' appIco = 'om.ico'

View File

@ -2,19 +2,18 @@
"name": "etf_portfolio", "name": "etf_portfolio",
"model": "black", "model": "black",
"underlying": [ "underlying": [
"IH1711", "510050"
"IH1712"
], ],
"chain": [ "chain": [
{ {
"underlyingSymbol": "IH1711", "underlyingSymbol": "510050",
"chainSymbol": "m1709", "chainSymbol": "510050-1712",
"interestRate": 0.03, "r": 0.03
}, },
{ {
"underlyingSymbol": "IH1711", "underlyingSymbol": "510050",
"chainSymbol": "m1801", "chainSymbol": "510050-1801",
"interestRate": 0.03, "r": 0.03
} }
] ]
} }

View File

@ -1,5 +1,7 @@
# encoding: UTF-8 # encoding: UTF-8
from __future__ import division
from copy import copy from copy import copy
from collections import OrderedDict from collections import OrderedDict
@ -26,7 +28,10 @@ class OmInstrument(VtTickData):
"""Constructor""" """Constructor"""
super(OmInstrument, self).__init__() super(OmInstrument, self).__init__()
self.tickInited = False
# 初始化合约信息 # 初始化合约信息
self.symbol = contract.symbol
self.exchange = contract.exchange self.exchange = contract.exchange
self.vtSymbol = contract.vtSymbol self.vtSymbol = contract.vtSymbol
@ -38,10 +43,15 @@ class OmInstrument(VtTickData):
self.midPrice = EMPTY_FLOAT self.midPrice = EMPTY_FLOAT
# 持仓数据 # 持仓数据
self.longPos = detail.longPos self.longPos = 0
self.shortPos = detail.shortPos self.shortPos = 0
self.netPos = self.longPos - self.shortPos self.netPos = 0
if detail:
self.longPos = detail.longPos
self.shortPos = detail.shortPos
self.netPos = self.longPos - self.shortPos
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def newTick(self, tick): def newTick(self, tick):
"""行情更新""" """行情更新"""
@ -61,6 +71,7 @@ class OmInstrument(VtTickData):
self.askPrice1 = tick.askPrice1 self.askPrice1 = tick.askPrice1
self.bidVolume1 = tick.bidVolume1 self.bidVolume1 = tick.bidVolume1
self.askVolume1 = tick.askVolume1 self.askVolume1 = tick.askVolume1
self.midPrice = (self.bidPrice1 + self.askPrice1) / 2
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def newTrade(self, trade): def newTrade(self, trade):
@ -98,17 +109,22 @@ class OmUnderlying(OmInstrument):
"""标的物""" """标的物"""
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def __init__(self, contract, chainList): def __init__(self, contract, detail, chainList=None):
"""Constructor""" """Constructor"""
super(OmUnderlying, self).__init__(contract) super(OmUnderlying, self).__init__(contract, detail)
# 以该合约为标的物的期权链字典 # 以该合约为标的物的期权链字典
self.chainDict = OrderedDict((chain.symbol, chain) for chain in chainList) self.chainDict = OrderedDict()
# 希腊值 # 希腊值
self.theoDelta = EMPTY_FLOAT # 理论delta值 self.theoDelta = EMPTY_FLOAT # 理论delta值
self.posDelta = EMPTY_FLOAT # 持仓delta值 self.posDelta = EMPTY_FLOAT # 持仓delta值
#----------------------------------------------------------------------
def addChain(self, chain):
"""添加以该合约为标的的期权链"""
self.chainDict[chain.symbol] = chain
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def newTick(self, tick): def newTick(self, tick):
"""行情更新""" """行情更新"""
@ -117,7 +133,7 @@ class OmUnderlying(OmInstrument):
self.theoDelta = self.size * self.midPrice / 100 self.theoDelta = self.size * self.midPrice / 100
# 遍历推送自己的行情到期权链中 # 遍历推送自己的行情到期权链中
for chain in self.chainList: for chain in self.chainDict.values():
chain.newUnderlyingTick() chain.newUnderlyingTick()
#---------------------------------------------------------------------- #----------------------------------------------------------------------
@ -137,12 +153,12 @@ class OmOption(OmInstrument):
"""期权""" """期权"""
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def __init__(self, contract, underlying, model, r): def __init__(self, contract, detail, underlying, model, r):
"""Constructor""" """Constructor"""
super(OmOption, self).__init__(contract) super(OmOption, self).__init__(contract, detail)
# 期权属性 # 期权属性
self.underlying = None # 标的物对象 self.underlying = underlying # 标的物对象
self.k = contract.strikePrice # 行权价 self.k = contract.strikePrice # 行权价
self.r = r # 利率 self.r = r # 利率
@ -183,7 +199,7 @@ class OmOption(OmInstrument):
self.chain = None self.chain = None
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def calculateImpv(self): def calculateOptionImpv(self):
"""计算隐含波动率""" """计算隐含波动率"""
underlyingPrice = self.underlying.midPrice underlyingPrice = self.underlying.midPrice
if not underlyingPrice: if not underlyingPrice:
@ -222,12 +238,12 @@ class OmOption(OmInstrument):
def newTick(self, tick): def newTick(self, tick):
"""行情更新""" """行情更新"""
super(OmOption, self).newTick(tick) super(OmOption, self).newTick(tick)
self.calculateImpv() self.calculateOptionImpv()
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def newUnderlyingTick(self): def newUnderlyingTick(self):
"""标的行情更新""" """标的行情更新"""
self.calculateImpv() self.calculateOptionImpv()
self.calculateTheoGreeks() self.calculateTheoGreeks()
self.calculatePosGreeks() self.calculatePosGreeks()
@ -243,7 +259,6 @@ class OmOption(OmInstrument):
self.underlying = underlying self.underlying = underlying
######################################################################## ########################################################################
class OmChain(object): class OmChain(object):
"""期权链""" """期权链"""
@ -394,10 +409,10 @@ class OmPortfolio(object):
self.posTheta = 0 self.posTheta = 0
self.posVega = 0 self.posVega = 0
for underlying in self.underlyingList: for underlying in self.underlyingDict.values():
self.posDelta += underlying.posDelta self.posDelta += underlying.posDelta
for chain in self.chainList: for chain in self.chainDict.values():
self.longPos += chain.longPos self.longPos += chain.longPos
self.shortPos += chain.shortPos self.shortPos += chain.shortPos

View File

@ -5,16 +5,26 @@ import json
import shelve import shelve
import os import os
import traceback import traceback
from collections import OrderedDict
from vnpy.event import Event from vnpy.event import Event
from vnpy.trader.vtEvent import EVENT_TICK, EVENT_TRADE, EVENT_CONTRACT from vnpy.trader.vtEvent import EVENT_TICK, EVENT_TRADE, EVENT_CONTRACT
from vnpy.trader.vtFunction import getTempPath, getJsonPath from vnpy.trader.vtFunction import getTempPath, getJsonPath
from vnpy.trader.vtObject import VtLogData from vnpy.trader.vtObject import VtLogData, VtSubscribeReq
from vnpy.trader.vtConstant import PRODUCT_OPTION, OPTION_CALL, OPTION_PUT
from vnpy.pricing import black
from .omBase import (OmOption, OmUnderlying, OmChain, OmPortfolio, from .omBase import (OmOption, OmUnderlying, OmChain, OmPortfolio,
EVENT_OM_LOG) EVENT_OM_LOG)
# 定价模型字典
MODEL_DICT = {}
MODEL_DICT['black'] = black
######################################################################## ########################################################################
class OmEngine(object): class OmEngine(object):
"""期权主引擎""" """期权主引擎"""
@ -28,7 +38,7 @@ class OmEngine(object):
self.eventEngine = eventEngine self.eventEngine = eventEngine
self.portfolio = None self.portfolio = None
self.contractDict = {} # symbol:contract self.optionContractDict = {} # symbol:contract
self.registerEvent() self.registerEvent()
@ -53,18 +63,22 @@ class OmEngine(object):
def processContractEvent(self, event): def processContractEvent(self, event):
"""合约事件""" """合约事件"""
contract = event.dict_['data'] contract = event.dict_['data']
if contract.symbol: if contract.symbol and contract.productClass == PRODUCT_OPTION:
self.contractDict[contract.symbol] = contract self.optionContractDict[contract.symbol] = contract
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def subscribeEvent(self, symbol): def subscribeEvent(self, symbol):
"""订阅对应合约的事件""" """订阅对应合约的事件"""
contract = self.contractDict[symbol] contract = self.mainEngine.getContract(symbol)
if not contract:
self.writeLog(u'行情订阅失败,找不到合约:%s' %symbol)
return
vtSymbol = contract.vtSymbol vtSymbol = contract.vtSymbol
# 订阅行情 # 订阅行情
req = VtSubscribeReq() req = VtSubscribeReq()
req.symbol = symbol req.symbol = contract.symbol
req.exchange = contract.exchange req.exchange = contract.exchange
self.mainEngine.subscribe(req, contract.gatewayName) self.mainEngine.subscribe(req, contract.gatewayName)
@ -80,67 +94,81 @@ class OmEngine(object):
f = file(fileName) f = file(fileName)
setting = json.load(f) setting = json.load(f)
# 读取定价模型
model = MODEL_DICT.get(setting['model'], None)
if not model:
self.writeLog(u'找不到定价模型%s' %setting['model'])
return
# 创建期货和股票标的对象 # 创建标的对象
equityDict = OrderedDict([(symbol, OmEquity(symbol)) for symbol in setting['equity']]) underlyingDict = OrderedDict()
futuresDict = OrderedDict([(symbol, OmFutures(symbol)) for symbol in setting['futures']])
for underlyingSymbol in setting['underlying']:
contract = self.mainEngine.getContract(underlyingSymbol)
if not contract:
self.writeLog(u'找不到标的物合约%s' %underlyingSymbol)
continue
detail = self.mainEngine.getPositionDetail(contract.vtSymbol)
underlying = OmUnderlying(contract, detail)
underlyingDict[underlyingSymbol] = underlying
# 创建期权链对象并初始化 # 创建期权链对象并初始化
chainDict = OrderedDict() chainList = []
for d in setting['chain']: for d in setting['chain']:
interestRate = d['interestRate'] chainSymbol = d['chainSymbol']
r = d['r']
# 锁定标的对象,若无则创建 # 锁定标的对象
if d['underlyingType'] == 'futures': underlying = underlyingDict.get(d['underlyingSymbol'], None)
if d['underlyingSymbol'] not in futuresDict: if not underlying:
underlying = OmFutures(d['underlyingSymbol']) self.writeLog(u'%s期权链的标的合约%s尚未创建,请检查配置文件' %(chainSymbol, underlyingSymbol))
futuresDict[underlying.symbol] = underlying continue
else:
underlying = futuresDict[d['underlyingSymbol']]
elif d['underlyingType'] == 'equity':
if d['underlyingSymbol'] not in equityDict:
underlying = OmEquity(d['underlyingSymbol'])
equityDict[underlying.symbol] = underlying
else:
underlying = equityDict[d['underlyingSymbol']]
# 创建期权对象并初始化 # 创建期权对象并初始化
callList = [] callDict = {}
putList = [] putDict = {}
for symbol, contract in self.contractDict.items(): for symbol, contract in self.optionContractDict.items():
if contract.optionType and contract.underlyingSymbol == d['chainSymbol']: if contract.underlyingSymbol == d['chainSymbol']:
option = OmOption(symbol) detail = self.mainEngine.getPositionDetail(contract.vtSymbol)
option.init(contract, underlying, interestRate) option = OmOption(contract, detail, underlying, model, r)
self.subscribeEvent(option.symbol) # 订阅事件
if contract.optionType is OPTION_CALL: if contract.optionType is OPTION_CALL:
callList.append(option) callDict[option.k] = option
else: else:
putList.append(option) putDict[option.k] = option
# 期权排序
strikeList = callDict.keys()
strikeList.sort()
callList = [callDict[k] for k in strikeList]
putList = [putDict[k] for k in strikeList]
# 创建期权链
chain = OmChain(chainSymbol, callList, putList)
chainList.append(chain)
# 添加标的映射关系
underlying.addChain(chain)
chain = OmChain(d['chainSymbol'])
chain.init(underlying, callList, putList)
chainDict[chain.symbol] = chain
# 初始化标的对象
for underlying in (equityDict.values() + futuresDict.values()):
l = []
for chain in chainDict.values():
if chain.underlying is underlying:
l.append(chain)
contract = self.contractDict[underlying.symbol]
underlying.init(contract, l)
self.subscribeEvent(underlying.symbol) # 订阅事件
# 创建持仓组合对象并初始化 # 创建持仓组合对象并初始化
self.portfolio = OmPortfolio(setting['name']) self.portfolio = OmPortfolio(setting['name'], underlyingDict.values(), chainList)
self.portfolio.init(futuresDict, equityDict, chainDict)
# 载入波动率配置 # 载入波动率配置
self.loadImpvSetting() self.loadImpvSetting()
# 订阅行情和事件
for underlying in underlyingDict.values():
self.subscribeEvent(underlying.vtSymbol)
for chain in chainList:
for option in chain.optionDict.values():
self.subscribeEvent(option.vtSymbol)
# 载入成功返回 # 载入成功返回
return True return True

View File

@ -0,0 +1,123 @@
# encoding: UTF-8
from __future__ import division
import os
from datetime import datetime
from vnpy.event import Event
from vnpy.trader.uiQt import QtWidgets, QtCore
from .omBase import EVENT_OM_LOG
########################################################################
class OmManager(QtWidgets.QWidget):
"""管理组件"""
signal = QtCore.pyqtSignal(type(Event()))
#----------------------------------------------------------------------
def __init__(self, omEngine, eventEngine, parent=None):
"""Constructor"""
super(OmManager, self).__init__(parent)
self.omEngine = omEngine
self.eventEngine = eventEngine
self.widgetDict = {}
self.initUi()
self.registerEvent()
#----------------------------------------------------------------------
def initUi(self):
"""初始化界面"""
self.setWindowTitle(u'OptionMaster管理')
# 读取配置文件
settingFileList = []
path = os.path.abspath(os.path.dirname(__file__))
for root, subdirs, files in os.walk(path):
for name in files:
if '_portfolio.json' in name:
settingFileList.append(name)
# 设置界面
self.comboSettingFile = QtWidgets.QComboBox()
self.comboSettingFile.addItems(settingFileList)
self.comboSettingFile.setCurrentIndex(0)
self.buttonInit = QtWidgets.QPushButton(u'初始化')
self.buttonInit.clicked.connect(self.initOmEngine)
self.logMonitor = QtWidgets.QTextEdit()
self.logMonitor.setReadOnly(True)
hbox = QtWidgets.QHBoxLayout()
hbox.addWidget(self.comboSettingFile)
hbox.addWidget(self.buttonInit)
hbox.addStretch()
hbox2 = QtWidgets.QHBoxLayout()
hbox2.addStretch()
vbox = QtWidgets.QVBoxLayout()
vbox.addLayout(hbox)
vbox.addLayout(hbox2)
vbox.addWidget(self.logMonitor)
self.setLayout(vbox)
#----------------------------------------------------------------------
def initOmEngine(self):
"""初始化引擎"""
path = os.path.abspath(os.path.dirname(__file__))
fileName = unicode(self.comboSettingFile.currentText())
fileName = os.path.join(path, fileName)
result = self.omEngine.initEngine(fileName)
if result:
self.writeLog(u'引擎初始化成功')
else:
self.writeLog(u'请勿重复初始化引擎')
#----------------------------------------------------------------------
def writeLog(self, content, time=''):
"""记录日志"""
if not time:
time = datetime.now().strftime('%H:%M:%S')
content = time + '\t' + content
self.logMonitor.append(content)
#----------------------------------------------------------------------
def processLogEvent(self, event):
"""处理日志事件"""
log = event.dict_['data']
self.writeLog(log.logContent, log.logTime)
self.raise_()
#----------------------------------------------------------------------
def openManualTrader(self):
"""打开手动交易"""
try:
self.widgetDict['manualTrader'].showMaximized()
except KeyError:
self.widgetDict['manualTrader'] = ManualTrader(self.omEngine)
self.widgetDict['manualTrader'].showMaximized()
#----------------------------------------------------------------------
def close(self):
"""关闭"""
for widget in self.widgetDict.values():
widget.close()
super(OmManagerWidget, self).close()
#----------------------------------------------------------------------
def registerEvent(self):
"""注册事件监听"""
self.signal.connect(self.processLogEvent)
self.eventEngine.register(EVENT_OM_LOG, self.signal.emit)

View File

@ -50,8 +50,11 @@ exchangeMapReverse = {v:k for k,v in exchangeMap.items()}
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def print_dict(d): def print_dict(d):
"""""" """"""
for k, v in d.items(): print '-' * 30
print '%s:%s' %(k, v) l = d.keys()
l.sort()
for k in l:
print '%s:%s' %(k, d[k])
######################################################################## ########################################################################
@ -893,7 +896,7 @@ class SecTdApi(TdApi):
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def onRspStockQryStockStaticInfo(self, data, error, flag): def onRspStockQryStockStaticInfo(self, data, error, flag):
"""股票合约查询回报""" """股票合约查询回报"""
if not data: if not data:
return return
@ -1226,8 +1229,8 @@ class SecTdApi(TdApi):
contract.strikePrice = data['execPrice'] contract.strikePrice = data['execPrice']
contract.underlyingSymbol = data['securityID'] contract.underlyingSymbol = '-'.join([data['securityID'], str(data['endTradingDay'])[2:-2]])
contract.expiryDate = data['endTradingDay'] contract.expiryDate = str(data['endTradingDay'])
# 合约类型 # 合约类型
contract.productClass = PRODUCT_OPTION contract.productClass = PRODUCT_OPTION

View File

@ -277,6 +277,11 @@ class MainEngine(object):
"""查询委托""" """查询委托"""
return self.dataEngine.getOrder(vtOrderID) return self.dataEngine.getOrder(vtOrderID)
#----------------------------------------------------------------------
def getPositionDetail(self, vtSymbol):
"""查询持仓细节"""
return self.dataEngine.getPositionDetail(vtSymbol)
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def getAllWorkingOrders(self): def getAllWorkingOrders(self):
"""查询所有的活跃的委托(返回列表)""" """查询所有的活跃的委托(返回列表)"""