0
$\begingroup$

I use QuantLib Python to calibrate a curve based on interpolated discount factors (https://github.com/lballabio/QuantLib-SWIG/blob/master/SWIG/discountcurve.i). Using LogLinear interpolation on discount factors results in well-behaved forward rates:

enter image description here

(black line is zero rates, dotted line is 1d-tenor forward curve)

However, using MonotonicLogCubic or SplineCubic, my forward rates are not well behaved - in the end-point, the forwards are 0:

enter image description here

Now I understand that using these interpolation methods, second derivatives in end points are set to zero, however this shouldn't necessarily mean that forwards are zero in the end points. What gives? How to overcome this issue?

These are my input DFs:

February 27th, 2020: 1.0000102501050636
February 28th, 2020: 1.0000205003151919
March 10th, 2020: 1.000135036504439
March 17th, 2020: 1.0002140481753308
April 1st, 2020: 1.0003903064472135
April 30th, 2020: 1.0007383708059454
June 1st, 2020: 1.0011864659905814
July 1st, 2020: 1.001606452340883
July 30th, 2020: 1.0020206702099752
September 1st, 2020: 1.0025224673306345
September 30th, 2020: 1.003007005031276
October 30th, 2020: 1.0034783631568704
December 2nd, 2020: 1.0040034616057292
December 30th, 2020: 1.0045093968882473
February 1st, 2021: 1.0050604130179728
March 3rd, 2021: 1.0055474482943993
September 1st, 2021: 1.0087481016987652
March 2nd, 2022: 1.01191304422858
August 31st, 2022: 1.0148211625629888
March 2nd, 2023: 1.0174479377825414
March 1st, 2024: 1.0215623647797287
March 4th, 2025: 1.023909696966405
March 4th, 2026: 1.0242610376714252
March 3rd, 2027: 1.02262864674708
March 1st, 2028: 1.0191710493588495
March 2nd, 2029: 1.013740853380904
March 4th, 2030: 1.0061949692591952
March 4th, 2031: 0.998108130543049
March 3rd, 2032: 0.9884922702699311
March 2nd, 2035: 0.961448481843742
March 1st, 2040: 0.9138611360550781
March 2nd, 2045: 0.8771539172898629
March 2nd, 2050: 0.8489994312646763

And I construct the instance as such:

ql.NaturalCubicDiscountCurve(dates, discountfactors, ql.Actual365Fixed(),ql.UnitedStates())

where dates and discountfactors are lists based on the above.

To get the forwards, I call forwardRate(date, date+1, ql.Actual360(), ql.Simple()).rate() on the discount curve

$\endgroup$
  • $\begingroup$ what is your extrapolation method? $\endgroup$ – Valometrics.com Feb 26 at 20:54
  • $\begingroup$ Would be easier to help if you post your inputs $\endgroup$ – David Duarte Feb 27 at 0:02
  • $\begingroup$ @Valometrics.com as far as I can tell from the interface, I cannot enable extrapolation on a DiscountCurve instance. I am able to call the enableExtrapolation() method, but this does not alter the result. $\endgroup$ – Physcs Envy Feb 27 at 9:19
2
$\begingroup$

I can't seem to replicate your problem...

import pandas as pd
import QuantLib as ql
import matplotlib.pyplot as plt

dates =  [
    '2020-02-26',  '2020-02-27',  '2020-02-28',  '2020-03-10',  '2020-03-17',  '2020-04-01',  '2020-04-30',  '2020-06-01',  '2020-07-01',  '2020-07-30',  '2020-09-01',
    '2020-09-30',  '2020-10-30',  '2020-12-01',  '2020-12-30',  '2021-02-01',  '2021-03-01',  '2021-09-01',  '2022-03-01',  '2022-08-31',  '2023-03-01',  '2024-03-01',
    '2025-03-01',  '2026-03-01',  '2027-03-01',  '2028-03-01',  '2029-03-01',  '2030-03-01',  '2031-03-01',  '2032-03-01',  '2035-03-01',  '2040-03-01',  '2045-03-01',
    '2050-03-01']

dfs = [1,  1.0000102501050636,  1.000020500315192,  1.000135036504439,  1.0002140481753308,  1.0003903064472135,  1.0007383708059454,  1.0011864659905814,  1.001606452340883,
 1.0020206702099752,  1.0025224673306343,  1.0030070050312758,  1.0034783631568704,  1.0040034616057292,  1.0045093968882473,  1.0050604130179728,  1.0055474482943991,
 1.0087481016987652, 1.01191304422858, 1.0148211625629888, 1.0174479377825414, 1.0215623647797287, 1.023909696966405, 1.0242610376714252, 1.02262864674708, 1.0191710493588495,
 1.013740853380904, 1.0061949692591952, 0.9981081305430491, 0.9884922702699313, 0.9614484818437421, 0.913861136055078, 0.8771539172898629, 0.8489994312646763]

qlDates = [ql.Date(dt, '%Y-%m-%d') for dt in dates]

params = [qlDates, dfs, ql.Actual365Fixed(),ql.UnitedStates()]
curves = {
    'DiscountCurve': ql.DiscountCurve(*params),
    'NaturalCubicDiscountCurve': ql.NaturalCubicDiscountCurve(*params),
    'MonotonicLogCubicDiscountCurve': ql.MonotonicLogCubicDiscountCurve(*params)   
}
plt.figure(figsize=(10,5))
for key in curves:
    crv = curves[key]
    crv.enableExtrapolation()
    times = crv.times()
    zeros = [crv.zeroRate(date, ql.Actual365Fixed(), ql.Continuous).rate() for date in crv.dates()]
    plt.plot(times, zeros, label=f"Spot {key}")
    fwds = [crv.forwardRate(date, date + ql.Period('1d'), ql.Actual360(), ql.Simple).rate() for date in crv.dates()]
    plt.plot(times, fwds, label=f"Fwd {key}")

plt.legend();

Which would result in :

enter image description here

| improve this answer | |
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.