【50ETF期权】 4. Greeks 和隐含波动率微笑

来源:https://uqer.io/community/share/560769faf9f06c597165ef75

在本文中,我们将通过量化实验室提供的数据,计算上证50ETF期权的隐含波动率微笑。

  1. from CAL.PyCAL import *
  2. import numpy as np
  3. import pandas as pd
  4. import matplotlib.pyplot as plt
  5. from matplotlib import rc
  6. rc('mathtext', default='regular')
  7. import seaborn as sns
  8. sns.set_style('white')
  9. import math
  10. from scipy import interpolate
  11. from scipy.stats import mstats
  12. from pandas import Series, DataFrame, concat
  13. import time
  14. from matplotlib import dates

上海银行间同业拆借利率 SHIBOR,用来作为无风险利率参考

  1. ## 银行间质押式回购利率
  2. def getHistDayInterestRateInterbankRepo(date):
  3. cal = Calendar('China.SSE')
  4. period = Period('-10B')
  5. begin = cal.advanceDate(date, period)
  6. begin_str = begin.toISO().replace('-', '')
  7. date_str = date.toISO().replace('-', '')
  8. # 以下的indicID分别对应的银行间质押式回购利率周期为:
  9. # 1D, 7D, 14D, 21D, 1M, 3M, 4M, 6M, 9M, 1Y
  10. indicID = [u"M120000067", u"M120000068", u"M120000069", u"M120000070", u"M120000071",
  11. u"M120000072", u"M120000073", u"M120000074", u"M120000075", u"M120000076"]
  12. period = np.asarray([1.0, 7.0, 14.0, 21.0, 30.0, 90.0, 120.0, 180.0, 270.0, 360.0]) / 360.0
  13. period_matrix = pd.DataFrame(index=indicID, data=period, columns=['period'])
  14. field = u"indicID,indicName,publishTime,periodDate,dataValue,unit"
  15. interbank_repo = DataAPI.ChinaDataInterestRateInterbankRepoGet(indicID=indicID,beginDate=begin_str,endDate=date_str,field=field,pandas="1")
  16. interbank_repo = interbank_repo.groupby('indicID').first()
  17. interbank_repo = concat([interbank_repo, period_matrix], axis=1, join='inner').sort_index()
  18. return interbank_repo
  19. ## 银行间同业拆借利率
  20. def getHistDaySHIBOR(date):
  21. cal = Calendar('China.SSE')
  22. period = Period('-10B')
  23. begin = cal.advanceDate(date, period)
  24. begin_str = begin.toISO().replace('-', '')
  25. date_str = date.toISO().replace('-', '')
  26. # 以下的indicID分别对应的SHIBOR周期为:
  27. # 1D, 7D, 14D, 1M, 3M, 6M, 9M, 1Y
  28. indicID = [u"M120000057", u"M120000058", u"M120000059", u"M120000060",
  29. u"M120000061", u"M120000062", u"M120000063", u"M120000064"]
  30. period = np.asarray([1.0, 7.0, 14.0, 30.0, 90.0, 180.0, 270.0, 360.0]) / 360.0
  31. period_matrix = pd.DataFrame(index=indicID, data=period, columns=['period'])
  32. field = u"indicID,indicName,publishTime,periodDate,dataValue,unit"
  33. interest_shibor = DataAPI.ChinaDataInterestRateSHIBORGet(indicID=indicID,beginDate=begin_str,endDate=date_str,field=field,pandas="1")
  34. interest_shibor = interest_shibor.groupby('indicID').first()
  35. interest_shibor = concat([interest_shibor, period_matrix], axis=1, join='inner').sort_index()
  36. return interest_shibor
  37. ## 插值得到给定的周期的无风险利率
  38. def periodsSplineRiskFreeInterestRate(date, periods):
  39. # 此处使用SHIBOR来插值
  40. init_shibor = getHistDaySHIBOR(date)
  41. shibor = {}
  42. min_period = min(init_shibor.period.values)
  43. min_period = 10.0/360.0
  44. max_period = max(init_shibor.period.values)
  45. for p in periods.keys():
  46. tmp = periods[p]
  47. if periods[p] > max_period:
  48. tmp = max_period * 0.99999
  49. elif periods[p] < min_period:
  50. tmp = min_period * 1.00001
  51. sh = interpolate.spline(init_shibor.period.values, init_shibor.dataValue.values, [tmp], order=3)
  52. shibor[p] = sh[0]/100.0
  53. return shibor
  • Greeks 和 隐含波动率计算 本文中计算的Greeks包括:

  • delta 期权价格关于标的价格的一阶导数

  • gamma 期权价格关于标的价格的二阶导数
  • rho 期权价格关于无风险利率的一阶导数
  • theta 期权价格关于到期时间的一阶导数
  • vega 期权价格关于波动率的一阶导数 注意:

  • 计算隐含波动率,我们采用Black-Scholes-Merton模型,此模型在平台Python包CAL中已有实现

  • 无风险利率使用SHIBOR
  • 期权的时间价值为负时(此种情况在50ETF期权里时有发生),没法通过BSM模型计算隐含波动率,故此时将期权隐含波动率设为0.0,实际上,此时的隐含波动率和各风险指标并无实际参考价值
  1. ## 使用DataAPI.OptGet, DataAPI.MktOptdGet拿到计算所需数据
  2. def getOptDayData(opt_var_sec, date):
  3. date_str = date.toISO().replace('-', '')
  4. #使用DataAPI.OptGet,拿到已退市和上市的所有期权的基本信息
  5. info_fields = [u'optID', u'varSecID', u'varShortName', u'varTicker', u'varExchangeCD', u'varType',
  6. u'contractType', u'strikePrice', u'contMultNum', u'contractStatus', u'listDate',
  7. u'expYear', u'expMonth', u'expDate', u'lastTradeDate', u'exerDate', u'deliDate',
  8. u'delistDate']
  9. opt_info = DataAPI.OptGet(optID='', contractStatus=[u"DE",u"L"], field=info_fields, pandas="1")
  10. #使用DataAPI.MktOptdGet,拿到历史上某一天的期权成交信息
  11. mkt_fields = [u'ticker', u'optID', u'secShortName', u'exchangeCD', u'tradeDate', u'preSettlePrice',
  12. u'preClosePrice', u'openPrice', u'highestPrice', u'lowestPrice', u'closePrice',
  13. u'settlPrice', u'turnoverVol', u'turnoverValue', u'openInt']
  14. opt_mkt = DataAPI.MktOptdGet(tradeDate=date_str, field=mkt_fields, pandas = "1")
  15. opt_info = opt_info.set_index(u"optID")
  16. opt_mkt = opt_mkt.set_index(u"optID")
  17. opt = concat([opt_info, opt_mkt], axis=1, join='inner').sort_index()
  18. return opt
  19. ## 分析历史某一日的期权收盘价信息,得到隐含波动率微笑和期权风险指标
  20. def getOptDayAnalysis(opt_var_sec, date):
  21. opt = getOptDayData(opt_var_sec, date)
  22. #使用DataAPI.MktFunddGet拿到期权标的的日行情
  23. date_str = date.toISO().replace('-', '')
  24. opt_var_mkt = DataAPI.MktFunddGet(secID=opt_var_sec,tradeDate=date_str,beginDate=u"",endDate=u"",field=u"",pandas="1")
  25. #opt_var_mkt = DataAPI.MktFunddAdjGet(secID=opt_var_sec,beginDate=date_str,endDate=date_str,field=u"",pandas="1")
  26. # 计算shibor
  27. exp_dates_str = opt.expDate.unique()
  28. periods = {}
  29. for date_str in exp_dates_str:
  30. exp_date = Date.parseISO(date_str)
  31. periods[exp_date] = (exp_date - date)/360.0
  32. shibor = periodsSplineRiskFreeInterestRate(date, periods)
  33. settle = opt.settlPrice.values # 期权 settle price
  34. close = opt.closePrice.values # 期权 close price
  35. strike = opt.strikePrice.values # 期权 strike price
  36. option_type = opt.contractType.values # 期权类型
  37. exp_date_str = opt.expDate.values # 期权行权日期
  38. eval_date_str = opt.tradeDate.values # 期权交易日期
  39. mat_dates = []
  40. eval_dates = []
  41. spot = []
  42. for epd, evd in zip(exp_date_str, eval_date_str):
  43. mat_dates.append(Date.parseISO(epd))
  44. eval_dates.append(Date.parseISO(evd))
  45. spot.append(opt_var_mkt.closePrice[0])
  46. time_to_maturity = [float(mat - eva + 1.0)/365.0 for (mat, eva) in zip(mat_dates, eval_dates)]
  47. risk_free = [] # 无风险利率
  48. for s, mat, time in zip(spot, mat_dates, time_to_maturity):
  49. #rf = math.log(forward_price[mat] / s) / time
  50. rf = shibor[mat]
  51. risk_free.append(rf)
  52. opt_types = [] # 期权类型
  53. for t in option_type:
  54. if t == 'CO':
  55. opt_types.append(1)
  56. else:
  57. opt_types.append(-1)
  58. # 使用通联CAL包中 BSMImpliedVolatity 计算隐含波动率
  59. calculated_vol = BSMImpliedVolatity(opt_types, strike, spot, risk_free, 0.0, time_to_maturity, settle)
  60. calculated_vol = calculated_vol.fillna(0.0)
  61. # 使用通联CAL包中 BSMPrice 计算期权风险指标
  62. greeks = BSMPrice(opt_types, strike, spot, risk_free, 0.0, calculated_vol.vol.values, time_to_maturity)
  63. greeks.vega = greeks.vega #/ 100.0
  64. greeks.rho = greeks.rho #/ 100.0
  65. greeks.theta = greeks.theta #* 365.0 / 252.0 #/ 365.0
  66. opt['strike'] = strike
  67. opt['optType'] = option_type
  68. opt['expDate'] = exp_date_str
  69. opt['spotPrice'] = spot
  70. opt['riskFree'] = risk_free
  71. opt['timeToMaturity'] = np.around(time_to_maturity, decimals=4)
  72. opt['settle'] = np.around(greeks.price.values.astype(np.double), decimals=4)
  73. opt['iv'] = np.around(calculated_vol.vol.values.astype(np.double), decimals=4)
  74. opt['delta'] = np.around(greeks.delta.values.astype(np.double), decimals=4)
  75. opt['vega'] = np.around(greeks.vega.values.astype(np.double), decimals=4)
  76. opt['gamma'] = np.around(greeks.gamma.values.astype(np.double), decimals=4)
  77. opt['theta'] = np.around(greeks.theta.values.astype(np.double), decimals=4)
  78. opt['rho'] = np.around(greeks.rho.values.astype(np.double), decimals=4)
  79. fields = [u'ticker', u'contractType', u'strikePrice', u'expDate', u'tradeDate',
  80. u'closePrice', u'settlPrice', 'spotPrice', u'iv',
  81. u'delta', u'vega', u'gamma', u'theta', u'rho']
  82. opt = opt[fields].reset_index().set_index('ticker').sort_index()
  83. #opt['iv'] = opt.iv.replace(to_replace=0.0, value=np.nan)
  84. return opt

