BSM的兩個基本問題與python實現(歐式期權定價公式)

在我們的定義中,定量分析是數學或統計學方法在市場數據上的應用。 ——John Forman

BSM定價模型的兩個基本問題:

  1. 隱含波動率
    以某些到期日的期權報價倒推出這些期權的隱含波動率,並匯出圖表——這是期權交易者和風險管理者每天都要面對的任務。
  2. 蒙特卡洛模擬
    歐式期權價值的計算。通過蒙特卡羅技術,模擬股票在一段時間中變化。
    像Black-Scholes-Merton(1973)這樣有深遠影響的期權定價公式中,隱含波動率是在其他條件不變的情況下輸入公式,得出不同期權行權價格和到期日測得市場報價的那些波動率值。

BSM公式(1-1)

C(St,K,t,T,r)=StN(d1)er(Tt)KN(d2)C(S_t,K,t,T,r)=S_t\cdot N(d_1)-e^{-r(T-t)} \cdot K\cdot N(d_2)
N(d)=12πde1/2x2dxN(d)=\frac{1}{\sqrt{2\pi}}\int_{-\infty}^{d}{e^{-1/2}x^2}dx
d1=log(sT/K)+(r+σ2/2)(Tt)σTtd_1=\frac{log(s_T/K)+(r+\sigma^2/2)(T-t)}{\sigma\sqrt{T-t}}
d2=log(sT/K)+(rσ2/2)(Tt)σTtd_2=\frac{log(s_T/K)+(r-\sigma^2/2)(T-t)}{\sigma\sqrt{T-t}}

不同參數有如下含義:
StS_t 在時點t的標的物價格水平;
σ\sigma 標的物固定波動率(也就是收益的標準差);
KK 期權行權價格;
TT 期權到期日;
rr 固定無風險短期利率;
現在考慮歐式看漲期權的一個報價CC^*已知的情況。隱含波動率σimp\sigma^{imp}是公式(1-1)中的隱式方程的解。
公式(1-2) 方程式數值化求根的牛頓迭代法
σn+1imp=σnimpC(σnimp)CC(σnimp)/σnimp\sigma_{n+1}^{imp}=\sigma_n^{imp}-\frac{C(\sigma_n^{imp})-C^*}{\partial C(\sigma_n^{imp})/\partial \sigma_n^{imp}}

期權定價公式對於波動率的偏微分稱作Vega,公式1-3給出了Vega的閉合方式。
公式1-3 BSM模型中歐式期權的Vega
Cσ=StN(d1)Tt\frac{\partial C}{\partial \sigma}=S_tN(d_1)\sqrt{T-t}

1. Black-Scholes-Merton python計算公式


import numpy as np
from math import sqrt, log
from scipy import stats
#
# 歐式期權BSM定價公式
def bsm_call_value(S0, K, T, r, sigma):
    """
    Parameters:
    ==========
    S0: float
        標的物初始價格水平
    K: float
       行權價格
    T: float
       到期日
    r: float
       固定無風險短期利率
    sigma: float
       波動因子
    Returns
    ==========
    value: float
    """
    S0 = float(S0)
    d1 = (np.log(S0 /K) + (r + 0.5 * sigma**2) * T )/(sigma * np.sqrt(T))
    d2 = (np.log(S0 /K) + (r - 0.5 * sigma**2) * T )/(sigma * np.sqrt(T))
    value = (S0 * stats.norm.cdf(d1, 0, 1) - K * np.exp(-r * T) * stats.norm.cdf(d2, 0, 1))
    return value
    
def bsm_vega(S0, K, T, r, sigma):
    """
    Vega 計算
    """
    S0 = float(S0)
    d1 = (np.log(S0/K)) + (r+0.5*sigma**2)*T /(sigma*sqrt(T))
    vega = S0 * stats.norm.cdf(d1, 0, 1) * np.sqrt(T)
    return vega

def bsm_call_imp_vol(S0, K, T, r, C0, sigma_est, it=100):
    for i in range(it):
        sigma_est -= ((bsm_call_value(S0, K, T, r, sigma_est) - C0)
                     / bsm_vega(S0, K, T, r, sigma_est))
    return sigma_est

