[Add]添加持仓情景分析功能
This commit is contained in:
parent
dfb23ab2f4
commit
20bae552d6
@ -1 +1 @@
|
||||
1akdYJY+uYtFOYGtWEJkKg==
|
||||
3PhIldq+vwFT0Ap6unvLxA==
|
@ -130,7 +130,7 @@ def calculateImpv(price, f, k, r, t, cp):
|
||||
|
||||
# 如果vega过小接近0,则直接返回
|
||||
if not vega:
|
||||
return v
|
||||
break
|
||||
|
||||
# 计算误差
|
||||
dx = (price - p) / vega
|
||||
@ -143,7 +143,7 @@ def calculateImpv(price, f, k, r, t, cp):
|
||||
v += dx
|
||||
|
||||
# 检查波动率计算结果非负
|
||||
if v < 0:
|
||||
if v <= 0:
|
||||
return 0
|
||||
|
||||
# 保留4位小数
|
||||
|
@ -368,9 +368,10 @@ class OmPortfolio(object):
|
||||
"""持仓组合"""
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self, name, underlyingList, chainList):
|
||||
def __init__(self, name, model, underlyingList, chainList):
|
||||
"""Constructor"""
|
||||
self.name = name
|
||||
self.model = model
|
||||
|
||||
# 原始容器
|
||||
self.underlyingDict = OrderedDict()
|
||||
|
@ -156,7 +156,7 @@ class OmEngine(object):
|
||||
underlying.addChain(chain)
|
||||
|
||||
# 创建持仓组合对象并初始化
|
||||
self.portfolio = OmPortfolio(setting['name'], underlyingDict.values(), chainList)
|
||||
self.portfolio = OmPortfolio(setting['name'], model, underlyingDict.values(), chainList)
|
||||
|
||||
# 载入波动率配置
|
||||
self.loadImpvSetting()
|
||||
@ -198,7 +198,7 @@ class OmEngine(object):
|
||||
f.close()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def close(self):
|
||||
def stop(self):
|
||||
"""关闭函数"""
|
||||
self.saveImpvSetting()
|
||||
|
||||
|
208
vnpy/trader/app/optionMaster/uiOmAnalysisManager.py
Normal file
208
vnpy/trader/app/optionMaster/uiOmAnalysisManager.py
Normal file
@ -0,0 +1,208 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
from __future__ import division
|
||||
|
||||
import numpy as np
|
||||
|
||||
from .uiOmBase import *
|
||||
|
||||
|
||||
########################################################################
|
||||
class ScenarioValueMonitor(QtWidgets.QTableWidget):
|
||||
"""情景分析监控工具,某一个数值"""
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self, key, parent=None):
|
||||
"""Constructor"""
|
||||
super(ScenarioValueMonitor, self).__init__(parent)
|
||||
|
||||
self.key = key
|
||||
|
||||
self.initUi()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def initUi(self):
|
||||
"""初始化界面"""
|
||||
self.setEditTriggers(self.NoEditTriggers)
|
||||
|
||||
self.setMinimumHeight(600)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def updateData(self, result, priceChangeArray, impvChangeArray):
|
||||
"""更新界面"""
|
||||
# 清空表格
|
||||
self.clearContents()
|
||||
|
||||
# 设置表头
|
||||
self.setColumnCount(len(priceChangeArray))
|
||||
priceChangeHeaders = [('%s%%' %(priceChange*100)) for priceChange in priceChangeArray]
|
||||
self.setHorizontalHeaderLabels(priceChangeHeaders)
|
||||
|
||||
self.setRowCount(len(impvChangeArray))
|
||||
impvChangeHeaders = [('%s%%' %(impvChange*100)) for impvChange in impvChangeArray]
|
||||
self.setVerticalHeaderLabels(impvChangeHeaders)
|
||||
|
||||
# 设置数据
|
||||
l = [d[self.key] for d in result.values()]
|
||||
maxValue = max(l)
|
||||
minValue = min(l)
|
||||
|
||||
# 最大和最小值相等,则说明计算逻辑有问题
|
||||
if maxValue == minValue:
|
||||
return
|
||||
|
||||
midValue = (maxValue + minValue) / 2
|
||||
colorRatio = 255*2/(maxValue-minValue)
|
||||
|
||||
for column, priceChange in enumerate(priceChangeArray):
|
||||
for row, impvChange in enumerate(impvChangeArray):
|
||||
value = result[(priceChange, impvChange)][self.key]
|
||||
|
||||
# 计算颜色
|
||||
red = 255
|
||||
green = 255
|
||||
colorValue = (value - midValue) * colorRatio
|
||||
|
||||
if colorValue <= 0:
|
||||
red -= abs(colorValue)
|
||||
else:
|
||||
green -= abs(colorValue)
|
||||
color = QtGui.QColor(red, green, 0)
|
||||
|
||||
# 插入单元格到表格中
|
||||
cell = QtWidgets.QTableWidgetItem('%.1f' %value)
|
||||
cell.setBackground(color)
|
||||
cell.setForeground(COLOR_BLACK)
|
||||
self.setItem(row, column, cell)
|
||||
|
||||
self.resizeColumnsToContents()
|
||||
self.resizeRowsToContents()
|
||||
|
||||
|
||||
########################################################################
|
||||
class ScenarioAnalysisMonitor(QtWidgets.QTabWidget):
|
||||
"""情景分析监控组件"""
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self, parent=None):
|
||||
"""Constructor"""
|
||||
super(ScenarioAnalysisMonitor, self).__init__(parent)
|
||||
|
||||
self.initUi()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def initUi(self):
|
||||
""""""
|
||||
self.valueMonitorList = []
|
||||
|
||||
for key in ['pnl', 'delta', 'gamma', 'theta', 'vega']:
|
||||
valueMonitor = ScenarioValueMonitor(key)
|
||||
self.addTab(valueMonitor, key)
|
||||
self.valueMonitorList.append(valueMonitor)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def updateData(self, result, priceChangeArray, impvChangeArray):
|
||||
"""更新数据"""
|
||||
for valueMonitor in self.valueMonitorList:
|
||||
valueMonitor.updateData(result, priceChangeArray, impvChangeArray)
|
||||
|
||||
|
||||
########################################################################
|
||||
class AnalysisManager(QtWidgets.QWidget):
|
||||
"""研究分析管理"""
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self, omEngine, parent=None):
|
||||
"""Constructor"""
|
||||
super(AnalysisManager, self).__init__(parent)
|
||||
|
||||
self.omEngine = omEngine
|
||||
self.portfolio = omEngine.portfolio
|
||||
|
||||
self.initUi()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def initUi(self):
|
||||
"""初始化界面"""
|
||||
self.setWindowTitle(u'持仓分析')
|
||||
|
||||
self.scenarioAnalysisMonitor = ScenarioAnalysisMonitor()
|
||||
|
||||
self.buttonScenarioAnalysis = QtWidgets.QPushButton(u'情景分析')
|
||||
self.buttonScenarioAnalysis.clicked.connect(self.updateData)
|
||||
|
||||
hbox = QtWidgets.QHBoxLayout()
|
||||
hbox.addWidget(self.buttonScenarioAnalysis)
|
||||
hbox.addStretch()
|
||||
|
||||
vbox = QtWidgets.QVBoxLayout()
|
||||
vbox.addLayout(hbox)
|
||||
vbox.addWidget(self.scenarioAnalysisMonitor)
|
||||
self.setLayout(vbox)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def updateData(self):
|
||||
"""更新数据"""
|
||||
result, priceChangeArray, impvChangeArray = self.runScenarioAnalysis()
|
||||
if result:
|
||||
self.scenarioAnalysisMonitor.updateData(result, priceChangeArray, impvChangeArray)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def runScenarioAnalysis(self):
|
||||
"""运行情景分析"""
|
||||
portfolio = self.portfolio
|
||||
calculateGreeks = portfolio.model.calculateGreeks
|
||||
|
||||
if not portfolio:
|
||||
return None, None, None
|
||||
|
||||
changeRange = 5
|
||||
priceChangeArray = np.arange(-changeRange, changeRange+1) / 100
|
||||
impvChangeArray = np.arange(-changeRange, changeRange+1) / 100
|
||||
expiryChange = 1/240 # 一个交易日对应的时间变化
|
||||
result = {} # 分析结果
|
||||
|
||||
for priceChange in priceChangeArray:
|
||||
for impvChange in impvChangeArray:
|
||||
print priceChange, impvChange
|
||||
portfolioPnl = 0
|
||||
portfolioDelta = 0
|
||||
portfolioGamma = 0
|
||||
portfolioTheta = 0
|
||||
portfolioVega = 0
|
||||
|
||||
for underlying in portfolio.underlyingDict.values():
|
||||
portfolioPnl += underlying.midPrice * underlying.netPos * priceChange
|
||||
portfolioDelta += underlying.theoDelta * underlying.netPos
|
||||
|
||||
try:
|
||||
for option in portfolio.optionDict.values():
|
||||
if not option.netPos:
|
||||
continue
|
||||
|
||||
price, delta, gamma, theta, vega = calculateGreeks(option.underlying.midPrice*(1+priceChange),
|
||||
option.k,
|
||||
option.r,
|
||||
max(option.t-expiryChange, 0),
|
||||
option.pricingImpv*(1+impvChange),
|
||||
option.cp)
|
||||
|
||||
portfolioPnl += (price - option.theoPrice) * option.netPos * option.size
|
||||
portfolioDelta += delta * option.netPos * option.size
|
||||
portfolioGamma += gamma * option.netPos * option.size
|
||||
portfolioTheta += theta * option.netPos * option.size
|
||||
portfolioVega += vega * option.netPos * option.size
|
||||
except ZeroDivisionError:
|
||||
return None, None, None
|
||||
|
||||
d = {
|
||||
'pnl': portfolioPnl,
|
||||
'delta': portfolioDelta,
|
||||
'gamma': portfolioGamma,
|
||||
'theta': portfolioTheta,
|
||||
'vega': portfolioVega
|
||||
}
|
||||
result[(priceChange, impvChange)] = d
|
||||
|
||||
return result, priceChangeArray, impvChangeArray
|
||||
|
@ -12,6 +12,7 @@ from .uiOmBase import QtWidgets, QtCore
|
||||
from .uiOmManualTrader import ManualTrader
|
||||
from .uiOmGreeksMonitor import GreeksMonitor
|
||||
from .uiOmVolatilityManager import VolatilityChart, VolatilityManager
|
||||
from .uiOmAnalysisManager import AnalysisManager
|
||||
|
||||
|
||||
########################################################################
|
||||
@ -70,6 +71,10 @@ class OmManager(QtWidgets.QWidget):
|
||||
self.buttonVolatilityManager.clicked.connect(self.openVolatilityManager)
|
||||
self.buttonVolatilityManager.setDisabled(True)
|
||||
|
||||
self.buttonAnalysisManager = QtWidgets.QPushButton(u'持仓分析')
|
||||
self.buttonAnalysisManager.clicked.connect(self.openAnalysisManager)
|
||||
self.buttonAnalysisManager.setDisabled(True)
|
||||
|
||||
self.logMonitor = QtWidgets.QTextEdit()
|
||||
self.logMonitor.setReadOnly(True)
|
||||
|
||||
@ -80,6 +85,7 @@ class OmManager(QtWidgets.QWidget):
|
||||
hbox.addWidget(self.buttonGreeksMonitor)
|
||||
hbox.addWidget(self.buttonVolatilityChart)
|
||||
hbox.addWidget(self.buttonVolatilityManager)
|
||||
hbox.addWidget(self.buttonAnalysisManager)
|
||||
hbox.addStretch()
|
||||
|
||||
hbox2 = QtWidgets.QHBoxLayout()
|
||||
@ -116,6 +122,7 @@ class OmManager(QtWidgets.QWidget):
|
||||
self.buttonGreeksMonitor.setEnabled(True)
|
||||
self.buttonVolatilityChart.setEnabled(True)
|
||||
self.buttonVolatilityManager.setEnabled(True)
|
||||
self.buttonAnalysisManager.setEnabled(True)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def writeLog(self, content, time=''):
|
||||
@ -168,6 +175,15 @@ class OmManager(QtWidgets.QWidget):
|
||||
self.widgetDict['volatilityManager'] = VolatilityManager(self.omEngine)
|
||||
self.widgetDict['volatilityManager'].show()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def openAnalysisManager(self):
|
||||
"""打开持仓分析组件"""
|
||||
try:
|
||||
self.widgetDict['analysisManager'].showMaximized()
|
||||
except KeyError:
|
||||
self.widgetDict['analysisManager'] = AnalysisManager(self.omEngine)
|
||||
self.widgetDict['analysisManager'].showMaximized()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def close(self):
|
||||
"""关闭"""
|
||||
|
Loading…
Reference in New Issue
Block a user