尝试用 getOptDayAnalysis 计算 2015-09-24 这一天的风险指标

  1. # Uqer 计算期权的风险数据
  2. opt_var_sec = u"510050.XSHG" # 期权标的
  3. date = Date(2015, 9, 24)
  4. option_risk = getOptDayAnalysis(opt_var_sec, date)
  5. option_risk.head(2)
optIDcontractTypestrikePriceexpDatetradeDateclosePricesettlPricespotPriceivdeltavegagammathetarho
ticker
510050C1510M0185010000405CO1.852015-10-282015-09-240.32680.35552.1870.43170.91010.10990.5550-0.29920.1568
510050C1510M0190010000406CO1.902015-10-282015-09-240.27910.31022.1870.41610.88100.13470.7058-0.34350.1550

进一步,我们和上交所给出的对应日期的风险指标参考数据对比一下

  • 上交所的数据需要自行下载,注意选择日期下载相应csv文件,http://www.sse.com.cn/assortment/derivatives/options/risk/
  • 下载完后,不做内容改动,请上传到UQER平台的 Data 中;文件名请相应修改,此处我设为了 option_risk_sse_0924.csv
  • 为了避免冗余,下面我们仅仅对比近月期权的各个风险指标
  1. # 读取上交所数据
  2. def readRiskDataSSE(file_str):
  3. # 按照上交所下载到的risk数据排版格式,做以处理
  4. opt = pd.read_csv(file_str, encoding='gb2312').reset_index()
  5. opt.columns = [['tradeDate','optID','ticker','secShortName','delta','theta','gamma','vega','rho','margin']]
  6. opt = opt[['tradeDate','optID','ticker','delta','theta','gamma','vega','rho']]
  7. opt['ticker'] = [tic[1:-2] for tic in opt['ticker']]
  8. opt['tradeDate'] = [td[0:-1] for td in opt['tradeDate']]
  9. #使用DataAPI.OptGet,拿到已退市和上市的所有期权的基本信息
  10. info_fields = [u'optID', u'varSecID', u'varShortName', u'varTicker', u'varExchangeCD', u'varType',
  11. u'contractType', u'strikePrice', u'contMultNum', u'contractStatus', u'listDate',
  12. u'expYear', u'expMonth', u'expDate', u'lastTradeDate', u'exerDate', u'deliDate',
  13. u'delistDate']
  14. opt_info = DataAPI.OptGet(optID='', contractStatus=[u"DE",u"L"], field=info_fields, pandas="1")
  15. # 上交所的数据和期权基本信息合并,得到比较完整的期权数据
  16. opt_info = opt_info.set_index(u"optID")
  17. opt = opt.set_index(u"optID")
  18. opt = concat([opt_info, opt], axis=1, join='inner').sort_index()
  19. fields = [u'ticker', u'contractType', u'strikePrice', u'expDate', u'tradeDate',
  20. u'delta', u'vega', u'gamma', u'theta', u'rho']
  21. opt = opt[fields].reset_index().set_index('ticker').sort_index()
  22. return opt

