lballabio / QuantLib

The QuantLib C++ library
http://quantlib.org
Other
5.23k stars 1.78k forks source link

TypeError: in method 'EndCriteria_call_', argument 3 of type 'Size &' #1956

Closed andrzimin72 closed 2 months ago

andrzimin72 commented 5 months ago

I cordially salute the participants of the QuantLib conference. As for me one of the most important tools, in particular in calibration procedures, is an optimizer of a function. For example, my typical problem is: find a model parameter set such that some cost function is minimized. The available optimizers in QuantLib are Levenberg Marquardt methods. To setup up an optimizer, I need to define the end criteria which lead to a successful optimization. They are summarized in the EndCriteria class whose constructor os EndCriteria (StationaryStateIterations). I wrote a small piece of code to сonsider the Vasicek model for standard Brownian motion. With a little bit of bad advice, I find myself facing a much bigger challenge.

model.calibrate(swaptions, optimization_method, end_criteria(10000, 100, 1e-6, 1e-8, 1e-8, 1.0e-8, 1.0e-8, 1.0e-8)) File "pandas1/lib/python3.10/site-packages/QuantLib/QuantLib.py", line 7508, in callreturn _QuantLib.EndCriteria_call_(self, iteration, statState, positiveOptimization, fold, normgold, fnew, normgnewx, ecType) TypeError: in method 'EndCriteriacall__', argument 3 of type 'Size &'

I am new in this field, could you please guide me about this error. And how should I write this argument ‘statState’ of type 'Size &'?

boring-cyborg[bot] commented 5 months ago

Thanks for posting! It might take a while before we look at your issue, so don't worry if there seems to be no feedback. We'll get to it.

lballabio commented 5 months ago

I'm kind of guessing since I don't know how you initialized the various variables, but

model.calibrate(swaptions, optimization_method, end_criteria)

might work. The operator() you're calling is for internal use of the model. I'm not sure it should be exposed to Python.

andrzimin72 commented 5 months ago

