#1768·QuantLib

Pricing Bonds with face values other than $100

Author: bkhoorCreated Aug 15, 2023Updated Apr 4, 2026
Labelshelp wanted

For pricing bonds that are issued with face values other than $100 (e.g. baby bonds which are issued with FV of $25), there is an automatic conversion of any prices calculated to be out of $100 in both BondFunctions analytics and the internal bond analytics. In the example below, I set a face amount to be $25 in the pricer, but when I call cleanPrice() or other relevant method, the price is automatically converted internally by multiplying with 100/notional. To convert the returned clean price back to the desired unit of par, I have to do it on my end by multiplying by faceAmount/100 (or alternatively I can use following to get cleanPrice: settlementValue - accruedAmount/(100/faceAmount)). Similarly, if I try to pass in the original price and retrieve a zspread, the price is assumed to be on $100 FV and the zspread calculated is wrong, so I have to convert the price the same way before passing it into the zspread method.

Is there a more direct way to retrieve clean price and other bond analytics in the unit specified by the notional without have to do additional pre/post conversion?

Please run below code for example.

Thanks for any thoughts.

python
import QuantLib as ql

#set evaluation date
eval_date_ql = ql.Date(15, 8, 2023)
ql.Settings.instance().setEvaluationDate(eval_date_ql)

#instrument information:
price = 14.6586
zspread = 0.08
maturity_date_ql = ql.Date(15, 8, 2053)
face_amount = 25.

#create flat rates curve with ability to add spread:
term_structure = ql.FlatForward(eval_date_ql, ql.QuoteHandle(ql.SimpleQuote(0.01)),
                                ql.Thirty360(), ql.Compounded, 4)

curve_handle = ql.YieldTermStructureHandle(term_structure)
spreads = [ql.SimpleQuote(0.0) for t in range(2)]
zero_curve = ql.SpreadedLinearZeroInterpolatedTermStructure(curve_handle,
                                                            [ql.QuoteHandle(q) for q in spreads],
                                                            [eval_date_ql, maturity_date_ql])

zero_curve.enableExtrapolation()
curve_handle = ql.YieldTermStructureHandle(zero_curve)


#create spreaded curve handle
spread_handle = ql.QuoteHandle(ql.SimpleQuote(zspread))
ts_spreaded = ql.ZeroSpreadedTermStructure(curve_handle, spread_handle,
                                           ql.Compounded, 4)
ts_spreaded_handle = ql.YieldTermStructureHandle(ts_spreaded)
schedule = ql.Schedule(eval_date_ql, maturity_date_ql, ql.Period(4),
                       ql.UnitedStates(), ql.ModifiedFollowing,
                       ql.ModifiedFollowing, ql.DateGeneration.Backward, False)

#Bond Pricer:
fixed_rate_bond = ql.FixedRateBond(2, face_amount, schedule, [0.05], ql.Thirty360(), ql.Following, 100.0)
fixed_rate_bond.setPricingEngine(ql.DiscountingBondEngine(ts_spreaded_handle))

#Calculating clean price
cleanPrice = fixed_rate_bond.cleanPrice()
cleanPrice2 = fixed_rate_bond.settlementValue() - fixed_rate_bond.accruedAmount()/(100/face_amount)

print(f"Clean Price from pricer: {cleanPrice}")
print(f"Clean Price we want: {cleanPrice2}")

#calculating Z-spread:
zspread = ql.BondFunctions.zSpread(fixed_rate_bond, price,
                                   ql.ImpliedTermStructure(curve_handle,
                                                           eval_date_ql+2),
                                   ql.Thirty360(), ql.Compounded, 4)
print(f"Z-spread from pricer: {zspread}")

zspread2 = ql.BondFunctions.zSpread(fixed_rate_bond, price*4,
                                   ql.ImpliedTermStructure(curve_handle,
                                                           eval_date_ql+2),
                                   ql.Thirty360(), ql.Compounded, 4)
print(f"Z-spread we want: {zspread2}")