读取 2015-09-24 上交所数据

  1. option_risk_sse = readRiskDataSSE('option_risk_sse_0924.csv')
  2. option_risk_sse.head(2)
optIDcontractTypestrikePriceexpDatetradeDatedeltavegagammathetarho
ticker
510050C1510M0185010000405CO1.852015-10-282015-09-240.9100.1090.555-0.3030.154
510050C1510M0190010000406CO1.902015-10-282015-09-240.8810.1340.706-0.3490.153

getOptDayAnalysis 函数计算结果和上交所数据的对比

  1. # 对比本文计算结果 option_risk 和上交所结果 option_risk_sse 中的近月期权风险指标
  2. near_exp = np.sort(option_risk.expDate.unique())[0] # 近月期权行权日
  3. opt_call_uqer = option_risk[option_risk.expDate==near_exp][option_risk.contractType=='CO'].set_index('strikePrice')
  4. opt_call_sse = option_risk_sse[option_risk_sse.expDate==near_exp][option_risk_sse.contractType=='CO'].set_index('strikePrice')
  5. opt_put_uqer = option_risk[option_risk.expDate==near_exp][option_risk.contractType=='PO'].set_index('strikePrice')
  6. opt_put_sse = option_risk_sse[option_risk_sse.expDate==near_exp][option_risk_sse.contractType=='PO'].set_index('strikePrice')
  7. ## ----------------------------------------------
  8. ## 风险指标对比
  9. fig = plt.figure(figsize=(10,12))
  10. fig.set_tight_layout(True)
  11. # ------ Delta ------
  12. ax = fig.add_subplot(321)
  13. ax.plot(opt_call_uqer.index, opt_call_uqer['delta'], '-')
  14. ax.plot(opt_call_sse.index, opt_call_sse['delta'], 's')
  15. ax.plot(opt_put_uqer.index, opt_put_uqer['delta'], '-')
  16. ax.plot(opt_put_sse.index, opt_put_sse['delta'], 's')
  17. ax.legend(['call-uqer', 'call-sse', 'put-uqer', 'put-sse'])
  18. ax.grid()
  19. ax.set_xlabel(u"strikePrice")
  20. ax.set_ylabel(r"Delta")
  21. plt.title('Delta Comparison')
  22. # ------ Theta ------
  23. ax = fig.add_subplot(322)
  24. ax.plot(opt_call_uqer.index, opt_call_uqer['theta'], '-')
  25. ax.plot(opt_call_sse.index, opt_call_sse['theta'], 's')
  26. ax.plot(opt_put_uqer.index, opt_put_uqer['theta'], '-')
  27. ax.plot(opt_put_sse.index, opt_put_sse['theta'], 's')
  28. ax.legend(['call-uqer', 'call-sse', 'put-uqer', 'put-sse'])
  29. ax.grid()
  30. ax.set_xlabel(u"strikePrice")
  31. ax.set_ylabel(r"Theta")
  32. plt.title('Theta Comparison')
  33. # ------ Gamma ------
  34. ax = fig.add_subplot(323)
  35. ax.plot(opt_call_uqer.index, opt_call_uqer['gamma'], '-')
  36. ax.plot(opt_call_sse.index, opt_call_sse['gamma'], 's')
  37. ax.plot(opt_put_uqer.index, opt_put_uqer['gamma'], '-')
  38. ax.plot(opt_put_sse.index, opt_put_sse['gamma'], 's')
  39. ax.legend(['call-uqer', 'call-sse', 'put-uqer', 'put-sse'], loc=0)
  40. ax.grid()
  41. ax.set_xlabel(u"strikePrice")
  42. ax.set_ylabel(r"Gamma")
  43. plt.title('Gamma Comparison')
  44. # # ------ Vega ------
  45. ax = fig.add_subplot(324)
  46. ax.plot(opt_call_uqer.index, opt_call_uqer['vega'], '-')
  47. ax.plot(opt_call_sse.index, opt_call_sse['vega'], 's')
  48. ax.plot(opt_put_uqer.index, opt_put_uqer['vega'], '-')
  49. ax.plot(opt_put_sse.index, opt_put_sse['vega'], 's')
  50. ax.legend(['call-uqer', 'call-sse', 'put-uqer', 'put-sse'], loc=4)
  51. ax.grid()
  52. ax.set_xlabel(u"strikePrice")
  53. ax.set_ylabel(r"Vega")
  54. plt.title('Vega Comparison')
  55. # ------ Rho ------
  56. ax = fig.add_subplot(325)
  57. ax.plot(opt_call_uqer.index, opt_call_uqer['rho'], '-')
  58. ax.plot(opt_call_sse.index, opt_call_sse['rho'], 's')
  59. ax.plot(opt_put_uqer.index, opt_put_uqer['rho'], '-')
  60. ax.plot(opt_put_sse.index, opt_put_sse['rho'], 's')
  61. ax.legend(['call-uqer', 'call-sse', 'put-uqer', 'put-sse'], loc=3)
  62. ax.grid()
  63. ax.set_xlabel(u"strikePrice")
  64. ax.set_ylabel(r"Rho")
  65. plt.title('Rho Comparison')
  66. <matplotlib.text.Text at 0x535d0d0>

