編程金融小白學 股票期權 lv.4 隱含波動率

上一篇 PCP策略

編程金融小白學 股票期權

隱含波動率

波動率 σ\sigma

  • 波動率 σ\sigma

    • 波動率爲期權價格影響的一個重要因素
    • 沒有波動,期權就沒有存在的價值
    • 不可觀測變量
  • 在統計中的對應概念:價格(對數)收益率的年化標準差

NN 天數
RnR_n 第n天的收益率
Rˉ\bar{R} 平均收益率
242242 一年中國交易天數

242×1Nn=1N(RnRˉ)2\sqrt{242\times\frac{1}{N}\sum_{n=1}^N(R_n-\bar{R})^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=SN(d1)Xer(Tt)N(d2)p=Xer(Tt)N(d2)SN(d1)d1=ln(S/X)+(r+σ2/2)(Tt)σTtd2=ln(S/X)+(rσ2/2)(Tt)σTt=d1σTt\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}

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_code name exercise_type list_date delist_date
0 10000579.SH 華夏上證50ETF期權1604認購2.15 歐式 20160225 20160427
1 10000108.SH 華夏上證50ETF期權1505認購2.65 歐式 20150326 20150527
2 10000111.SH 華夏上證50ETF期權1505認沽2.55 歐式 20150326 20150527
3 10001067.SH 華夏上證50ETF期權1712認購3.24 歐式 20171123 20171227
4 10001068.SH 華夏上證50ETF期權1712認沽3.24 歐式 20171123 20171227
  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_code exercise_type list_date delist_date new_name delist type callput price
0 10000579.SH 歐式 20160225 20160427 華夏上證 1604 50 Call 2.15
1 10000108.SH 歐式 20150326 20150527 華夏上證 1505 50 Call 2.65
2 10000111.SH 歐式 20150326 20150527 華夏上證 1505 50 Put 2.55
3 10001067.SH 歐式 20171123 20171227 華夏上證 1712 50 Call 3.24
4 10001068.SH 歐式 20171123 20171227 華夏上證 1712 50 Put 3.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_code exercise_type list_date delist_date new_name delist type callput price trade_date ... pre_settle pre_close open high low close settle vol amount oi
20 10002421.SH 歐式 20200320 20200624 華夏上證 2006 50 Call 2.35 20200429 ... 0.46 0.4327 0.4300 0.4542 0.4300 0.4498 0.479 0.0221 985895.0 2353.0
46 10002401.SH 歐式 20200319 20200624 華夏上證 2006 50 Call 2.40 20200429 ... 0.41 0.3842 0.3816 0.4050 0.3808 0.3950 0.429 0.0043 170508.0 2079.0
47 10002402.SH 歐式 20200319 20200624 華夏上證 2006 50 Call 2.45 20200429 ... 0.36 0.3373 0.3362 0.3576 0.3362 0.3549 0.379 0.0104 364896.0 1075.0
96 10002291.SH 歐式 20200204 20200624 華夏上證 2006 50 Call 2.50 20200429 ... 0.31 0.2909 0.2889 0.3130 0.2864 0.3069 0.329 0.0162 493784.0 2725.0
97 10002292.SH 歐式 20200204 20200624 華夏上證 2006 50 Call 2.55 20200429 ... 0.26 0.2458 0.2430 0.2673 0.2430 0.2620 0.279 0.0127 328143.0 2058.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_code exchange name per_unit opt_code opt_type call_put exercise_type exercise_price s_month maturity_date list_price list_date delist_date last_edate last_ddate quote_unit min_price_chg type
0 10000579.SH SSE 華夏上證50ETF期權1604認購2.15 10000.0 OP510050.SH ETF期權 C 歐式 2.15 201604 20160427 0.0412 20160225 20160427 20160427 20160428 人民幣元 0.0001 50
1 10000108.SH SSE 華夏上證50ETF期權1505認購2.65 10000.0 OP510050.SH ETF期權 C 歐式 2.65 201505 20150527 0.1006 20150326 20150527 20150527 20150528 人民幣元 0.0001 50
# 找到 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

  • 看跌期權波動率 普遍比 看漲波動率高,說明投資者比較偏愛於購買看跌期權。

下篇 希臘字母概要

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章