上がるか下がるかしかない株
ブラック=ショールズは公式ですが、二項ツリーは手順であり、公式では扱えないものも値付けできます。満期までの時間を小さなステップに分け、各ステップで株価は u 倍に上がるか d 倍に下がるかのどちらかだけとします。n ステップ後には n + 1 通りの株価があります。満期のペイオフは分かっており、それより前の各ノードは、そこから続く2つのノードの割引平均の価値なので、ツリーを現在まで後ろ向きに埋められます。
# One step: a stock at 100 goes to 110 or 90. What is a call with strike 100 worth?
import math
spot, strike, rate, dt = 100, 100, 0.05, 1.0
u, d = 1.10, 0.90
p = (math.exp(rate * dt) - d) / (u - d) # the probability that makes the stock grow at the rate
up_value, down_value = max(spot * u - strike, 0), max(spot * d - strike, 0)
price = math.exp(-rate * dt) * (p * up_value + (1 - p) * down_value)
print(round(p, 4), round(up_value, 4), down_value) # 0.7564 10.0 0
print(round(price, 4)) # 7.1947この確率は予測ではない
数 p は、株価が平均して無リスク金利で成長するように選びます:p * u + (1 - p) * d = exp(rate * dt)、したがって p = (exp(rate * dt) - d) / (u - d) です。これはリスク中立確率で、複製ポートフォリオを割引期待値に等しくするための道具であり、株価がどこへ向かうかについての見方ではありません。観測されたボラティリティに合わせるため、コックス=ロス=ルービンシュタイン(CRR)のツリーは u = exp(sigma * sqrt(dt))、d = 1 / u を使います。上昇のあとの下落は元の価格に戻るのでツリーは再結合し、n ステップでも終端ノードは n + 1 個だけです。
# The CRR parameters and the prices at expiry of a 3-step tree
import math
def crr_parameters(rate, sigma, t, steps):
dt = t / steps
u = math.exp(sigma * math.sqrt(dt))
d = 1 / u
p = (math.exp(rate * dt) - d) / (u - d)
return u, d, p
def terminal_prices(spot, u, d, steps):
return [spot * u ** j * d ** (steps - j) for j in range(steps + 1)]
u, d, p = crr_parameters(0.05, 0.2, 1, 3)
print(round(u, 4), round(d, 4), round(p, 4)) # 1.1224 0.8909 0.5438
print([round(price, 2) for price in terminal_prices(100, u, d, 3)])後ろ向きに計算する
満期では各ノードはペイオフそのもので、コールなら max(S - K, 0) です。1列前では各値が exp(-rate * dt) * (p * value_up + (1 - p) * value_down) となり、列のノードは1つ減ります。これを n 回繰り返すと値が1つ残り、それが今日の価格です。Python では1列がリストで、1回の掃引は隣り合うペアに対するリスト内包表記です。ステップ数を増やすと価格はブラック=ショールズの価格に収束し、ツリーは公式の結果を再現します。
# The tree price of a European call approaches the Black-Scholes price as steps grow
import math
def norm_cdf(x):
return 0.5 * (1 + math.erf(x / math.sqrt(2)))
def bs_call(spot, strike, rate, sigma, t):
d1 = (math.log(spot / strike) + (rate + 0.5 * sigma ** 2) * t) / (sigma * math.sqrt(t))
return spot * norm_cdf(d1) - strike * math.exp(-rate * t) * norm_cdf(d1 - sigma * math.sqrt(t))
def european_call_tree(spot, strike, rate, sigma, t, steps):
dt = t / steps
u = math.exp(sigma * math.sqrt(dt))
d = 1 / u
p = (math.exp(rate * dt) - d) / (u - d)
disc = math.exp(-rate * dt)
values = [max(spot * u ** j * d ** (steps - j) - strike, 0) for j in range(steps + 1)]
for _ in range(steps):
values = [disc * (p * values[j + 1] + (1 - p) * values[j]) for j in range(len(values) - 1)]
return values[0]
print("Black-Scholes", round(bs_call(100, 100, 0.05, 0.2, 1), 4))
for steps in (1, 5, 25, 100, 400):
print(steps, round(european_call_tree(100, 100, 0.05, 0.2, 1, steps), 4))早期行使
ツリーの本当の強みは、公式では値付けできないオプションです。アメリカンオプションはいつでも行使できます。各ノードで保有者は2つの値、保有を続けたときの割引期待値と今すぐ行使したときのペイオフを比べ、大きいほうを取ります。アルゴリズムの変更点は、各ノードでの max がひとつ増えるだけです。配当のない株のコールでは早期行使は得になりませんが、プットでは得になりえます。深くイン・ザ・マネーのプットは今日の現金のほうが価値が高いため、アメリカン・プットはヨーロピアンより高くなります。
# An American put against a European put: the early-exercise premium
import math
def tree(spot, strike, rate, sigma, t, steps, american):
dt = t / steps
u = math.exp(sigma * math.sqrt(dt))
d = 1 / u
p = (math.exp(rate * dt) - d) / (u - d)
disc = math.exp(-rate * dt)
values = [max(strike - spot * u ** j * d ** (steps - j), 0) for j in range(steps + 1)]
for i in range(steps - 1, -1, -1):
values = [disc * (p * values[j + 1] + (1 - p) * values[j]) for j in range(i + 1)]
if american:
values = [max(values[j], strike - spot * u ** j * d ** (i - j)) for j in range(i + 1)]
return values[0]
for spot in (80, 100, 120):
european = tree(spot, 100, 0.05, 0.2, 1, 200, False)
american = tree(spot, 100, 0.05, 0.2, 1, 200, True)
print(spot, round(european, 4), round(american, 4), "premium", round(american - european, 4))n ステップはおよそ n**2 / 2 回のノード更新になり、収束も滑らかではありません(行使価格がノードの間のどこに入るかで誤差が揺れる)。そのためツリーは複数のステップ数で比べて検証します。- 各ステップで株価は
u倍かd倍になる。CRR の選び方u = exp(sigma * sqrt(dt))、d = 1 / uでツリーは再結合し、nステップの終端ノードはn + 1個 - 後ろ向きに値付けする。満期のペイオフから始め、各ノードで
exp(-rate * dt) * (p * up + (1 - p) * down)。pはリスク中立確率で予測ではない - ステップを増やすとブラック=ショールズに収束する。アメリカン・オプションは各ノードに
max(継続, 行使)を1つ加えるだけで、これがツリーの存在理由
次は有限差分法です。オプションの偏微分方程式を格子上で解いて値付けします。
ラボ:採点される内容
crr_parametersで上昇と下落の幅を決める25点terminal_pricesで満期時の株価を並べる25点european_call_treeでツリーを後ろ向きにたどる25点american_put_treeで早期行使を認める25点