【50ETF期权】 4. Greeks 和隐含波动率微笑 - 图1

上述五张图中,对于近月期权,我们分别对比了五个Greeks风险指标:DeltaThetaGammaVegaRho

  • 每张图中,CallPut 分开比较,横轴为行权价
  • 可以看出,本文中的计算结果和上交所的参考数值符合的比较好
  • 在接下来的50ETF期权分析中,我们将使用本文中的计算方法来计算期权隐含波动率和Greeks风险指标 把上面的数据整理整理,格式更简洁一点
  1. # 每日期权分析数据整理
  2. def getOptDayGreeksIV(date):
  3. # Uqer 计算期权的风险数据
  4. opt_var_sec = u"510050.XSHG" # 期权标的
  5. opt = getOptDayAnalysis(opt_var_sec, date)
  6. # 整理数据部分
  7. opt.index = [index[-10:] for index in opt.index]
  8. opt = opt[['contractType','strikePrice','expDate','closePrice','iv','delta','theta','gamma','vega','rho']]
  9. opt_call = opt[opt.contractType=='CO']
  10. opt_put = opt[opt.contractType=='PO']
  11. opt_call.columns = pd.MultiIndex.from_tuples([('Call', c) for c in opt_call.columns])
  12. opt_call[('Call-Put', 'strikePrice')] = opt_call[('Call', 'strikePrice')]
  13. opt_put.columns = pd.MultiIndex.from_tuples([('Put', c) for c in opt_put.columns])
  14. opt = concat([opt_call, opt_put], axis=1, join='inner').sort_index()
  15. opt = opt.set_index(('Call','expDate')).sort_index()
  16. opt = opt.drop([('Call','contractType'), ('Call','strikePrice')], axis=1)
  17. opt = opt.drop([('Put','expDate'), ('Put','contractType'), ('Put','strikePrice')], axis=1)
  18. opt.index.name = 'expDate'
  19. ## 以上得到完整的历史某日数据,格式简洁明了
  20. return opt
  1. date = Date(2015, 9, 24)
  2. option_risk = getOptDayGreeksIV(date)
  3. option_risk.head(10)