S0 = 1
K = 2
T = 2
r = 0.01
sigma = 0.1
C0 = 1
bsm_call_imp_vol(S0, K, T, r, C0, sigma, it=2000)
import pandas as pd
h5 = pd.HDFStore('./vstoxx_data_31032014.h5','r')
futures_data = h5['futures_data']
options_data = h5['options_data']
futures_data['DATE'] = pd.to_datetime(futures_data['DATE'])
options_data['DATE'] = pd.to_datetime(options_data['DATE'])
futures_data['MATURITY'] = pd.to_datetime(futures_data['MATURITY'])
options_data['MATURITY'] = pd.to_datetime(options_data['MATURITY'])
h5.close()
V0 = 17.6639
r = 0.01
# imp_vol  -> implied volality
options_data['IMP_VOL'] = 0.0
tol = 0.5 #tolerance
for option in options_data.index:
    item = options_data.loc[option]
    forward = futures_data[futures_data['MATURITY']== \
                           item['MATURITY']]['PRICE'].values[0]
    if (forward * (1 - tol) < item['STRIKE'] 
        < forward*(1 + tol)):
        imp_vol = bsm_call_imp_vol(V0,
                                  item['STRIKE'],
                                  item['TTM'],
                                  r,
                                  item['PRICE'],
                                  sigma_est=2.,
                                  it=100)
        options_data['IMP_VOL'].loc[option] = imp_vol
plot_data = options_data[options_data['IMP_VOL']>0]
maturies = sorted(set(options_data['MATURITY']))
import matplotlib.pyplot as plt
plt.figure(figsize=(8, 6))
for maturity in maturies:
    data = plot_data[options_data.MATURITY==maturity]
    plt.plot(data['STRIKE'], data['IMP_VOL'], label=maturity.date(), lw=1.5
            )
    plt.plot(data['STRIKE'], data['IMP_VOL'], 'r.')
plt.grid(True)
plt.xlabel('strike')
plt.ylabel('implied volatility of volatility')
plt.legend()
plt.show()

在這裏插入圖片描述
在股票或外匯市場中,你將注意到所謂的隱含波動率微笑,而且到期日越短,隱含波動率微笑越明顯;到期日越長,越不明顯。

2. 期權定價的蒙特卡羅模擬

蒙特卡羅是金融學和數值科學中最重要的算法之一。它之所以重要,是因爲在期權定價或者風險管理問題上有很強的能力。
和其他數值方法相比,蒙特卡羅方法很容易處理高維問題,在這種問題上複雜度和計算需求通常以線性方式增大。一下例子闡述了python的基於蒙特卡羅模擬的歐式期權估值方法。
公式 2-1 Black-Scholes-Merton隨機微分方程
dSt=rStdt+σStdZtdS_t=rS_tdt+\sigma S_tdZ_t
Z是一個布朗運動。

公式 2-2 SDE的歐拉離散
St=StΔt expr12σ2Δt+σΔtztS_t=S_{t-\Delta_t}\ exp\lgroup \lgroup r -\frac{1}{2}\sigma^2\rgroup \Delta t+\sigma \sqrt{\Delta t}z_t\rgroup
變量Z是標準正態分佈隨機變量,0&lt;Δt&lt;T0&lt;\Delta_t&lt;T,是一個足夠小的時間間隔。
以S0=100、K=105、T=1.0、r=0.05、σ\sigma=0.2參數化上述模型,利用前面例子中的計算公式,可以得到精確的期權價值:

S0 = 100
K = 105
T = 1.0
r = 0.05
sigma = 0.2
bsm_call_value(S0, K, T, r, sigma)
#8.021352235143176

蒙特卡羅算法流程:

  1. 將時間間隔 [0,T] 分爲等距的、長度爲Δt\Delta_t的子時段。
  2. 開始循環 i=1,2,&ThinSpace;,li=1,2,\cdots, l。
    a) 對於每個時間步t{Δt,2Δt,,T}t\in \{\Delta_t, 2\Delta_t,\dots, T \},取僞隨機數 Z_t(i)。
    b) 逐個時間步應用僞隨機數,確定指數水平ST(i)S_T(i)的T值,以離散化公式2-2的方案
    c) 確定T時點歐式看漲期權的內在價值:ht:ht(ST(i))=max(ST(i)K,0)h_t:h_t(ST(i))=max(ST(i)-K, 0)
    d) 循環到 i=Ii=I
  3. 根據公式2-3,加總內在價值,求平均值,並扣除無風險短期利率。
    公式 2-3 歐式看漲期權的蒙特卡羅估算函數:
    C0erT1IIhT(ST(i))C_0\approx e^{-rT}\frac{1}{I}\sum_Ih_T(S_T(i))

