# Compute yield to maturity for a bond with coupons when price is different from face value

Given the formula $$P = \sum_{t=1}^n \frac{CF_t}{(1+i)^t}$$ we can apply it to the case of a bond with constant coupons so that :

$$\displaystyle P =\sum_{t=1}^n \frac{C}{(1+i)^t} + \frac{FV}{(1+i)^n} \implies P = C(\frac{1-(1+i)^{-n}}{i})+\frac{FV}{(1+i)^n}$$

with $$P$$ = price , $$C$$ = coupon at the time $$t$$ , $$FV$$ = face value , $$i$$ = yield to maturity

My problem : How is it estimated $$i$$ when $$P\neq FV$$ and $$P, C,FV$$ are known ?

I think that it's not possible to obtain an exact result , because it doesn't seem possible to separate the $$i$$ from the rest . I tried to use Taylor formulas somehow to get an approximation but I haven't come to anything.

EDIT :

Is bisection method the only possible way to proceed ? Is something about polynomial approximation possible?

• I’m used to more complex formulae (irregular day counts, etc.), and the quick and dirty solution is bisection - get an upper and lower limit, take the midpoint, and keep sub-dividing. There might be something more rigorous for the simplified formula you use there, so I won’t post this as the answer. – Brian Romanchuk May 9 '20 at 22:36
• ah yes , this could be a good way to proceed even if I guess it's an expensive method for what regards the number of computations – Tortar May 9 '20 at 22:41
• I would have worried about computational complexity in 1990. In 2020? Not as much. It converges at a known rate, Since you are using a simplified formula, it’s presumably not a real time pricing application. – Brian Romanchuk May 10 '20 at 0:08

There might be a closed-form method that is more efficient, but in practice, bond market calculations have a lot of irregularities when compared to the pricing formula given above.

1. A function to calculate price from yield.
2. A function to calculate modified duration from the yield. This could be done by using the previous function, and approximating the modified duration by looking at the price change that results from a small yield shock.

The modified duration (which I will shorten to “duration” here) is the sensitivity of price return as a percentage from a yield change (need to multiply by -1). E.g., if the duration is 2, a .01% rise in yields (approximately) gives a price percentage change of -0.02%.

1. Start off with the yield estimate equalling the coupon. (If you are working from time series data, you could use the previous yield as a starting point.)
2. Calculate the price. If within the target margin of error of the market price, stop, you’re done.
3. Otherwise, calculate the duration for the bond at the estimated yield, and the deviation of the estimated price from the market price.
4. The new estimate is now the old yield plus the change required to get the new price to match the market price, based on the modified duration relation. E.g., yield change = -1*(price error in percentage terms)/(modified duration).
5. Using the new estimate, go back to step 2.

This procedure eliminates the need to generate an upper and lower bound for the yield, and should converge faster. If you are working from a time series, the previous yield will tend to be close the current yield, and the duration price change estimate will be a good approximation of the actual price change, and so convergence may only take a few steps.

Following the suggestion by Brian Romanchuk of the 'quick and dirty' bisection method in the comments, I wrote this python program to estimate the yield to maturity :

def find_i(P, C, FV, n, epsilon):

if P > FV : i_min, i_max = 0 , C / FV
elif P < FV : i_min, i_max = C / FV , 10
else : return C / FV

while True :

i_mid = ( i_max + i_min ) / 2 # mid point of the current intervall
point = C*(1-(1+i_mid)**(-n))/i_mid + FV/(1+i_mid)**n - P # it should converge to 0

if abs(point) < epsilon  : return i_mid # if point is close enough to 0 then finish

if point > 0 : i_min = i_mid # [i_mid,i_max] : new intervall
elif point < 0 : i_max = i_mid # [i_min,i_mid] : new intervall

P, C, FV, n = 1100, 100, 1000, 10
epsilon = 10**-5
i = find_i(P, C, FV, n, epsilon)


The choice of $$i_{min}$$ and $$i_{max}$$ is important to estimate $$i$$ in edge cases. Dealing with negative interest is harder.

I'm going to update the method to improve the computation , for now I list the relavant facts used :

1) When $$P = FV$$ then $$i = C / FV$$

2) $$P$$ and $$i$$ are negative correlated when $$i>0$$

• If you go with bisection, probably need to add code to search for wider limits if needed. Another alternative is the Newton-Raphson method, which also eliminates the need for hard limits. If you have a formula for the duration, you can use the duration to estimate what yield change causes the price to match the market price. It will converge faster. – Brian Romanchuk May 10 '20 at 12:16
• Thanks very much for this helpful comment ! I will for sure try to add some of this tools ! – Tortar May 10 '20 at 12:25