価格から利回りへ戻る
市場では債券は価格で提示されますが、投資家は利回りで債券を比べます。最終利回り(YTM)は、債券のすべてのキャッシュフローの現在価値が価格に等しくなる唯一の割引率で、今買って満期まで持ち、すべての支払いが行われた場合に得られる年率リターンです。クーポン5%、価格925.61の10年債では6%になります。価格と違って、未知数が高次の多項式の中にあるため、YTMには閉じた式がありません。
import numpy as np
def bond_price(face, coupon_rate, ytm, years, freq=2):
n = int(round(years * freq))
times = np.arange(1, n + 1) / freq
flows = np.full(n, face * coupon_rate / freq)
flows[-1] += face
return (flows / (1 + ytm / freq) ** (freq * times)).sum()
price = bond_price(1000, 0.05, 0.06, 10)
print(round(price, 4)) # 925.6126 the market price we pretend to observe
# A back-of-the-envelope yield: annual coupon plus the yearly share of the discount, over the average of price and par
coupon = 1000 * 0.05
print(round((coupon + (1000 - price) / 10) / ((1000 + price) / 2), 5)) # 0.05966 close to 6%, but not exact探索:二分法
利回りが上がると価格は着実に下がるので、探索できます。答えを確実に含む区間 [low, high](0%〜100%)を取り、中点で債券を評価します。価格が目標より高ければ利回りはもっと高いはずなので low を中点に上げ、そうでなければ high を下げます。1回ごとに区間は半分になり、40回で1兆分の1に縮みます。二分法は他の方法より遅いものの、答えが区間内にある限り失敗しないため、安全な切り札になります。
low, high = 0.0, 1.0
for step in range(6):
mid = (low + high) / 2
price_mid = bond_price(1000, 0.05, mid, 10)
print(step, round(mid, 5), round(price_mid, 2))
if price_mid > price: # priced too high: the yield must be higher
low = mid
else:
high = mid
# 0 0.5 110.38
# 1 0.25 275.86
# 2 0.125 578.47
# 3 0.0625 908.08
# 4 0.03125 1159.97
# 5 0.04688 1024.72 the interval keeps shrinking around 6% (the target price is 925.61)より速い探索:ニュートン法
ニュートン法は傾きを使います。f(y) = bond_price(y) - 目標 とおき、f(y) = 0 を解きます。現在の推定値で接線をたどってゼロに下ろします:y_new = y - f(y) / f'(y)。傾きは微小な刻み h による数値微分 (f(y + h) - f(y - h)) / (2h) で推定できます。答えの近くでは正しい桁数が1回ごとにほぼ倍増するため、二分法で40回かかるところが4〜5回で済みます。出発点が悪い、傾きが平ら、といった場合は失敗しうるので、回数の上限を設け、収束しなければ二分法に戻りましょう。
y, h = 0.05, 1e-6 # start at the coupon rate
for step in range(5):
f = bond_price(1000, 0.05, y) - price
slope = (bond_price(1000, 0.05, y + h) - bond_price(1000, 0.05, y - h)) / (2 * h)
print(step, round(y, 8), round(f, 6))
y = y - f / slope
# 0 0.05 74.387374
# 1 0.05954347 3.245932
# 2 0.05999903 0.006916
# 3 0.06 0.0
# 4 0.06 0.0 converged to 6% after 3 steps# Check the answer by pricing the bond at the yield we found
print(round(bond_price(1000, 0.05, y, 10), 4), round(price, 4)) # 925.6126 925.6126 the same price: the yield is right利回りは比較でき、価格は比較できない最終利回りは、各クーポンを同じ利回りで再投資でき、発行体が全額を支払うことを前提にしており、予測ではなく約束です。非常に高い利回りで取引されている債券は、たいてい市場が返済を疑っているものです。利回りの比較は、リスクと満期が似た債券どうしに限りましょう。
- YTMは債券の現在価値を価格に一致させる割引率。閉じた式がないので探索して求める
- 二分法:
[low, high]を半分にし続ける(価格が目標より高ければlowを上げる)。遅いが安全 - ニュートン法:数値微分の傾きで
y = y - f / slope。数回で収束するが、回数上限と良い出発点が必要
次はデュレーションとコンベクシティです。債券価格が利回りにどれだけ敏感かを測ります。
ラボ:採点される内容
approx_ytmで利回りを概算する25点bisect_yieldで区間を半分にして利回りを求める25点newton_yieldでより速く利回りを求める25点classify_priceで債券の状態を分類する25点