1
$\begingroup$

I try to use FittedBondDiscountCurve with NelsonSiegelFitting, but I faced with error when call fitResults() method:

14415     def fitResults(self) -> "FittingMethod const &":
14416         return _QuantLib.FittedBondDiscountCurve_fitResults(self)
14417     __swig_destroy__ = _QuantLib.delete_FittedBondDiscountCurve
14418     __del__ = lambda self: None

RuntimeError: unable to bracket root in 100 function evaluations (last
bracket attempt: f[-2.29538e+025,5.968e+025] -> [-1.#IND,10200.1])

What does this problem mean? What can be solution? Moreover, could you tell me what is a minization method using in "NelsonSiegelFitting"? How can I change it? Or does it use several methods by default? I've uploaded full code with input file to my github.

Import QuanLib

from datetime import datetime, date, time
import QuantLib as ql

class Bond(object):

    def __init__(self, issuer, bond_name, price, tenor, face_amount):
        self.dates = []
        self.cashflows = [] 
        self.issuer = issuer
        self.bond_name = bond_name
        self.price = ql.QuoteHandle(ql.SimpleQuote(price))        
        self.tenor = tenor
        self.face_amount = face_amount

    def add_date(self, date):
        day, month, year = map(int, date.split('.'))
        self.dates.append(ql.Date(day, month, year))

    def add_cashflow(self, cashflow):
        self.cashflows.append(float(cashflow))

Import Data

face_amount = 1000.0 # for all bonds face_amount = 1000.0
tenor = ql.Period(6, ql.Months) # for all bonds tenor = 6m

bonds = {}

with open('bonds.txt') as f:
    next(f) #skip header

for line in f:
    s = line.rstrip().split(';')
    bond_name = s[1]
    if bond_name not in bonds:
        issuer = s[0]        
        price = float(s[4])            
        bonds[bond_name] = Bond(issuer, bond_name, price, tenor, face_amount)

    bonds[bond_name].add_date(s[2])
    bonds[bond_name].add_cashflow(s[3])

Set QuantLib Param

evaluationDate = ql.Date(1, 6, 2016)
ql.Settings.instance().evaluationDate = evaluationDate
calendar = ql.TARGET()
day_counter = ql.Thirty360()
accrualConvention = ql.Unadjusted
bussiness_convention = ql.Unadjusted
bondSettlementDays = 0
curveSettlementDays = 0
bondSettlementDate = calendar.advance(evaluationDate, bondSettlementDays, ql.Days)

Create QuantLib objects

instruments = []
instruments_names = []

for bond in bonds.keys():

    schedule = ql.Schedule(bonds[bond].dates[0] - bonds[bond].tenor, 
                           bonds[bond].dates[-1], 
                           bonds[bond].tenor,
                           calendar,
                           accrualConvention,
                           accrualConvention,
                           ql.DateGeneration.Forward,
                           False)

    helperA = ql.FixedRateBondHelper(bonds[bond].price,
                                     bondSettlementDays,
                                     bonds[bond].face_amount,
                                     schedule,
                                     bonds[bond].cashflows,
                                     day_counter,
                                     bussiness_convention)

    instruments.append(helperA)
    instruments_names.append(bond)

QuantLib Optimization

tolerance = 1.0e-5
iterations = 50000
nelsonSiegel = ql.NelsonSiegelFitting()
term_structure = ql.FittedBondDiscountCurve(curveSettlementDays, 
                             calendar, 
                             instruments,
                             day_counter,
                             nelsonSiegel,
                             tolerance,
                             iterations)
a = term_structure.fitResults()
$\endgroup$
1
  • $\begingroup$ Hi @StudentT! Thank you for your prompt reply. I suppose that my code is too large for post here. I've uploaded to my github. If you have any ideas how to cut code without loss of meaning for posting here, I will grateful to you. $\endgroup$ Commented Jul 20, 2016 at 8:57

1 Answer 1

2
$\begingroup$

There seems to be a problem in the QuantLib code with using a face amount other than 100. If you initialize your bond helpers with face amount = 100 and rescale your price accordingly, the fit succeeds.

I suggest you file an issue at https://github.com/lballabio/quantlib/issues so that this can be fixed. In the meantime, you can rescale your prices during the fit. Once you have the term structure, you can use it to price Bond objects with the correct face amount.

About the minimization method: by default, the simplex algorithm is used. Currently, it's not possible to choose another one from Python (from C++, the chosen optimizer can be passed to the NelsonSiegelFitting constructor).

$\endgroup$
2
  • $\begingroup$ But why only simplex? It is well known that Nelson-Siegel and Nelson-Siegel-Svennson are unstable when the exponential decay rate (lambda or tau) are allowed to vary. QL has Differential Evolution as a possible optimizer. This would be a far better choice. $\endgroup$
    – NBF
    Commented Jan 10, 2020 at 18:10
  • 1
    $\begingroup$ It's probably from a time when differential evolution was not yet available. QuantLib is almost 20 years old, and different parts of the code have wildly different ages. Fortunately, from time to time someone points out where older code can be updated, like in this case. $\endgroup$ Commented Jan 11, 2020 at 9:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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