CallCall-PutPut
closePriceivdeltathetagammavegarhostrikePriceclosePriceivdeltathetagammavegarho
expDate
2015-10-280.32680.43170.9101-0.29920.55500.10990.15681.850.01290.4319-0.0900-0.24100.55510.1100-0.0201
2015-10-280.27910.41610.8810-0.34350.70580.13470.15501.900.01760.4174-0.1197-0.28540.70630.1352-0.0268
2015-10-280.23600.39900.8449-0.38620.88230.16150.15171.950.02320.3992-0.1552-0.32470.88220.1615-0.0348
2015-10-280.19550.18110.9532-0.12250.79800.06630.18112.000.03450.4020-0.2105-0.39401.06010.1954-0.0474
2015-10-280.15990.24530.8237-0.27641.55880.17540.15742.050.04740.3975-0.2703-0.44411.22900.2241-0.0612
2015-10-280.12750.26980.7137-0.36961.86250.23040.13742.100.06430.3952-0.3381-0.48471.36600.2476-0.0771
2015-10-280.09900.28140.6081-0.42082.01620.26020.11802.150.08690.4013-0.4114-0.52001.43170.2635-0.0946
2015-10-280.07680.29550.5057-0.44891.99340.27010.09872.200.11460.4121-0.4836-0.54281.42840.2699-0.1124
2015-10-280.05840.30680.4132-0.44871.87460.26370.08102.250.14500.4200-0.5517-0.54381.39080.2679-0.1296
2015-10-280.04700.32640.3381-0.44341.65380.24760.06642.300.18260.4426-0.6091-0.55201.28090.2600-0.1452

