编程金融小白学 股票期权 lv.4 隐含波动率

上一篇 PCP策略

编程金融小白学 股票期权

隐含波动率

波动率 σ \sigma σ

  • 波动率 σ \sigma σ

    • 波动率为期权价格影响的一个重要因素
    • 没有波动,期权就没有存在的价值
    • 不可观测变量
  • 在统计中的对应概念:价格(对数)收益率的年化标准差

N N N 天数
R n R_n Rn 第n天的收益率
R ˉ \bar{R} Rˉ 平均收益率
242 242 242 一年中国交易天数

242 × 1 N ∑ n = 1 N ( R n − R ˉ ) 2 \sqrt{242\times\frac{1}{N}\sum_{n=1}^N(R_n-\bar{R})^2} 242×N1n=1N(RnRˉ)2

波动率分类

  • 历史波动率 Historical volatility & 未来波动率 Future Volatility

  • 历史波动率法:

    • 基于标的资产已发生的历史价格数据估计波动率:
      • 标准差
      • 极差波动率
      • 已实现波动率 Realized Volatility (RV)
    • 根据历史波动率预测未来的波动率:
      • 预测值 = 历史值
      • GARCH, EWMA
      • HAR-RV
  • 隐含波动率法:

    • 从期权价格倒推市场预测未来波动率
      • Black-Scholes 隐含波动率
      • 无模型隐含波动率 (如 VIX)

隐含波动率

import numpy as np
from scipy.stats import norm

class BlackScholes:
    def __init__(self, S0, X, r, T, sigma=0.3,t=0):
        self.S0 = S0
        self.X = X
        self.r = r
        self.sigma = sigma
        self.dT = T-t
    
    def d1(self):
        return(np.log(self.S0/self.X)+(self.r+self.sigma**2/2)*(self.dT))/(self.sigma*np.sqrt(self.dT))

    def d2(self):
        return self.d1()-self.sigma*np.sqrt(self.dT)
    
    def calc(self, call_put):
        if call_put in {'c','C','call','Call','CALL'}:
            return self.S0 * norm.cdf(self.d1())- \
                    self.X*np.exp(-self.r*self.dT)*norm.cdf(self.d2())
        elif call_put in {'p','P','put','Put','PUT'}:
            return self.X*np.exp(-self.r*self.dT)*norm.cdf(-self.d2())- \
                    self.S0 * norm.cdf(-self.d1())
        raise NameError('Must be call or Put!',call_put)
        
    def imp_vol(self,call_put,mktprice):
        price = 0
        sigma = 0.3
        up, low = 1,0
        loop = 0
        while abs(price-mktprice)>1e-6 and loop<50:
            price = BlackScholes(self.S0,self.X,self.r,self.dT,sigma).calc(call_put)
            if (price-mktprice)>0:
                up = sigma
                sigma = (sigma+low)/2
            else:
                low = sigma
                sigma = (sigma+up)/2
            loop+=1
        return sigma

c = S N ( d 1 ) − X e − r ( T − t ) N ( d 2 ) p = X e − r ( T − t ) N ( − d 2 ) − S N ( − d 1 ) d 1 = ln ⁡ ( S / X ) + ( r + σ 2 / 2 ) ( T − t ) σ T − t d 2 = ln ⁡ ( S / X ) + ( r − σ 2 / 2 ) ( T − t ) σ T − t = d 1 − σ T − t \begin{aligned}c &= SN(d_1)-Xe^{-r(T-t)}N(d_2)\\ p &= Xe^{-r(T-t)}N(-d_2)-SN(-d_1)\\ d_1 &= \frac{\ln(S/X)+(r+\sigma^2/2)(T-t)}{\sigma \sqrt{T-t}} \\ d_2 &= \frac{\ln(S/X)+(r-\sigma^2/2)(T-t)}{\sigma \sqrt{T-t}}\\ &= d_1 -\sigma\sqrt{T-t}\end{aligned} cpd1d2=SN(d1)Xer(Tt)N(d2)=Xer(Tt)N(d2)SN(d1)=σTt ln(S/X)+(r+σ2/2)(Tt)=σTt ln(S/X)+(rσ2/2)(Tt)=d1σTt

