[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.app import (riskManager)
from vnpy.trader.app import (riskManager, optionMaster)
#----------------------------------------------------------------------
@ -39,6 +39,7 @@ def main():
# 添加上层应用
me.addApp(riskManager)
me.addApp(optionMaster)
# 创建主窗口
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%
'''
from __future__ import division
from scipy import stats
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):
"""计算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)
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
#----------------------------------------------------------------------
@ -109,12 +116,13 @@ def calculateImpv(price, f, k, r, t, cp):
return 0
# 采用Newton Raphson方法计算隐含波动率
v = 0.2 # 初始波动率猜测
v = 0.3 # 初始波动率猜测
for i in range(50):
# 计算当前猜测波动率对应的期权价格和vega值
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

View File

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

View File

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

View File

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

View File

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

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):
""""""
for k, v in d.items():
print '%s:%s' %(k, v)
print '-' * 30
l = d.keys()
l.sort()
for k in l:
print '%s:%s' %(k, d[k])
########################################################################
@ -1226,8 +1229,8 @@ class SecTdApi(TdApi):
contract.strikePrice = data['execPrice']
contract.underlyingSymbol = data['securityID']
contract.expiryDate = data['endTradingDay']
contract.underlyingSymbol = '-'.join([data['securityID'], str(data['endTradingDay'])[2:-2]])
contract.expiryDate = str(data['endTradingDay'])
# 合约类型
contract.productClass = PRODUCT_OPTION

View File

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