[Python] Hull White calibration runtime error using Quantlib in python

Discussão em 'Python' iniciado por Stack, Maio 2, 2025.

  1. Stack

    Stack Membro Participativo

    I am refreshing my previous QuantLib code to calibrate HW parameters using Cap. I encountered some error never experienced before. Since Quantlib version 1.28 can work but 1.29 cannot, guess reason might be some function depreciated.


    import QuantLib as ql
    from collections import namedtuple
    import math
    import pandas as pd
    import numpy as np
    from dateutil.relativedelta import relativedelta
    from datetime import datetime

    analysis_date = "2025-03-31"
    freq = 2
    df = pd.read_excel("MarketData_IR - LIBOR.xlsx", index_col=0)
    df = df.loc[[analysis_date], :] / 100
    today = df.index[0].date()
    today = ql.Date(today.day, today.month, today.year)

    df = np.log(df / freq + 1) * freq

    ql.Settings.instance().evaluationDate = today;
    dates = [today + ql.Period(x, ql.Days) for x in [0, 7, 14]] + [today + ql.Period(x, ql.Months) for x in
    [1, 2, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 48,
    60, 72, 84, 96, 108, 120, 144, 180, 240, 300, 360, 363]]
    yields = df.values[0].tolist()
    yields = yields + [yields[-1]]
    day_count = ql.Actual360()
    calendar = ql.UnitedStates(ql.UnitedStates.GovernmentBond)
    interpolation = ql.Linear()
    compounding = ql.Compounded
    compounding_frequency = ql.Continuous

    ts_handle = ql.YieldTermStructureHandle(
    ql.ZeroCurve(dates, yields, day_count, calendar,
    interpolation, compounding, compounding_frequency))

    df_vol = pd.read_excel("MarketData_CapNormalVol.xlsx", index_col=0)
    df_vol = df_vol.loc[[analysis_date], :]
    vols = df_vol.values[0]

    ibor_index = ql.USDLibor(ql.Period(3, ql.Months), ts_handle)

    expiries = [ql.Period(i, ql.Years) for i in range(1, 11)] + [ql.Period(15, ql.Years)] + [ql.Period(20, ql.Years)] + [
    ql.Period(30, ql.Years)]


    def create_cap_helpers(expiries, vols, index, term_structure, engine):
    caps = []
    fixed_leg_tenor = ql.Quarterly
    fixed_leg_daycounter = ql.Actual365Fixed()
    # fixed_leg_daycounter = ql.Actual360()
    for d in range(len(expiries)):
    vol_handle = ql.QuoteHandle(ql.SimpleQuote(vols[d]))
    helper = ql.CapHelper(expiries[d], # Period
    vol_handle,
    index,
    fixed_leg_tenor,
    fixed_leg_daycounter,
    False,
    term_structure,
    # ql.BlackCalibrationHelper.RelativePriceError,
    ql.BlackCalibrationHelper.PriceError,
    # ql.BlackCalibrationHelper.ImpliedVolError,
    ql.Normal,
    # ql.ShiftedLognormal
    )
    helper.setPricingEngine(engine)
    caps.append(helper)

    return caps


    def calibration_report(swaptions, vol, expirie_date):
    print("-" * 60)
    print("%15s %15s %15s %15s" % \
    ("Maturity date", "Model Price", "Market Price", "Rel Error"))
    print("-" * 60)
    cum_err = 0.0
    for i, s in enumerate(swaptions):
    date = expirie_date
    model_price = s.modelValue()
    market_vol = vol
    black_price = s.blackPrice(market_vol)
    rel_error = model_price / black_price - 1.0
    print("%15s %15.5f %15.5f %15.5f" % \
    (date, model_price, black_price, rel_error))


    print("-" * 60)

    model = ql.HullWhite(ts_handle, a=0.001)
    engine = ql.AnalyticCapFloorEngine(model)

    optimization_method = ql.LevenbergMarquardt(1.0e-14, 1.0e-8, 1.0e-8)

    end_criteria = ql.EndCriteria(10000000000, 100000, 1e-8, 1e-8, 1e-8)
    caps = create_cap_helpers(expiries, vols, ibor_index, ts_handle, engine)

    model.calibrate(caps,
    optimization_method,
    end_criteria,
    ql.CompositeConstraint(ql.BoundaryConstraint(0.001, 1.0),
    ql.BoundaryConstraint(0.00001, 1.0)),
    [],
    [False, False]) # First is if fixed alpha, second is sigma

    a, sigma = model.params()
    print("a = %.10f, sigma = %6.9f" % (a, sigma))

    calibration_report(caps, vols, expiries)




    The run time error occurs at model.calibrate(), with error message:

    RuntimeError Traceback (most recent call last)
    ~\AppData\Local\Temp\ipykernel_19944\955511495.py in ?()
    1 period = pd.date_range(start = '4-1-2024' , end = '3-31-2025', freq="D")
    ----> 2 results = pd.DataFrame([calibration(str(i.date())) for i in period]).dropna()

    ~\AppData\Local\Temp\ipykernel_19944\3509880946.py in ?(analysis_date, a, sigma)
    41 # optimization_method = ql.NonLinearLeastSquare()
    42 end_criteria = ql.EndCriteria(1000000000, 1000000, 1e-8, 1e-8, 1e-8)
    43 caps = create_cap_helpers(expiries, vols, ibor_index, ts_handle, engine)
    44
    ---> 45 model.calibrate(caps, optimization_method, end_criteria, ql.CompositeConstraint(ql.BoundaryConstraint(0.001, 1.0),
    46 ql.BoundaryConstraint(0.00001, 1.0)), [], [False, False]) # First is alpha, second sigma
    47
    48 a, sigma = model.params()

    ~\.conda\envs\Quantlib\Lib\site-packages\QuantLib\QuantLib.py in ?(self, *args)
    17197 def calibrate(self, *args):
    17198 r"""calibrate(CalibratedModel self, CalibrationHelperVector arg2, OptimizationMethod arg3, EndCriteria arg4, Constraint constraint=Constraint(), DoubleVector weights=std::vector< Real >(), BoolVector fixParameters=std::vector< bool >())"""
    > 17199 return _QuantLib.CalibratedModel_calibrate(self, *args)

    RuntimeError: null number of evaluations

    Continue reading...

Compartilhe esta Página