c 看涨期权价格
p 看跌期权价格
S 标的资产现价
X 行权价
r 无风险利率
T 到期时刻
t 当前时刻
𝜎 波动率
N()为标准正态分布累积分布函数

  • 隐含波动率偏高 → \to 期权价格偏高
  • 隐含波动率偏低 → \to 期权价格偏低

实例

  • 来看一下 具体实例:
  1. 导入 需要的库(还是实用 tushare 的数据)
import pandas as pd
import tushare as tus
import matplotlib.pyplot as plt # 画图
plt.rcParams['font.sans-serif'] = ['FangSong'] # 设置中文
plt.rcParams['axes.unicode_minus'] = False # 设置中文负号

pro = tus.pro_api()
print(tus.__version__)
1.2.54
  1. 以上证 50 ETF 为例
    • 获取上证 50 ETF 数据
opt_name = pro.opt_basic(exchange='SSE', fields='ts_code,name,exercise_type,list_date,delist_date')
opt_name.head()
ts_codenameexercise_typelist_datedelist_date
010000579.SH华夏上证50ETF期权1604认购2.15欧式2016022520160427
110000108.SH华夏上证50ETF期权1505认购2.65欧式2015032620150527
210000111.SH华夏上证50ETF期权1505认沽2.55欧式2015032620150527
310001067.SH华夏上证50ETF期权1712认购3.24欧式2017112320171227
410001068.SH华夏上证50ETF期权1712认沽3.24欧式2017112320171227
  1. 提取 需要的期权名 到期日期 价格 认购标签等
    • 当然 Tushare 本身带有 这一系列简单的提取方式,在上式 修改 fields 所需参数 即可。
# 把 name 里的数据提取出来
opt_name['new_name']= opt_name['name'].str.extract(r'([\u4e00-\u9fa5]+)') # 提取期权名
opt_name['delist'] = opt_name['name'].str.extract(r'(期权)(\d+)')[1].astype(int) # 期权到期日期
opt_name['type']= opt_name['name'].str.extract(r'(\d+)') # 提取期权类型 300 或 50
opt_name['callput']= opt_name['name'].str.extract(r'(\w\w)(\d+\.\d+)')[0]# 认购或认沽
opt_name['price'] = opt_name['name'].str.extract(r'(\d+\.\d+)').astype(float) # 价格
opt_name.drop(columns=['name'],inplace=True)
opt_name['callput'].replace({'认购':'Call', '认沽':'Put'},inplace=True)
opt_name.head()
ts_codeexercise_typelist_datedelist_datenew_namedelisttypecallputprice
010000579.SH欧式2016022520160427华夏上证160450Call2.15
110000108.SH欧式2015032620150527华夏上证150550Call2.65
210000111.SH欧式2015032620150527华夏上证150550Put2.55
310001067.SH欧式2017112320171227华夏上证171250Call3.24
410001068.SH欧式2017112320171227华夏上证171250Put3.24
  1. 以 昨天 2020年 4 月 29 日数据为例
    • 提取 2020年 4 月 29 日期权交易数据
# 找到 4月 29日的期权交易数据
DATE = '20200429'
opt_date = pro.opt_daily(trade_date=DATE,exchange='SSE')
  1. 合并交易名与交易具体数据
new_date = pd.merge(opt_name,opt_date,on=['ts_code']) # 正在交易的名字和4月29日的数据交集
  1. 提取 上证2020年06月到期的 50ETF 认购 4月29日 数据
    • 并按行权价 排序