2. 隐含波动率微笑

利用上一小节的代码,给出隐含波动率微笑结构

隐含波动率微笑

  1. # 做图展示某一天的隐含波动率微笑
  2. def plotSmileVolatility(date):
  3. # Uqer 计算期权的风险数据
  4. opt = getOptDayGreeksIV(date)
  5. # 下面展示波动率微笑
  6. exp_dates = np.sort(opt.index.unique())
  7. ## ----------------------------------------------
  8. fig = plt.figure(figsize=(10,8))
  9. fig.set_tight_layout(True)
  10. for i in range(exp_dates.shape[0]):
  11. date = exp_dates[i]
  12. ax = fig.add_subplot(2,2,i+1)
  13. opt_date = opt[opt.index==date].set_index(('Call-Put', 'strikePrice'))
  14. opt_date.index.name = 'strikePrice'
  15. ax.plot(opt_date.index, opt_date[('Call', 'iv')], '-o')
  16. ax.plot(opt_date.index, opt_date[('Put', 'iv')], '-s')
  17. ax.legend(['call', 'put'], loc=0)
  18. ax.grid()
  19. ax.set_xlabel(u"strikePrice")
  20. ax.set_ylabel(r"Implied Volatility")
  21. plt.title(exp_dates[i])
  1. plotSmileVolatility(Date(2015,9,24))

