満期ごとに1つの金利
ここまでは1つの利回りで債券のすべての支払いを割り引いていました。実際には、6か月、2年、10年貸したお金は異なる金利を稼ぎ、満期ごとのパターンがイールドカーブです。ゼロレートは、途中のクーポンのない、ある日付の1回の支払いに対する年率です。通常、カーブは満期とともに上がります(長く待つ分、多くを求めるため)が、フラットになったり、短期金利が長期金利を上回る逆イールドになったりし、市場はそれを成長への警告と読みます。
import numpy as np
maturities = np.array([0.5, 1, 2, 5, 10, 30]) # years where the market quotes a rate
rates = np.array([0.040, 0.042, 0.045, 0.048, 0.050, 0.052]) # an upward-sloping curve
# The rate at 3 years is not quoted: interpolate linearly between 2 years (4.5%) and 5 years (4.8%)
print(float(np.interp(3, maturities, rates))) # 0.046
print(np.interp([0.25, 7, 40], maturities, rates).tolist()) # [0.04, 0.0488, 0.052] flat outside the quoted range割引係数とフォワードレート
満期 t のゼロレート r から割引係数 (1 + r) ** -t(t に支払われる1単位の今日の価値)が得られます。カーブには期待も隠れています。2年間4.5%で貸すことは、1年間4.2%で貸し、さらに1年間を合意した金利 f で貸すことに等しいはずです:(1 + 4.5%)² = (1 + 4.2%)(1 + f)、したがって f = 4.80%。このフォワードレートが、今日固定される2年目の損益分岐金利です。一般には f = ((1 + r2) ** t2 / (1 + r1) ** t1) ** (1 / (t2 - t1)) - 1 です。
def discount_factor(rate, t):
return (1 + rate) ** -t
print(round(float(discount_factor(0.045, 2)), 5)) # 0.91573 one unit paid in 2 years
r1, t1, r2, t2 = 0.042, 1, 0.045, 2
forward = ((1 + r2) ** t2 / (1 + r1) ** t1) ** (1 / (t2 - t1)) - 1
print(round(forward, 5)) # 0.04801 the rate for year 2, implied by the curve
print(round((1 + r1) ** t1 * (1 + forward) ** (t2 - t1), 5), round((1 + r2) ** t2, 5)) # 1.09 1.09 both routes agreeカーブ全体での価格付け
単一の利回りでの価格付けは、1年後の支払いと10年後の支払いを同じに扱います。カーブを使う方法のほうが忠実です。各支払いについて、その日付のゼロレートを読み(必要なら補間)、割引係数にして フロー * 係数 を合計します。同じキャッシュフローを持つ2つの債券は、提示利回りに関係なく同じ価格でなければならず、銀行が債券を時価評価するのもこの方法です。このカーブ上の3年・5%の債券は1011.25で、フラットな4.6%なら1010.98です。カーブがなだらかなのでここでは近い値ですが、カーブがねじれると債券ごとに反応が異なります。
times = np.array([1.0, 2.0, 3.0])
flows = np.array([50.0, 50.0, 1050.0]) # a 3-year 5% bond with annual coupons
zero = np.interp(times, maturities, rates)
print(zero.tolist()) # [0.042, 0.045, 0.046]
print((discount_factor(zero, times)).round(5).tolist()) # [0.95969, 0.91573, 0.87379]
print(round(float((flows * discount_factor(zero, times)).sum()), 4)) # 1011.2462 on the curve
print(round(float((flows / 1.046 ** times).sum()), 4)) # 1010.9752 with one flat 4.6% instead# The forward rates between the quoted maturities, in one array expression
t = np.array([1.0, 2.0, 5.0])
r = np.array([0.042, 0.045, 0.048])
forwards = ((1 + r[1:]) ** t[1:] / (1 + r[:-1]) ** t[:-1]) ** (1 / np.diff(t)) - 1
print(forwards.round(5).tolist()) # [0.04801, 0.05] year 2 and years 3 to 5: an upward-sloping curve implies rising forwardsnp.interp は金利を一定に保ちますが、これは慣習であり事実ではありません。カーブを公表するときは、方法を必ず明記しましょう。- イールドカーブは満期ごとのゼロレート。ない満期は補間で読む(
np.interp、範囲外は一定) - 割引係数
(1 + r) ** -t、フォワードレート((1 + r2) ** t2 / (1 + r1) ** t1) ** (1 / (t2 - t1)) - 1 - 価格 = 各フローをその日付のゼロレートで割り引いた
フロー * 割引係数の合計。補間方法を明記する
次はオプションのペイオフです。コールやプットが満期に支払う額、オプション価格付けの出発点です。
ラボ:採点される内容
interpolate_rateで提示された満期の間の金利を読む25点discount_factorで金利を割引係数にする25点forward_rateでカーブに隠れた将来の金利を読む25点price_on_curveでカーブ全体で支払いの流れを評価する25点