call_date_2006 = new_date.query('delist == 2006 and type == "50" and callput == "Call"').sort_values(by='price')
call_date_2006.head(5)
ts_codeexercise_typelist_datedelist_datenew_namedelisttypecallputpricetrade_date...pre_settlepre_closeopenhighlowclosesettlevolamountoi
2010002421.SH欧式2020032020200624华夏上证200650Call2.3520200429...0.460.43270.43000.45420.43000.44980.4790.0221985895.02353.0
4610002401.SH欧式2020031920200624华夏上证200650Call2.4020200429...0.410.38420.38160.40500.38080.39500.4290.0043170508.02079.0
4710002402.SH欧式2020031920200624华夏上证200650Call2.4520200429...0.360.33730.33620.35760.33620.35490.3790.0104364896.01075.0
9610002291.SH欧式2020020420200624华夏上证200650Call2.5020200429...0.310.29090.28890.31300.28640.30690.3290.0162493784.02725.0
9710002292.SH欧式2020020420200624华夏上证200650Call2.5520200429...0.260.24580.24300.26730.24300.26200.2790.0127328143.02058.0

5 rows × 21 columns

  1. 为了解释 标的资产现价 与 行权价的关系,绘制图表
    • 可见 越接近 标的资产现价 它的时间价值或者说 期权价 越高
current = 2.829 # 当天收盘价为 2.829元
plt.figure(figsize=(8,5), dpi=800)
plt.plot(call_date_2006['price'],current-call_date_2006['price'],color='red',label='股票收益')
plt.stem(call_date_2006['price'],call_date_2006['settle'],use_line_collection=True)
plt.ylabel('结算价 & 股票收益')
plt.xlabel('行权价格')
plt.ylim((-.1,.5))
plt.title('4月29日 华夏上证50ETF期权2006 认购')
plt.show()

16

  1. 具体行权价与期权价到期收益情况绘图
    • 更改一下之前作简要介绍的期权类,就变如下:
class OptionPlot:
    '''
    输入行权价 与 到期价格区间 绘制 期权收益图像
    
    ST: 到期价区间
    opt: 期权价
    X: 行权价
    '''
    def __init__(self, X, ST, buy=True):
        self.ST = ST
        self.X = X
        self.buy = 1 if buy else -1
    
    def call_opt(self, opt:float=0):
        return self.buy*((self.ST-self.X)*(self.ST>=self.X)-opt)
    
    def put_opt(self, opt:float=0):
        return self.buy*((self.X-self.ST)*(self.ST<=self.X)-opt)
plt.figure(figsize=(8,5), dpi=800)
plt.axhline(y=0,ls="-",c="black")
plt.axvline(x=current,ls="-",c="black")
ST_period = np.linspace(2.35,3.5,101)
plt.plot(ST_period,ST_period-current,color='red',linewidth = '3',label='股票收益')
for i in call_date_2006['settle'].index[6:18]:
    option_price = call_date_2006['settle'][i] # 期权价
    price = call_date_2006['price'][i] # 行权价
    if not price*100%5:
        buy = OptionPlot(price,ST_period)
        plt.plot(ST_period,buy.call_opt(option_price),label=f'行权价({price})')
plt.ylabel('收益')
plt.xlabel('到期价格')
plt.xlim((2.6,3.0))
plt.ylim((-.2,.2))
plt.title('4月29日 华夏上证50ETF期权2006 认购')
plt.legend(loc='upper left')
plt.grid(True)
plt.show()

19

* 从之前所学,可知 虚值期权 它的风险是相当高,同时当你买入实值越大的期权时,随着期权费越高,它和普通股票价格直线越接近。
  1. 接下来 我们 来看看之前构造的 BlackScholes 定价系统
    • 输入当天价格 current、行权价 call_date_2006['price']、无风险利率 5%、到期时间 40/250=0.16年、默认波动率 30%
    • 对比 BS 期权价 与 实际 期权价
BS = [BlackScholes(current, x, 0.05, 0.16) for x in call_date_2006['price']]
BS_price = np.array([bs.calc('c') for bs in BS])
plt.figure(figsize=(8,5), dpi=800)
plt.stem(call_date_2006['price'],call_date_2006['settle'],'b',use_line_collection=True,label='市场 期权价')
plt.plot(call_date_2006['price'],BS_price,'r',label='BS计算 期权价')
plt.xlabel('认购期权价格')
plt.title('4月29日 华夏上证50ETF期权2006 BS波动率30% 认购期权价格')
plt.legend(loc='upper right')
plt.grid(True)
plt.show()