【50ETF期权】 4. Greeks 和隐含波动率微笑 - 图2

行权价和行权日期两个方向上的隐含波动率微笑

  1. from mpl_toolkits.mplot3d import Axes3D
  2. from matplotlib import cm
  3. # 做图展示某一天的隐含波动率结构
  4. def plotSmileVolatilitySurface(date):
  5. # Uqer 计算期权的风险数据
  6. opt = getOptDayGreeksIV(date)
  7. # 下面展示波动率结构
  8. exp_dates = np.sort(opt.index.unique())
  9. strikes = np.sort(opt[('Call-Put', 'strikePrice')].unique())
  10. risk_mt = {'Call': pd.DataFrame(index=strikes),
  11. 'Put': pd.DataFrame(index=strikes) }
  12. # 将数据整理成Call和Put分开来,分别的结构为:
  13. # 行为行权价,列为剩余到期天数(以自然天数计算)
  14. for epd in exp_dates:
  15. exp_days = Date.parseISO(epd) - date
  16. opt_date = opt[opt.index==epd].set_index(('Call-Put', 'strikePrice'))
  17. opt_date.index.name = 'strikePrice'
  18. for cp in risk_mt.keys():
  19. risk_mt[cp][exp_days] = opt_date[(cp, 'iv')]
  20. for cp in risk_mt.keys():
  21. for strike in risk_mt[cp].index:
  22. if np.sum(np.isnan(risk_mt[cp].ix[strike])) > 0:
  23. risk_mt[cp] = risk_mt[cp].drop(strike)
  24. # Call和Put分开显示,行index为行权价,列index为剩余到期天数
  25. #print risk_mt
  26. # 画图
  27. for cp in ['Call', 'Put']:
  28. opt = risk_mt[cp]
  29. x = []
  30. y = []
  31. z = []
  32. for xx in opt.index:
  33. for yy in opt.columns:
  34. x.append(xx)
  35. y.append(yy)
  36. z.append(opt[yy][xx])
  37. fig = plt.figure(figsize=(10,8))
  38. fig.suptitle(cp)
  39. ax = fig.gca(projection='3d')
  40. ax.plot_trisurf(x, y, z, cmap=cm.jet, linewidth=0.2)
  41. return risk_mt

画出某一天的波动率微笑曲面结构

  1. opt = plotSmileVolatilitySurface(Date(2015,9,24))
  2. opt # Call和Put分开显示,行index为行权价,列index为剩余到期天数
  3. {'Call': 34 62 90 181
  4. 2.10 0.2698 0.2817 0.2823 0.3042
  5. 2.15 0.2814 0.2888 0.2916 0.3063
  6. 2.20 0.2955 0.3008 0.2922 0.3237
  7. 2.25 0.3068 0.3067 0.3093 0.3157
  8. 2.30 0.3264 0.3155 0.3128 0.3172,
  9. 'Put': 34 62 90 181
  10. 2.10 0.3952 0.4403 0.4740 0.4449
  11. 2.15 0.4013 0.4442 0.4794 0.4632
  12. 2.20 0.4121 0.4498 0.4802 0.4451
  13. 2.25 0.4200 0.4581 0.4863 0.4547
  14. 2.30 0.4426 0.4673 0.4893 0.4691}

【50ETF期权】 4. Greeks 和隐含波动率微笑 - 图3

【50ETF期权】 4. Greeks 和隐含波动率微笑 - 图4

波动率曲面结构图中:

  • 上图为Call,下图为Put,此处没有进行任何插值处理,所以略显粗糙
  • Put的隐含波动率明显大于Call
  • 期限结构来说,波动率呈现远高近低的特征