From 9ec841ec9a073516d15ea3aad028fec70af7ca7b Mon Sep 17 00:00:00 2001 From: ArsenP0doba Date: Mon, 4 May 2026 16:50:03 +0200 Subject: [PATCH 1/4] Add NOKNibor, Cubic__* interpolations, LinearThenFlat interpolation, GenericRateHelper, Actual365NoLeap --- QuantLib.vcxproj | 3 + QuantLib.vcxproj.filters | 3 + ql/indexes/ibor/Makefile.am | 1 + ql/indexes/ibor/all.hpp | 1 + ql/indexes/ibor/noknibor.hpp | 57 +++++++++ ql/math/interpolations/Makefile.am | 1 + ql/math/interpolations/all.hpp | 1 + ql/math/interpolations/cubicinterpolation.hpp | 88 +++++++++++++ .../linearthenflatinterpolation.hpp | 120 ++++++++++++++++++ ql/termstructures/yield/ratehelpers.cpp | 20 +++ ql/termstructures/yield/ratehelpers.hpp | 23 ++++ ql/time/daycounters/Makefile.am | 1 + ql/time/daycounters/actual365nl.hpp | 84 ++++++++++++ ql/time/daycounters/all.hpp | 1 + 14 files changed, 404 insertions(+) create mode 100644 ql/indexes/ibor/noknibor.hpp create mode 100644 ql/math/interpolations/linearthenflatinterpolation.hpp create mode 100644 ql/time/daycounters/actual365nl.hpp diff --git a/QuantLib.vcxproj b/QuantLib.vcxproj index 6029f4b6e5a..5416e6760a3 100644 --- a/QuantLib.vcxproj +++ b/QuantLib.vcxproj @@ -831,6 +831,7 @@ + @@ -1048,6 +1049,7 @@ + @@ -1865,6 +1867,7 @@ + diff --git a/QuantLib.vcxproj.filters b/QuantLib.vcxproj.filters index a640b163009..7531de576d5 100644 --- a/QuantLib.vcxproj.filters +++ b/QuantLib.vcxproj.filters @@ -4534,6 +4534,9 @@ + + + diff --git a/ql/indexes/ibor/Makefile.am b/ql/indexes/ibor/Makefile.am index 68b918d7813..055a7d1c53f 100644 --- a/ql/indexes/ibor/Makefile.am +++ b/ql/indexes/ibor/Makefile.am @@ -28,6 +28,7 @@ this_include_HEADERS = \ kofr.hpp \ libor.hpp \ mosprime.hpp \ + noknibor.hpp \ nzdlibor.hpp \ nzocr.hpp \ pribor.hpp \ diff --git a/ql/indexes/ibor/all.hpp b/ql/indexes/ibor/all.hpp index ba4b4facbcb..d941e263665 100644 --- a/ql/indexes/ibor/all.hpp +++ b/ql/indexes/ibor/all.hpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include diff --git a/ql/indexes/ibor/noknibor.hpp b/ql/indexes/ibor/noknibor.hpp new file mode 100644 index 00000000000..6aeaf1ce39c --- /dev/null +++ b/ql/indexes/ibor/noknibor.hpp @@ -0,0 +1,57 @@ +/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* + Copyright (C) 2000, 2001, 2002, 2003 RiskMap srl + Copyright (C) 2003, 2004, 2005, 2006 StatPro Italia srl + + This file is part of QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + + QuantLib is free software: you can redistribute it and/or modify it + under the terms of the QuantLib license. You should have received a + copy of the license along with this program; if not, please email + . The license is also available online at + . + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the license for more details. +*/ + +/*! \file noknibor.hpp + \brief NOK-NIBOR index + \ingroup indexes +*/ + +#ifndef quantext_noknibor_hpp +#define quantext_noknibor_hpp + +#include +#include +#include +#include + +namespace QuantLib { + + //! NOK-NIBOR index + /*! NOK-NIBOR rate published by Oslo Boers. + See . + https://nore-benchmarks.com/ + https://finansfag.no/wp-content/uploads/2024/10/Rentekonvensjon-6.0_engelsk_oppdatert-18.09.2024_final.pdf + https://papers.ssrn.com/sol3/papers.cfm?abstract_id=5099269 + + \remark Using Norway calendar, should be Oslo. + + \warning Check roll convention and EOM. + + \ingroup indexes + */ + class NOKNibor : public IborIndex { + public: + NOKNibor(const Period &tenor, const Handle &h = Handle()) + : IborIndex("NOK-NIBOR", tenor, 2, NOKCurrency(), Norway(), ModifiedFollowing, false, Actual360(), h) {} + }; + +} + +#endif diff --git a/ql/math/interpolations/Makefile.am b/ql/math/interpolations/Makefile.am index 9ba4efe59e1..ff587ed42df 100644 --- a/ql/math/interpolations/Makefile.am +++ b/ql/math/interpolations/Makefile.am @@ -19,6 +19,7 @@ this_include_HEADERS = \ kernelinterpolation2d.hpp \ lagrangeinterpolation.hpp \ linearinterpolation.hpp \ + linearthenflatinterpolation.hpp \ loginterpolation.hpp \ mixedinterpolation.hpp \ multicubicspline.hpp \ diff --git a/ql/math/interpolations/all.hpp b/ql/math/interpolations/all.hpp index 6e05b32fb6c..aeffd7ae234 100644 --- a/ql/math/interpolations/all.hpp +++ b/ql/math/interpolations/all.hpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include diff --git a/ql/math/interpolations/cubicinterpolation.hpp b/ql/math/interpolations/cubicinterpolation.hpp index 04a2195071e..a7a7ae5f60f 100644 --- a/ql/math/interpolations/cubicinterpolation.hpp +++ b/ql/math/interpolations/cubicinterpolation.hpp @@ -815,6 +815,94 @@ namespace QuantLib { } + class Cubic__FritschButland_nonMonotonic : public Cubic { + public: + Cubic__FritschButland_nonMonotonic() + : Cubic(CubicInterpolation::FritschButland, false, + CubicInterpolation::SecondDerivative, 0.0, + CubicInterpolation::SecondDerivative, 0.0) {} + }; + + class Cubic__FritschButland_monotonic : public Cubic { + public: + Cubic__FritschButland_monotonic() + : Cubic(CubicInterpolation::FritschButland, true, + CubicInterpolation::SecondDerivative, 0.0, + CubicInterpolation::SecondDerivative, 0.0) {} + }; + + class Cubic__NaturalSpline : public Cubic { + public: + Cubic__NaturalSpline() + : Cubic(CubicInterpolation::Spline, false, + CubicInterpolation::SecondDerivative, 0.0, + CubicInterpolation::SecondDerivative, 0.0) {} + }; + + class Cubic__Monotonic_NaturalSpline : public Cubic { + public: + Cubic__Monotonic_NaturalSpline() + : Cubic(CubicInterpolation::Spline, true, + CubicInterpolation::SecondDerivative, 0.0, + CubicInterpolation::SecondDerivative, 0.0) {} + }; + + class Cubic__SplineOvershootingMinimization1 : public Cubic { + public: + Cubic__SplineOvershootingMinimization1() + : Cubic(CubicInterpolation::SplineOM1, false, + CubicInterpolation::SecondDerivative, 0.0, + CubicInterpolation::SecondDerivative, 0.0) {} + }; + + class Cubic__SplineOvershootingMinimization2 : public Cubic { + public: + Cubic__SplineOvershootingMinimization2() + : Cubic(CubicInterpolation::SplineOM2, false, + CubicInterpolation::SecondDerivative, 0.0, + CubicInterpolation::SecondDerivative, 0.0) {} + }; + + class Cubic__Akima : public Cubic { + public: + Cubic__Akima() + : Cubic(CubicInterpolation::Akima, false, + CubicInterpolation::SecondDerivative, 0.0, + CubicInterpolation::SecondDerivative, 0.0) {} + }; + + class Cubic__Parabolic : public Cubic { + public: + Cubic__Parabolic() + : Cubic(CubicInterpolation::Parabolic, false, + CubicInterpolation::SecondDerivative, 0.0, + CubicInterpolation::SecondDerivative, 0.0) {} + }; + + class Cubic__MonotonicParabolic : public Cubic { + public: + Cubic__MonotonicParabolic() + : Cubic(CubicInterpolation::Parabolic, true, + CubicInterpolation::SecondDerivative, 0.0, + CubicInterpolation::SecondDerivative, 0.0) {} + }; + + class Cubic__Kruger : public Cubic { + public: + Cubic__Kruger() + : Cubic(CubicInterpolation::Kruger, false, + CubicInterpolation::SecondDerivative, 0.0, + CubicInterpolation::SecondDerivative, 0.0) {} + }; + + class Cubic__MonotonicKruger : public Cubic { + public: + Cubic__MonotonicKruger() + : Cubic(CubicInterpolation::Kruger, true, + CubicInterpolation::SecondDerivative, 0.0, + CubicInterpolation::SecondDerivative, 0.0) {} + }; + } #endif diff --git a/ql/math/interpolations/linearthenflatinterpolation.hpp b/ql/math/interpolations/linearthenflatinterpolation.hpp new file mode 100644 index 00000000000..d3b116bfca5 --- /dev/null +++ b/ql/math/interpolations/linearthenflatinterpolation.hpp @@ -0,0 +1,120 @@ +/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* + Copyright (C) 2002, 2003, 2008, 2009 Ferdinando Ametrano + Copyright (C) 2004, 2007, 2008 StatPro Italia srl + + This file is part of QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + + QuantLib is free software: you can redistribute it and/or modify it + under the terms of the QuantLib license. You should have received a + copy of the license along with this program; if not, please email + . The license is also available online at + . + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the license for more details. +*/ + +/*! \file linearthenflatinterpolation.hpp + \brief linear interpolation between discrete points, that after last point becomes flat +*/ + +#ifndef quantlib_linearthenflat_interpolation_hpp +#define quantlib_linearthenflat_interpolation_hpp + +#include +#include + +namespace QuantLib { + + namespace detail { + template class LinearThenFlatInterpolationImpl; + } + + //! %Linear-then-flat interpolation between discrete points + class LinearThenFlatInterpolation : public Interpolation { + public: + /*! \pre the \f$ x \f$ values must be sorted. */ + template + LinearThenFlatInterpolation(const I1& xBegin, + const I1& xEnd, + const I2& yBegin) { + impl_ = ext::shared_ptr(new + detail::LinearThenFlatInterpolationImpl(xBegin, xEnd, yBegin)); + impl_->update(); + } + }; + + //! %Linear-then-flat interpolation factory and traits + class LinearThenFlat { + public: + template + Interpolation interpolate(const I1& xBegin, + const I1& xEnd, + const I2& yBegin) const { + return LinearThenFlatInterpolation(xBegin, xEnd, yBegin); + } + static const bool global = false; + static const Size requiredPoints = 2; + }; + + namespace detail { + + template + class LinearThenFlatInterpolationImpl : public Interpolation::templateImpl { + public: + LinearThenFlatInterpolationImpl(const I1& xBegin, + const I1& xEnd, + const I2& yBegin) + : Interpolation::templateImpl(xBegin, xEnd, yBegin), + primitiveConst_(xEnd-xBegin), s_(xEnd-xBegin), + primitive_(xEnd-xBegin), n_(xEnd-xBegin) {} + void update() override { + primitiveConst_[0] = 0.0; + for (Size i=1; ixEnd_-this->xBegin_); ++i) { + Real dx = this->xBegin_[i]-this->xBegin_[i-1]; + s_[i-1] = (this->yBegin_[i]-this->yBegin_[i-1])/dx; + primitiveConst_[i] = primitiveConst_[i-1] + + dx*(this->yBegin_[i-1] + 0.5*dx*s_[i-1]); + } + } + Real value(Real x) const override { + if (x >= this->xBegin_[n_-1]) + return this->yBegin_[n_-1]; + Size i = this->locate(x); + return this->yBegin_[i] + (x-this->xBegin_[i])*s_[i]; + } + Real primitive(Real x) const override { + // TODO: fix primitive implementation + if (x >= this->xBegin_[n_-1]) { + Size i = this->locate(x); + Real dx = x-this->xBegin_[i]; + return primitive_[i] + dx*this->yBegin_[i]; + } + Size i = this->locate(x); + Real dx = x-this->xBegin_[i]; + return primitiveConst_[i] + dx*(this->yBegin_[i] + 0.5*dx*s_[i]); + } + Real derivative(Real x) const override { + if (x >= this->xBegin_[n_-1]) + return 0.0; + Size i = this->locate(x); + return s_[i]; + } + Real secondDerivative(Real) const override { + return 0.0; + } + private: + std::vector primitiveConst_, s_; + std::vector primitive_; + Size n_; + }; + + } + +} + +#endif diff --git a/ql/termstructures/yield/ratehelpers.cpp b/ql/termstructures/yield/ratehelpers.cpp index 76637201361..f5fed9c95e6 100644 --- a/ql/termstructures/yield/ratehelpers.cpp +++ b/ql/termstructures/yield/ratehelpers.cpp @@ -836,4 +836,24 @@ namespace QuantLib { RateHelper::accept(v); } + GenericRateHelper::GenericRateHelper(const Handle& rate, + const Date& settlementDate, + const Date& refDate, + const DayCounter& dayCounter, + const Compounding& comp, + const Frequency& freq, + const bool& extrapolate) + : RelativeDateRateHelper(rate), dayCounter_(dayCounter), + compounding_(comp), frequency_(freq), extrapolate_(extrapolate) { + earliestDate_ = settlementDate; + latestDate_ = refDate; + } + + Real GenericRateHelper::impliedQuote() const { + return termStructure_->zeroRate(latestDate_, dayCounter_, + compounding_, frequency_, extrapolate_); + } + + void GenericRateHelper::initializeDates() {} + } diff --git a/ql/termstructures/yield/ratehelpers.hpp b/ql/termstructures/yield/ratehelpers.hpp index 3a242cf3577..97a00998487 100644 --- a/ql/termstructures/yield/ratehelpers.hpp +++ b/ql/termstructures/yield/ratehelpers.hpp @@ -432,6 +432,29 @@ namespace QuantLib { return fwdStart_; } + class GenericRateHelper : public RelativeDateRateHelper { + public: + GenericRateHelper(const Handle& rate, + const Date& settlementDate, + const Date& refDate, + const DayCounter& dayCounter, + const Compounding& comp, + const Frequency& freq, + const bool& extrapolate); + + Real impliedQuote() const override; + + protected: + DayCounter dayCounter_; + private: + Calendar calendar_; + Compounding compounding_; + Frequency frequency_; + bool extrapolate_; + + void initializeDates() override; + }; + } #endif diff --git a/ql/time/daycounters/Makefile.am b/ql/time/daycounters/Makefile.am index 3b73e1f1841..c3ad1866eb3 100644 --- a/ql/time/daycounters/Makefile.am +++ b/ql/time/daycounters/Makefile.am @@ -7,6 +7,7 @@ this_include_HEADERS = \ actual360.hpp \ actual364.hpp \ actual365fixed.hpp \ + actual365nl.hpp \ actual36525.hpp \ actual366.hpp \ actualactual.hpp \ diff --git a/ql/time/daycounters/actual365nl.hpp b/ql/time/daycounters/actual365nl.hpp new file mode 100644 index 00000000000..3cae62ea31a --- /dev/null +++ b/ql/time/daycounters/actual365nl.hpp @@ -0,0 +1,84 @@ +/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* + Copyright (C) 2022 Ignacio Anguita + + This file is part of QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + + QuantLib is free software: you can redistribute it and/or modify it + under the terms of the QuantLib license. You should have received a + copy of the license along with this program; if not, please email + . The license is also available online at + . + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the license for more details. +*/ + +/*! \file actual365nl.hpp + \brief Actual/365 (No Leap) day counter +*/ + +#ifndef quantlib_actual365nl_h +#define quantlib_actual365nl_h + +#include + +namespace QuantLib { + + //! Actual/365 (No Leap) day count convention + /*! "Actual/365 (No Leap)" day count convention, also known as + "Act/365 (NL)", "NL/365", or "Actual/365 (JGB)". + + \ingroup daycounters + */ + class Actual365NoLeap : public DayCounter { + private: + class Impl : public DayCounter::Impl { + public: + std::string name() const { return std::string("Actual/365 (NL)"); } + + // Returns the exact number of days between 2 dates, excluding leap days + Date::serial_type dayCount(const Date& d1, + const Date& d2) const { + + static const Integer MonthOffset[] = { + 0, 31, 59, 90, 120, 151, // Jan - Jun + 181, 212, 243, 273, 304, 334 // Jun - Dec + }; + BigInteger s1, s2; + + s1 = d1.dayOfMonth() + MonthOffset[d1.month()-1] + (d1.year() * 365); + s2 = d2.dayOfMonth() + MonthOffset[d2.month()-1] + (d2.year() * 365); + + if (d1.month() == Feb && d1.dayOfMonth() == 29) + { + --s1; + } + + if (d2.month() == Feb && d2.dayOfMonth() == 29) + { + --s2; + } + + return (Date::serial_type) s2 - s1; + } + + QuantLib::Time yearFraction(const Date& d1, + const Date& d2, + const Date& d3, + const Date& d4) const { + return dayCount(d1, d2)/365.0; + } + }; + public: + Actual365NoLeap() + : DayCounter(boost::shared_ptr( + new Actual365NoLeap::Impl)) {} + }; + +} + +#endif diff --git a/ql/time/daycounters/all.hpp b/ql/time/daycounters/all.hpp index 87a6627b4c9..20750140d69 100644 --- a/ql/time/daycounters/all.hpp +++ b/ql/time/daycounters/all.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include From 72cd52d7b4c51ab9c1c9983cdd406561037cc7f4 Mon Sep 17 00:00:00 2001 From: ArsenP0doba Date: Mon, 4 May 2026 17:05:23 +0200 Subject: [PATCH 2/4] Add in CMakeLists noknibor.hpp, linearthenflatinterpolation.hpp, actual365nl.hpp --- ql/CMakeLists.txt | 3 +++ ql/indexes/ibor/noknibor.hpp | 36 ++++++++++++++++++------------------ 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/ql/CMakeLists.txt b/ql/CMakeLists.txt index 1202b99d1a5..5e96cd7aa11 100644 --- a/ql/CMakeLists.txt +++ b/ql/CMakeLists.txt @@ -1278,6 +1278,7 @@ set(QL_HEADERS indexes/ibor/kofr.hpp indexes/ibor/libor.hpp indexes/ibor/mosprime.hpp + indexes/ibor/noknibor.hpp indexes/ibor/nzdlibor.hpp indexes/ibor/nzocr.hpp indexes/ibor/pribor.hpp @@ -1486,6 +1487,7 @@ set(QL_HEADERS math/interpolations/kernelinterpolation2d.hpp math/interpolations/lagrangeinterpolation.hpp math/interpolations/linearinterpolation.hpp + math/interpolations/linearthenflatinterpolation.hpp math/interpolations/loginterpolation.hpp math/interpolations/mixedinterpolation.hpp math/interpolations/multicubicspline.hpp @@ -2248,6 +2250,7 @@ set(QL_HEADERS time/daycounters/actual360.hpp time/daycounters/actual364.hpp time/daycounters/actual365fixed.hpp + time/daycounters/actual365nl.hpp time/daycounters/actual366.hpp time/daycounters/actual36525.hpp time/daycounters/actualactual.hpp diff --git a/ql/indexes/ibor/noknibor.hpp b/ql/indexes/ibor/noknibor.hpp index 6aeaf1ce39c..a27abd5535f 100644 --- a/ql/indexes/ibor/noknibor.hpp +++ b/ql/indexes/ibor/noknibor.hpp @@ -33,24 +33,24 @@ namespace QuantLib { - //! NOK-NIBOR index - /*! NOK-NIBOR rate published by Oslo Boers. - See . - https://nore-benchmarks.com/ - https://finansfag.no/wp-content/uploads/2024/10/Rentekonvensjon-6.0_engelsk_oppdatert-18.09.2024_final.pdf - https://papers.ssrn.com/sol3/papers.cfm?abstract_id=5099269 - - \remark Using Norway calendar, should be Oslo. - - \warning Check roll convention and EOM. - - \ingroup indexes - */ - class NOKNibor : public IborIndex { - public: - NOKNibor(const Period &tenor, const Handle &h = Handle()) - : IborIndex("NOK-NIBOR", tenor, 2, NOKCurrency(), Norway(), ModifiedFollowing, false, Actual360(), h) {} - }; + //! NOK-NIBOR index + /*! NOK-NIBOR rate published by Oslo Boers. + See . + https://nore-benchmarks.com/ + https://finansfag.no/wp-content/uploads/2024/10/Rentekonvensjon-6.0_engelsk_oppdatert-18.09.2024_final.pdf + https://papers.ssrn.com/sol3/papers.cfm?abstract_id=5099269 + + \remark Using Norway calendar, should be Oslo. + + \warning Check roll convention and EOM. + + \ingroup indexes + */ + class NOKNibor : public IborIndex { + public: + NOKNibor(const Period &tenor, const Handle &h = Handle()) + : IborIndex("NOK-NIBOR", tenor, 2, NOKCurrency(), Norway(), ModifiedFollowing, false, Actual360(), h) {} + }; } From 60130647f62aa047fe7c65ac860173b3b18af936 Mon Sep 17 00:00:00 2001 From: ArsenP0doba Date: Mon, 4 May 2026 18:44:20 +0200 Subject: [PATCH 3/4] Fix a git workflow build error --- ql/time/daycounters/actual365nl.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ql/time/daycounters/actual365nl.hpp b/ql/time/daycounters/actual365nl.hpp index 3cae62ea31a..9527d9b8adc 100644 --- a/ql/time/daycounters/actual365nl.hpp +++ b/ql/time/daycounters/actual365nl.hpp @@ -25,6 +25,7 @@ #define quantlib_actual365nl_h #include +#include namespace QuantLib { @@ -75,7 +76,7 @@ namespace QuantLib { }; public: Actual365NoLeap() - : DayCounter(boost::shared_ptr( + : DayCounter(ext::shared_ptr( new Actual365NoLeap::Impl)) {} }; From 006f886c27cc5ebe768c54090370c53b9a23f640 Mon Sep 17 00:00:00 2001 From: ArsenP0doba Date: Tue, 5 May 2026 08:50:56 +0200 Subject: [PATCH 4/4] Delete ingroup indexes from noknibor.hpp --- ql/indexes/ibor/noknibor.hpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/ql/indexes/ibor/noknibor.hpp b/ql/indexes/ibor/noknibor.hpp index a27abd5535f..149c8d11259 100644 --- a/ql/indexes/ibor/noknibor.hpp +++ b/ql/indexes/ibor/noknibor.hpp @@ -20,7 +20,6 @@ /*! \file noknibor.hpp \brief NOK-NIBOR index - \ingroup indexes */ #ifndef quantext_noknibor_hpp @@ -43,8 +42,6 @@ namespace QuantLib { \remark Using Norway calendar, should be Oslo. \warning Check roll convention and EOM. - - \ingroup indexes */ class NOKNibor : public IborIndex { public: