落とし穴のある連鎖律
S が時間の滑らかな関数なら、f(S) の変化は通常の連鎖律どおり f'(S) * dS です。しかし dS には dW が含まれ、(dW)^2 = dt は dt と同じオーダーで無視できません。f(S + dS) を2次まで展開すると 0.5 * f''(S) * (dS)^2 の項が残り、(dS)^2 = sigma^2 * S^2 * dt です。この追加項が伊藤の補題で、オプション価格を導く主要な道具です。
def ito_drift(s, mu, sigma, f_t, f_s, f_ss):
return f_t + mu * s * f_s + 0.5 * sigma ** 2 * s ** 2 * f_ss
# f(S) = S: the drift is just mu * S (no curvature, no extra term)
print(ito_drift(100.0, 0.08, 0.2, 0.0, 1.0, 0.0)) # 8.0
# f(S) = S squared: f_S = 2 S and f_SS = 2, so the curvature term appears
print(ito_drift(100.0, 0.08, 0.2, 0.0, 2 * 100.0, 2.0)) # 1600 + 400 = 2000.0# The drift for several functions of the price, S = 100, mu = 8%, sigma = 20%
def ito_drift(s, mu, sigma, f_t, f_s, f_ss):
return f_t + mu * s * f_s + 0.5 * sigma ** 2 * s ** 2 * f_ss
s, mu, sigma = 100.0, 0.08, 0.2
print("S ", ito_drift(s, mu, sigma, 0.0, 1.0, 0.0)) # 8.0
print("S^2 ", ito_drift(s, mu, sigma, 0.0, 2 * s, 2.0)) # 2000.0
print("S^3 ", ito_drift(s, mu, sigma, 0.0, 3 * s ** 2, 6 * s)) # 240000 + 120000 = 360000.0
print("sqrt(S)", round(ito_drift(s, mu, sigma, 0.0, 0.5 / s ** 0.5, -0.25 / s ** 1.5), 4)) # 0.35 : curvature pulls it down対数価格とシグマ二乗の2分の1
補題を f = ln S に適用します。f_t = 0、f_S = 1/S、f_SS = -1/S^2 なので、ドリフトは mu - 0.5 * sigma^2、拡散は sigma で、d ln S = (mu - sigma^2/2) dt + sigma dW です。対数価格は算術ブラウン運動であり、GBMの解の指数に mu - sigma^2/2 が入る理由がまさにこれです。exp の凸性と ln の凹性は同じ事実の表裏です。
def ito_drift(s, mu, sigma, f_t, f_s, f_ss):
return f_t + mu * s * f_s + 0.5 * sigma ** 2 * s ** 2 * f_ss
def log_drift(mu, sigma):
s = 1.0 # the answer does not depend on s
return ito_drift(s, mu, sigma, 0.0, 1.0 / s, -1.0 / s ** 2)
print(log_drift(0.08, 0.20)) # 0.06 = 0.08 - 0.5 * 0.2**2
print(round(log_drift(0.08, 0.60), 4)) # -0.1 a high volatility makes the typical growth negativeシミュレーションで補題を確かめる
補題を鵜呑みにする必要はありません。s_new = s + mu * s * dt + sigma * s * sqrt(dt) * z というランダムな1ステップの動きを多数作り、関数 f について f(s_new) - f(s) を計算して平均し、dt で割ります。結果は補題のドリフトに収束します。f = S^2 で曲率項を省くとシミュレーションとはっきりずれ、追加項が本物だと確認できます。
import numpy as np
def mean_change_estimate(f, s, mu, sigma, dt, z):
s_new = s + mu * s * dt + sigma * s * np.sqrt(dt) * z
return float(np.mean(f(s_new) - f(s)) / dt)
half = np.random.default_rng(1).standard_normal(200_000)
z = np.concatenate([half, -half]) # pairs of opposite draws: the noise at first order cancels
estimate = mean_change_estimate(lambda x: x ** 2, 100.0, 0.08, 0.2, 0.001, z)
print(round(estimate)) # about 2000
print(round(0.08 * 100.0 * 2 * 100.0)) # 1600 the ordinary chain rule alone: the curvature term (400) is missingオプションにとって重要な理由オプションを値付けするには、その価値
V(S, t) に補題を適用します。ドリフトには V_t、mu * S * V_S、0.5 * sigma^2 * S^2 * V_SS が含まれ、デルタヘッジで dW の項を消すとブラック=ショールズ方程式が残ります。そこにはこれまでのトピックのシータ、デルタ、ガンマが入っています。mu は無リスク金利に置き換わり、株の期待リターンは価格から消えます。- 伊藤の補題:
f(S, t)のドリフトはf_t + mu * S * f_S + 0.5 * sigma^2 * S^2 * f_SS、ノイズ係数はsigma * S * f_S - 曲率項があるのは
(dW)^2 = dtだから。ln SではGBMの解のドリフトmu - sigma^2 / 2を与える - シミュレーションで確認する:
f(s_new) - f(s)の平均をdtで割ると補題と一致する。オプション価値に適用するとブラック=ショールズが得られる
次はアジアンオプションです。ペイオフがオプション期間中の平均価格に依存します。
ラボ:採点される内容
ito_driftで関数のドリフトを求める25点ito_diffusionでノイズの係数を求める25点log_driftで対数価格に適用する25点mean_change_estimateでシミュレーションにより確かめる25点