I have an example the pricing of a CDS in Excel and I am trying to match it with QuantLib, in order to get the upfront bps.
Below there is a print of the excel screen where I know all the values are correct. I am adding the python quantlib code that tries to replicate what is in the worksheet. The code runs fine when you ask the NPV at the end, but it yields the wrong value. But when I ask for the upfrontBPS the code doesn't even compute a value for it.
What am I missing here?
import QuantLib as ql
# Trade parameters
today = ql.Date(22, ql.June, 2018)
ql.Settings.instance().evaluationDate = today
position = ql.Protection.Seller
notional = 10_000_000
spread = 210 / 10_000
recovery_rate = 0.25
calendar = ql.TARGET()
maturity = ql.Date(20, ql.June, 2023)
# Risk-free zero curve
curve_value = {
ql.Period(1, ql.Months): 2.091, # 1M
ql.Period(2, ql.Months): 2.172, # 2M
ql.Period(3, ql.Months): 2.335, # 3M
ql.Period(6, ql.Months): 2.504, # 6M
ql.Period(1, ql.Years): 2.77, # 1Y
ql.Period(2, ql.Years): 2.806, # 2Y
ql.Period(3, ql.Years): 2.878, # 3Y
ql.Period(4, ql.Years): 2.903, # 4Y
ql.Period(5, ql.Years): 2.914, # 5Y
ql.Period(6, ql.Years): 2.92, # 6Y
ql.Period(7, ql.Years): 2.931, # 7Y
ql.Period(8, ql.Years): 2.937, # 8Y
ql.Period(9, ql.Years): 2.947, # 9Y
ql.Period(10, ql.Years): 2.961, # 10Y
ql.Period(12, ql.Years): 2.989, # 12Y
ql.Period(15, ql.Years): 3.001, # 15Y
ql.Period(20, ql.Years): 3, # 20Y
ql.Period(25, ql.Years): 2.989, # 25Y
ql.Period(30, ql.Years): 2.967, # 30Y
}
dates = [today + p for p in curve_value.keys()]
zero_quotes = [val/100 for val in curve_value.values()]
curve = ql.YieldTermStructureHandle(ql.ZeroCurve(dates, zero_quotes, ql.Actual365Fixed(), ql.TARGET()))
# Payment Schedule
schedule = ql.Schedule( # bool endOfMonth, Date firstDate=Date(), Date nextToLastDate=Date()) -> Schedule
today, # Start date
maturity, # last date
ql.Period(ql.Quarterly), # Frequency of payments
calendar, # Calendar
ql.Following, # Business Day Payment Convention
ql.Unadjusted, # Termination Date Convention
ql.DateGeneration.TwentiethIMM, # Rule for date generation
False, # end of month convention
)
# CDS Contract
cds = ql.CreditDefaultSwap(
position, # Buyer or seller of protection
notional, # Real notional
spread, # Rate spread
schedule, # Schedule schedule
ql.Following, # BusinessDayConvention paymentConvention
ql.Actual365Fixed(), # DayCounter dayCounter
)
hazard_curve = ql.FlatHazardRate(
2, # settlementDays
calendar, # Calendar
ql.QuoteHandle(ql.SimpleQuote(spread)),
ql.Actual365Fixed(),
)
probability = ql.DefaultProbabilityTermStructureHandle(hazard_curve)
engine = ql.MidPointCdsEngine(
probability,
recovery_rate,
curve,
)
cds.setPricingEngine(engine)
print(cds.NPV()) # Code works up to here, but yield the wrong value
print(cds.upfrontBPS()) # does not work here