Valuation of Fixed Rate Bond using Spread with QuantLib

Valuation of Fixed Rate Bond using Spread with QuantLib


In the last article we made a brief introduction on fixed rate bond using QuantLib. In this article we will extend the discussion by using QuantLib's bond pricing engine to price a bond based on a spread. Pricing will be done base on a yield term structure which may not necessarily flat.

The Yield Term Structure

For our purpose we will use the term structure created in previous exercises - 'Interest Rate Term Structure With QuantLib - Part 1 and 2' with minor changes to the conventions and a notable change in the calendar. In the event that a country's calendar is not in the list supported by QuantLib, a customs calendar can be created using ql.BespokeCalendar(country_name). This will create a calendar with all dates considered as business days. For standard weekends we can join the newly created calendar with ql.WeekendsOnly() using ql.JoinCalendar(calendar1, calendar2). Then we can add the country's holidays using addHoliday(). This will gives us a custom calendar with holidays including weekends.

The following should look familiar apart from the new created custom calendar and a single public holiday added as an example. After joining the calendars any holiday update via cal will affect mycal but not vice versa.

We will assume that the curve generated above is a risk free curve generated from Government issued securities. Now let us contruct a 10-year corporate bond with 5.00% semi-annual coupon. The following snippet should still look familiar, albeit minor changes, to those who read my previous article.

Revaluing A Bond Using Curve

In practice, revaluation of a bond should be done at its own credit curve, or at its last traded price. Instead of using the internal rate of return in valuing the bond, we will revalue the bond at the risk free curve to see how it is done using QuantLib.

ql.YieldTermStructureHandle, ql.DiscountingBondEngine and ql.setPricingEngine will be the three new classes we will use in this article. ql.YieldTermStructureHandle is required for ql.DiscountingBondEngine which in turn is required by ql.setPricingEngine as shown in the snippet below. Once we have set the pricing engine, the bond can be revalued using bond.NPV(). Note that the coupon of the bond is at 5.00% while the curve is generally below 3.00%. The price therefore will be a lot more than 100.

A function that we purposely left out in our previous article is BondFunctions.zSpread(). For the definition or description of z-spread please visit Wikipedia. In the snippet we used the result of bond.NPV(), to calculate z-spread, which will of course led to an answer of 0.00%. The market price of the bond should instead be used to derived the appropriate z-spread. Try changing bond_npv in the paramater of ql.BondFunctions.zSpread to 103, assuming that it is the market price of the bond. The z-spread is calculated is 1.7349%. We will use this number in the following section.

Spread Curve

We can now introduced a spread curve which is a TermStructure class in QuantLib. While there are other ways to add spread to a YieldTermStructure class such as ql.ForwardSpreadedTermStructure and ql.ZeroSpreadedTermStructure we will be using ql.SpreadedLinearZeroInterpolatedTermStructure as it requires more explanation. The other two classes are sufficient simple and documented here which include the parameters required for ql.SpreadedLinearZeroInterpolatedTermStructure.

ts_spread is the TermStructure class of the spread with initial values set at 0.00 (formed by spread_start and spread_end), i.e. no spread above the risk free curve denoted by ts_handle. Running the code will simple give us the same value as bond_npv in the previous snippet.

We can change the spread curve to either upward sloping, downward sloping or simple flat at a higher spread by changing the value used by spread_start and spread_end. Lets use our z-spread number calculated earlier and change spread_start to spread_start = ql.SimpleQuote(0.017349) and spread_end to spread_end = ql.SimpleQuote(0.017349) and run the code again. bond.NPV() will return a value of 103.0001 (due to rounding of the z-spread), which is the same price we input in ql.BondFunctions.zSpread.

We can create variations to the above method used. For instance, we can use ql.FlatForward to create the risk free curve and the flat spread curve simply becomes spread above the risk free curve which is akin to quoting the bond as \(10Y_{\text{riskfree}} + \text{spread}\).


Creation of custom calendars is simple enough if a country or a calendar is not included. The bond engine is also flexible to be used for various method of valuing a bond. One can use yield to maturity, or the yield curve + z-spread or a standard yield to maturity + spread to value or price a bond. An added advantage is the flexibility of creating the shape of the spread curve itself.