21

marketprice = list(call_date_2006['settle']) # 市场期权价
imp_vol = np.array([bs.imp_vol('c',marketprice[i]) for i,bs in enumerate(BS)])
plt.figure(figsize=(8,5), dpi=800)
plt.plot(call_date_2006['price'],imp_vol)
plt.ylabel('隐含波动率')
plt.ylim((-.1,1.1))
plt.yticks(np.arange(0,1.1,0.1), [f'{int(y*100)}%' for y in np.arange(0,1.1,0.1)])
plt.xlabel('行权价格')
plt.title('4月29日 华夏上证50ETF2006认购期权 隐含波动率')
plt.grid(True)
plt.show()

22

绘制期权隐含波动率

  • 结合上面的分步绘图 我们可以绘制出 并对比 当前所有可购买 50EFT 期权的隐含波动率
opt_name = pro.opt_basic(exchange='SSE')
opt_name['type']= opt_name['name'].str.extract(r'(\d+)') # 提取期权类型 300 或 50

opt_name.head(2) # 预览
ts_codeexchangenameper_unitopt_codeopt_typecall_putexercise_typeexercise_prices_monthmaturity_datelist_pricelist_datedelist_datelast_edatelast_ddatequote_unitmin_price_chgtype
010000579.SHSSE华夏上证50ETF期权1604认购2.1510000.0OP510050.SHETF期权C欧式2.15201604201604270.041220160225201604272016042720160428人民币元0.000150
110000108.SHSSE华夏上证50ETF期权1505认购2.6510000.0OP510050.SHETF期权C欧式2.65201505201505270.100620150326201505272015052720150528人民币元0.000150
# 找到 4月 29日的期权交易数据
DATE = '20200429'
opt_date = pro.opt_daily(trade_date=DATE,exchange='SSE')

new_date = pd.merge(opt_name,opt_date,on=['ts_code'])
current = 2.829
plt.figure(figsize=(8,5), dpi=800)
for s_month in new_date.s_month.unique():
    call_date = new_date[new_date['s_month'] == s_month].query(f'type == "50" and call_put == "C"').sort_values(by='exercise_price')
    ex_price = call_date['exercise_price'] # 行权价
    opt_price = call_date['settle'] # 期权价
    comb_BS = [(BlackScholes(current, ex_price[i], 0.05, 0.16), opt_price[i])for i in ex_price.index]
    imp_vol = np.array([bs.imp_vol('c',marketprice) for bs,marketprice in comb_BS])
    plt.plot(ex_price,imp_vol,label=f'认购期权{s_month[2:]}')
for s_month in new_date.s_month.unique():
    put_date = new_date[new_date['s_month'] == s_month].query(f'type == "50" and call_put == "P"').sort_values(by='exercise_price')
    ex_price = put_date['exercise_price'] # 行权价
    opt_price = put_date['settle'] # 期权价
    comb_BS = [(BlackScholes(current, ex_price[i], 0.05, 0.16), opt_price[i])for i in ex_price.index]
    imp_vol = np.array([bs.imp_vol('p',marketprice) for bs,marketprice in comb_BS])
    plt.plot(ex_price,imp_vol,'--',label=f'认沽期权{s_month[2:]}')
    
plt.ylabel('隐含波动率')
plt.ylim((-.1,1.1))
plt.yticks(np.arange(0,1.1,0.1), [f'{int(y*100)}%' for y in np.arange(0,1.1,0.1)])
plt.xlabel('行权价格')
plt.title(f'{DATE[4:6]}月{DATE[6:]}日 华夏上证50ETF认购期权 隐含波动率')
plt.grid(True)
plt.legend(loc='upper right')
plt.show()

26

  • 看跌期权波动率 普遍比 看涨波动率高,说明投资者比较偏爱于购买看跌期权。

下篇 希腊字母概要

  • 3
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值