2.1 基礎純python版

from time import time
from math import exp, sqrt, log
from random import gauss, seed
seed(20000)
t0 = time()
# 參數設定
S0 = 100.
K = 105.
T = 1.
r = 0.05
sigma = 0.2
M = 50 # 時間步長
dt = T / M
I = 250000
S = []

# M步循環
for i in range(I):
    path = []
    for t in range(M+1):
        if t == 0 :
            path.append(S0)
        else:
            z = gauss(0, 1)
            St = path[t-1] * exp((r - 0.5 * sigma **2) * dt 
                                + sigma * sqrt(dt) * z)
            path.append(St)
    S.append(path)
C0 = exp(-r * T) * sum([max(path[-1] - K, 0) for path in S])/ I
print(f'歐式期權定價 {C0}.')
print(f'共計花費時間 {np.round(time()-t0,1) }s.')
#歐式期權定價 7.9990448881765825.
#共計花費時間 19.3s.

2.2 Numpy 向量化版本

Numpy 的優勢

  • 更緊湊的實現,減少代碼冗餘,往往更容易維護。
  • 大部分Numpy是用C或者Fortran實現的,正確使用時,比純python更快
#示例:使用Numpy的歐式看漲期權蒙特卡羅估值
import math
import numpy as np
np.random.seed(20000)
t0 = time()
# 參數
S0 = 100; K=105; T=1.; r=0.05; sigma=0.2
M=50; dt=T/M; I=250000
S = np.zeros((M+1, I))
S[0] = S0
for t in range(1, M+1):
    z = np.random.standard_normal(I)
    S[t] = S[t-1] * np.exp((r - 0.5 *sigma**2)*dt
                          + sigma * math.sqrt(dt) * z)
C0 = math.exp(-r*T) * np.sum(np.maximum(S[-1]-K, 0)) /I
print(f'歐式期權定價 {C0}.')
print(f'共計花費時間 {np.round(time()-t0,1) }s.')
#歐式期權定價 8.03650296250933.
#共計花費時間 0.7s.

向量化和純Python 相比,速度有30倍以上的提升。且估算的蒙特卡羅值和基準值很接近。在對2-2公式進行對數化處理後,我們可以獲得更高的效率。
公式2-4 SDE的歐拉離散化方法(對數版本)
logST=logSTΔt+(r(1/2)σ2)Δt+σΔtztlogS_T=logS_{T-\Delta_t}+(r-(1/2)\sigma^2)\Delta_t + \sigma\sqrt{\Delta_t}z_t
這個版本完全採用遞增法,可以在Python層面上不使用任何循環的情況下實現蒙特卡羅算法。

#示例:
import math
import numpy as np
np.random.seed(20000)
t0 = time()
# 參數
S0 = 100; K=105; T=1.; r=0.05; sigma=0.2
S = S0 * np.exp(np.cumsum((r-0.5*sigma**2)*dt + sigma * math.sqrt(dt)
                       * np.random.standard_normal((M+1, I)), axis=0
                      ))
S[0] = S0
C0 = math.exp(-r*T) * np.sum(np.maximum(S[-1]-K, 0))/I

print(f'歐式期權定價 {C0}.')
print(f'共計花費時間 {np.round(time()-t0,1) }s.')
#歐式期權定價 8.165807966259603.
#共計花費時間 0.7s.
# 路徑可視化
import matplotlib.pyplot as plt
plt.plot(S[:, : 10])
plt.grid(True)
plt.xlabel('time step')
plt.ylabel('index level')

在這裏插入圖片描述

plt.hist(S[-1], bins=50)
plt.grid(True)
plt.xlabel('index level')
plt.ylabel('frequency')

在這裏插入圖片描述

數據文件下載:https://github.com/NanguangChou/BSM_call_option

參考文獻:

希爾皮斯科, 姚軍. Python金融大數據分析[M]. 人民郵電出版社, 2015.

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