Python Quantlib Incorrectly Bond Pricing Issue

117 views Asked by At

I was trying to use Python Quantlib to calculate Z-spread of a fixed coupon bond.

I used scipy to optimize the difference between spread-adjusted-bond-price and spread-free-bond-price to solve the spread. So I built a function using Quantlib to do the bond pricing, where in some specific cases, the bond price is calculated to be zero.

Here are my codes:

import QuantLib as ql

# bond info and settings
settlementDays = 0
settlementDate = ql.Date('2023-02-18', '%Y-%m-%d')
effectiveDate = ql.Date('2019-08-21', '%Y-%m-%d')
terminationDate = ql.Date('2023-02-21', '%Y-%m-%d')
faceAmount = 100
coupon = 0.0625
frequency = ql.Period(2)
paymentConvention = ql.Thirty360(ql.Thirty360.ISMA)
calendar = ql.UnitedStates(ql.UnitedStates.NYSE)

# schedule
schedule = ql.Schedule(
    effectiveDate,
    terminationDate,
    frequency,
    calendar,
    ql.Unadjusted,
    ql.Unadjusted,
    ql.DateGeneration.Backward,
    False
)

# pricing curce

key_term_tenor = [0, 1, 3, 6, 12, 24, 36, 60, 84, 120, 240, 360, 1200]      # month
key_term_interest = [0, 0.049206, 0.049206, 0.050475, 0.050166, 0.046579, 0.043151, 0.040502, 0.039244, 0.038166, 0.040554, 0.038661, 0.038661]
key_term_spread = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

spot_dates = [settlementDate + ql.Period(round(tenor), ql.Months) for tenor in key_term_tenor]
spot_rates = [x + y for x, y in zip(key_term_interest, key_term_spread)]

spot_curve = ql.ZeroCurve(
    spot_dates,
    spot_rates,
    paymentConvention,
    calendar,
    ql.Linear(),
    ql.Compounded,
    ql.Annual
)

# bond
pricing_curve = ql.YieldTermStructureHandle(spot_curve)

bond = ql.FixedRateBond(
    settlementDays,
    faceAmount,
    schedule,
    [coupon],
    paymentConvention
)

bond.setPricingEngine(ql.DiscountingBondEngine(pricing_curve))

print(bond.cleanPrice())   # 0.0
print(bond.dirtyPrice())   # 0.0

Through the above codes, I got zero cleanprice and zero dirtyprice, got me confused. Is there something wrong in my codes?

An interesting finding is that bond.settlementDays is 2, which is not the same as what I input.

BTW, is there any better alternative solutions to solve the Z-spread?

Thanks!

I think the price should not be 0 obviously, something must be wrong here.

1

There are 1 answers

3
Luigi Ballabio On

To see what's happening, you can add

print(bond.settlementDays())
print(ql.Settings.instance().evaluationDate)
print(bond.settlementDate())

at the end of your script.

Before doing that, though: are you setting the evaluation date? Because that is missing from the code in your question, and without setting it the bond will be repriced as of today and it will be expired. So, assuming that you also have added

ql.Settings.instance().evaluationDate = ql.Date(18,2,2023)

your code still returns price = 0, and the print statements above output

0
February 18th, 2023
February 21st, 2023

i.e., 0 settlement days, the evaluation date we just set, and the calculated settlement date. (By the way, I'm using 1.32, but I see 0 settlement days even if I reinstall 1.28. Are you sure you're getting 2?)

Why the bond settlement date is on February 21st, which causes the price to be 0 because we're at maturity and not trading anymore? Because February 18th is a Saturday, the 19th is a Sunday, and the 20th (the third Monday in February) is President's day in the U.S. and therefore a bank holiday for the calendar you're using. The first available trading day is the 21st, but that's too late.

If you try February 17th instead and set the evaluation date (and the curve nodes) accordingly, you're going to get a non-null price.