Evaluating Fixed Income Parametric Curves Without Fitting
Summary
The document reports a QuantLib usage problem involving a fitted bond discount curve. The author first optimizes an exponential-spline curve against bond helpers, then reuses the fitted parameters as a guess while setting the iteration limit to zero, intending to evaluate the parametric curve without refitting. The optimized curve returns discount factors and bond values, but the evaluation-only curve fails when queried for a discount factor or used in a bond pricing engine.
The example includes bond schedules, prices, curve construction, fitted parameters, and the attempted parameter adjustment, making the failure reproducible in principle. However, the document contains only the question and code; it provides no answer, confirmed cause, or workaround. It therefore illustrates a distinction between fitting a curve and evaluating a specified parameter set, but does not establish whether the issue stems from API semantics, initialization, parameter handling, or a library defect. The report is specific to the described Python and QuantLib setup.
Key ideas
- The author fits a parametric discount curve to bond helpers and obtains a parameter solution.
- The attempted evaluation-only curve uses those parameters with the iteration limit set to zero.
- The fitted curve returns discount factors and bond NPVs, while the second curve crashes when queried.
- The document provides a code example but no diagnosis or confirmed fix.
- The reported behavior may depend on the specific software setup.
Tags
Full text
# Using Python Quantlib's FittedBondDiscountCurve as Evaluator of Parametric Curve - Errors
# Using Python Quantlib's FittedBondDiscountCurve as Evaluator of Parametric Curve - Errors
I am using Quantlib's FittedBondDiscountCurve in Python 3.7 and setting MaxIterations to 0, and giving a guess_solution, which then turns the routine into an evaluator for the parametric form I choose, according to the documentation.
The problem is that while an optimized spline will allow me to produce discount factors and NPVs for bonds, the evaluated spline will only crash (in pycharm) or return no value (in command line).
Am I calling the evaluated curve wrongly? Should I be treating it differently to the optimized curve?
I include some code below. Some is clearly copied from other YC building examples, but seems to be a minimal amount of code to produce the errors. I get the same problem no matter which parametric form I use.
Thank you in advance.
```
import numpy as np
import QuantLib as ql
today = ql.Date(8, 2, 2018)
ql.Settings.instance().evaluationDate = today
# Create some bonds
terminationDates = [ql.Date(4, 7, 2044), ql.Date(15, 2, 2028), ql.Date(14, 4, 2023)]
tenors = np.repeat(ql.Period(ql.Semiannual), 3)
calenders = np.repeat(ql.UnitedStates(), 3)
termDateConvs = np.repeat(ql.Following, 3)
genRules = np.repeat(ql.DateGeneration.Backward, 3)
endOfMonths = np.repeat(False, 3)
firstDates = [ql.Date(27, 4, 2012), ql.Date(10, 1, 2018), ql.Date(2, 2, 2018)]
settlementDays = np.repeat(2, 3)
coupons = [0.025, 0.005, 0.0]
cleanPrices = [126.18, 98.18, 99.73]
faceValues = np.repeat(100.0, 3)
dayCounts = np.repeat(ql.ActualActual(), 3)
schedules = []
bonds = []
bondHelpers = []
for j in range(0, 3):
schedules.append(ql.Schedule(firstDates[j], terminationDates[j], tenors[j], calenders[j],
int(termDateConvs[j]), int(termDateConvs[j]), int(genRules[j]),
bool(endOfMonths[j])))
bonds.append(ql.FixedRateBond(int(settlementDays[j]), float(faceValues[j]), schedules[j],
[float(coupons[j])], dayCounts[j]))
bondHelpers.append(ql.BondHelper(ql.QuoteHandle(ql.SimpleQuote(float(cleanPrices[j]))), bonds[j]))
# Create Two yield Curves- one optimized, one with shifted parameters
curveSettlementDays = 2
curveCalendar = ql.UnitedStates()
curveDaycounter = ql.ActualActual()
curveFittingMethod = ql.ExponentialSplinesFitting()
tolerance = 1.0e-5
iterations = 10000
yieldCurveExp = ql.FittedBondDiscountCurve(curveSettlementDays, curveCalendar,\
bondHelpers, curveDaycounter, curveFittingMethod, tolerance, iterations)
res = yieldCurveExp.fitResults()
solution = list(res.solution())
print('optimal solution = ', solution)
# Altered value
list_guess = solution.copy()
list_guess[-1] = list_guess[-1] * 1.01 # small shifts?
list_guess[-2] = list_guess[-2] * 1.01
list_guess[0] = list_guess[0] * 1.01
print('altered solution = ', list_guess)
guess = ql.Array(list_guess)
iterations = 0 # evaluate, don't fit
yieldCurveExp2 = ql.FittedBondDiscountCurve(curveSettlementDays, curveCalendar, bondHelpers,
curveDaycounter, curveFittingMethod, tolerance, iterations, guess)
YieldCurveHandle = ql.YieldTermStructureHandle(yieldCurveExp)
YieldCurveHandle2 = ql.YieldTermStructureHandle(yieldCurveExp2)
res = yieldCurveExp.fitResults()
print(res.solution()) # optimization
res2 = yieldCurveExp2.fitResults()
print(res2.solution()) # = [ ]
ql_date = ql.Date(30,5,2018)
ql_date2 = ql.Date(30,6,2022)
print(yieldCurveExp.discount(ql_date2)) # 1.0040129
print(yieldCurveExp2.discount(ql_date2)) # Crash
# use it for pricing
bondEngine = ql.DiscountingBondEngine(YieldCurveHandle)
bondEngine2 = ql.DiscountingBondEngine(YieldCurveHandle2)
pricing_bond = bonds[0]
pricing_bond.setPricingEngine(bondEngine)
print('Fit model NPV =', pricing_bond.NPV()) # 137.2513
pricing_bond.setPricingEngine(bondEngine2)
print('Adjusted model NPV =', pricing_bond.NPV()) # Crash!
```Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.