Thank you very much for your good advices. May be you a right. But I have read very interesting article about this topic: “The Levenberg-Marquardt Method and its Implementation in Python” submitted by Marius Kaltenbach (Konstanzer Online-Publikations-System (KOPS) URL: http://nbn-resolving.de/urn:nbn:de:bsz:352-2-1ofyba49ud2jr5). So far I have no complaints about this argument ‘statState’. I think the input parameters of this argument are: -maximum iterations: restrict the maximum number of solver iterations; -minimum stationary state iterations: give a minimum number of iterations at stationary point (for both, function value stationarity and root stationarity). I suppose that not all of the end criteria are needed in each optimizer. I haven’t found any argument 3 of type 'Size &' which checks for the optimization. Furthermore, the argument which leads to a stopping of the algorithm is returned. May be it should be checked that this criteria is not of type EndCriteria::None. But I don't know how to calculate argument 3 of type 'Size &' in method 'EndCriteria_call'. And I don't know how to change it.

lballabio commented 5 months ago

You don't have to call the method 'EndCriteriacall'. That method is internal and shouldn't be exposed to Python.

You have already passed the input parameters to the EndCriteria constructor and built end_criteria. You can pass it to the calibrate method by saying model.calibrate(swaptions, optimization_method, end_criteria).

andrzimin72 commented 5 months ago

Thank you very much for your good advices. I corrected the error in the script, so it shouldn't happen again. Apparently there is another problem with the code. model.calibrate(swaptions, optimization_method, end_criteria) File "/pandas1/lib/python3.10/site-packages/QuantLib/QuantLib.py", line 16672, in calibrate return _QuantLib.CalibratedModel_calibrate(self, *args) RuntimeError: wrong argument type I suppose in general there is now be another possibilities to find out where exactly in the code it goes wrong. I have read the instructions https://www.implementingquantlib.com/2013/08/chapter-5-part-4-of-5-models-and.html, https://rkapl123.github.io/QLAnnotatedSource/d7/db6/class_quant_lib_1_1_calibrated_model.html. May be I am again a bit confused here.

lballabio commented 5 months ago

Hard to say what's wrong without seeing the rest of the code. Can you post a script that we can run to reproduce the problem?

andrzimin72 commented 5 months ago

But you're a busy man. Therefore, if I’m already in a difficult situation, I should not waste your time on unnecessary thoughts or waiting for a miracle, which simply can not be. For a long time I assumed that this error was due purely to my own personal bumbling, and not a problem with the method. Anyway I'm trying to answer the question, to articulate my ask. vasicek_calib.zip

lballabio commented 5 months ago

No problem, it took all of 5 minutes once I had code to run. The code below runs the calibration. You were nearly there. The needed changes were:

import numpy as np
import QuantLib as ql
from collections import namedtuple
import math

def create_swaption_helpers(data_, index_, term_structure_, engine_):
    swaptions_ = []
    fixed_leg_tenor = ql.Period(1, ql.Years)
    fixed_leg_daycounter = ql.Actual360()
    floating_leg_daycounter = ql.Actual360()
    for d in data_:
        vol_handle = ql.QuoteHandle(ql.SimpleQuote(d.volatility))
        helper = ql.SwaptionHelper(ql.Period(d.start, ql.Months),
                                   ql.Period(d.length, ql.Months),
                                   vol_handle,
                                   index_,
                                   fixed_leg_tenor,
                                   fixed_leg_daycounter,
                                   floating_leg_daycounter,
                                   term_structure_,
                                   ql.BlackCalibrationHelper.RelativePriceError,
                                   ql.nullDouble(),
                                   1.,
                                   ql.Normal,
                                   0.
                                   )
        helper.setPricingEngine(engine_)
        swaptions_.append(helper)
    return swaptions_

def calibration_report(swaptions_, data_):
    print("-" * 82)
    print("%15s %15s %15s %15s %15s" % \
          ("Model Price", "Market Price", "Implied Vol", "Market Vol", "Rel Error"))
    print("-" * 82)
    cum_err = 0.0
    for i, s in enumerate(swaptions_):
        model_price = s.modelValue()
        market_vol = data_[i].volatility
        black_price = s.blackPrice(market_vol)
        rel_error = model_price / black_price - 1.0
        implied_vol = s.impliedVolatility(model_price,
                                          1e-6, 500, 0.0, 0.50)
        rel_error2 = implied_vol / market_vol - 1.0
        cum_err += rel_error2 * rel_error2

        print("%15.5f %15.5f %15.5f %15.5f %15.5f" % \
              (model_price, black_price, implied_vol, market_vol, rel_error))
    print("-" * 82)
    print("Cumulative Error : %15.5f" % math.sqrt(cum_err))

today = ql.Date().todaysDate()
# ql.Settings.instance().evaluationDate = today
# crv = ql.ZeroCurve([today, settlement], [0.05, 0.05], ql.Actual365Fixed())
crv = ql.FlatForward(today, 0.05, ql.Actual365Fixed())
yts = ql.YieldTermStructureHandle(crv)
vol = ql.QuoteHandle(ql.SimpleQuote(0.1))
model = ql.Vasicek(r0=0.05, a=0.2, b=0.05, sigma=0.1)
# engine = ql.BlackCallableFixedRateBondEngine(vol, yts)
engine = ql.JamshidianSwaptionEngine(model, yts)
index = ql.Euribor1Y(yts)

CalibrationData = namedtuple("CalibrationData", 
                             "start, length, volatility")

data = [
    # CalibrationData(0, 1, 0.1),
    CalibrationData(1, 1, 0.1),
    CalibrationData(2, 1, 0.1),
    CalibrationData(3, 1, 0.1),
    CalibrationData(4, 1, 0.1)
]
swaptions = create_swaption_helpers(data, index, yts, engine)

optimization_method = ql.LevenbergMarquardt(1.0e-8, 1.0e-8, 1.0e-8)
end_criteria = ql.EndCriteria(10000, 100, 1e-6, 1e-8, 1e-8)
model.calibrate(swaptions, optimization_method, end_criteria)

calibration_report(swaptions, data)
a, b, sigma, lam = model.params()
print('%6.5f' % a, '%6.5f' % b, '%6.5f' % sigma, '%6.5f' % lam)
andrzimin72 commented 5 months ago

It's amazing incredible! Excellent attention, you have solved the problem. I’m grateful for your help and support. Also, I am grateful for the good fortune to learn at you, to communicate with the great minds, I'm not afraid of this word, experts in programming and machine learning. May be interest to the independent decision of a problem is the stimulus, driving force of process of knowledge. I hope this information proves helpful to someone in the future. Regards!

github-actions[bot] commented 3 months ago

This issue was automatically marked as stale because it has been open 60 days with no activity. Remove stale label or comment, or this will be closed in two weeks.