من السعر عودةً إلى العائد
في السوق يُسعَّر السند بسعره، لكن المستثمرين يقارنون السندات بالعائد. والعائد حتى الاستحقاق (YTM) هو معدل الخصم الوحيد الذي تساوي عنده القيمة الحالية لكل تدفقات السند سعره: العائد السنوي الذي تكسبه إن اشتريت الآن واحتفظت حتى الاستحقاق ودُفعت كل الدفعات. لسند عشر سنوات سعره 925.61 وقسيمته 5% يساوي 6%. وبخلاف السعر، ليس للعائد صيغة مغلقة لأن المجهول داخل متعددة حدود عالية الدرجة.
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. كل خطوة تنصّف المجال، فتختزله 40 خطوة بعامل تريليون. والتنصيف بطيء قياسًا بطرق أخرى لكنه لا يفشل ما دام الجواب داخل المجال، فهو الملاذ الآمن.
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). قرب الجواب يتضاعف عدد الأرقام الصحيحة تقريبًا في كل خطوة، فتكفي أربع أو خمس خطوات حيث يحتاج التنصيف إلى أربعين. وقد تفشل طريقة نيوتن إن بدأت بداية سيئة أو كان الميل مسطحًا، فاحتفظ بحد للخطوات وارجع إلى التنصيف إن لم تتقارب.
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- العائد حتى الاستحقاق هو معدل الخصم الذي يساوي القيمة الحالية للسند بسعره؛ وليس له صيغة مغلقة فيُبحث عنه.
- التنصيف: واصل تنصيف
[low, high](السعر فوق الهدف: ارفعlow)؛ بطيء لكنه آمن. - نيوتن:
y = y - f / slopeبميل عددي؛ يتقارب في خطوات قليلة لكنه يحتاج حدًا للخطوات وبداية جيدة.
التالي: المدة والتحدب، قياس حساسية سعر السند للعائد.
المختبر: ما الذي يُقيَّم
- قدّر العائد تقريبيًا باستخدام
approx_ytm25 نقطة - اعثر على العائد بتنصيف المجال باستخدام
bisect_yield25 نقطة - اعثر على العائد أسرع باستخدام
newton_yield25 نقطة - سمِّ وضع السند باستخدام
classify_price25 نقطة