Last active
May 8, 2020 17:48
-
-
Save albert-yu/b9b909bb32f7ff2c9851b2f0e8edaac5 to your computer and use it in GitHub Desktop.
Some potentially useful functions for bond math
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def _solver_try(y, face_val, num_periods, coupon_payment): | |
result = 0 | |
for t in range(1, num_periods + 1): | |
result += coupon_payment / (1 + y) ** t | |
if t == num_periods: | |
result += face_val / (1 + y) ** t | |
return result | |
def solver(price, face_val, num_periods, coupon_payment): | |
""" | |
Computes the yield-to-maturity, just like Excel's solver | |
Parameters | |
---------- | |
price : double | |
present value or price | |
face_val : double | |
bond's face value | |
num_periods : int | |
number of periods this bond pays | |
coupon_payment : double | |
coupon payment per period, if any | |
""" | |
start = 0.05 # start at 5% | |
tol = 1e-10 | |
last_result = 0 | |
to_try = start | |
step_size = 0.025 | |
iters = 0 | |
while iters < 1000: | |
res = _solver_try(to_try, face_val, num_periods, coupon_payment) | |
if abs(res - price) < tol: | |
print("converged") | |
break | |
if res < price: | |
# we've been increasing: need to decrease | |
if step_size > 0: | |
step_size = step_size / 2 | |
step_size = -step_size | |
else: | |
# we've been decreasing, need to increase | |
# y now | |
if step_size < 0: | |
step_size = step_size / 2 | |
step_size = -step_size | |
to_try += step_size | |
iters += 1 | |
if iters == 1000: | |
print("failed to converge") | |
return to_try | |
def bond_dur(ytm, price, face_val, num_periods, coupon_payment=0): | |
""" | |
Computes bond duration | |
Parameters | |
---------- | |
ytm : double | |
yield to maturity | |
price : double | |
the price the bond is trading at | |
face_val : double | |
face value | |
num_periods: int | |
the number of periods | |
coupon_payment : double | |
coupon payment per period | |
""" | |
result = 0 | |
for t in range(1, num_periods + 1): | |
pv = 0 | |
if t == num_periods: | |
pv = (coupon_payment + face_val) / (1 + ytm) ** t | |
else: | |
pv = coupon_payment / (1 + ytm) ** t | |
s = pv * t | |
result += s | |
return result / price | |
def main(): | |
num_periods = 5 | |
coupon = 80 | |
face = 1000 | |
price = 1090 | |
ytm = solver(price, face, num_periods, coupon) | |
print(ytm) | |
# 0.058709 | |
print(bond_dur(ytm, price, face, num_periods, coupon)) | |
# 4.34 (in years) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment