From ad80c87daa70383ec093bba27a53b8fe0031cfff Mon Sep 17 00:00:00 2001
From: Auto Differentiation Dev Team
<107129969+auto-differentiation-dev@users.noreply.github.com>
Date: Fri, 30 Oct 2026 15:06:25 +0000
Subject: [PATCH 01/15] deps: xad 2.2 from PyPI
The wheels build and test against xad>=2.2,<2.3 from PyPI instead of the
xad-py git pin (9ca1797): for build, runtime and the QuantLib build in
before-all. The before-build, build-frontend and before-test overrides and
the sdist job's non-isolated build were only there for the git pin, so
wheels and sdist go back to isolated builds.
---
.github/workflows/quantlib-risks.yml | 6 +-----
pyproject.toml | 11 +++--------
2 files changed, 4 insertions(+), 13 deletions(-)
diff --git a/.github/workflows/quantlib-risks.yml b/.github/workflows/quantlib-risks.yml
index 5fd8471c..afd39f8d 100644
--- a/.github/workflows/quantlib-risks.yml
+++ b/.github/workflows/quantlib-risks.yml
@@ -100,11 +100,7 @@ jobs:
submodules: recursive
persist-credentials: false
- name: Build sdist
- # no isolation until xad 2.2 is on PyPI
- run: |
- python3 -m venv /tmp/sdist-env
- /tmp/sdist-env/bin/pip install build "scikit-build-core>=0.11"
- /tmp/sdist-env/bin/python -m build --sdist --no-isolation --skip-dependency-check
+ run: pipx run build --sdist
- name: Check metadata
run: pipx run twine check --strict dist/*
- name: Upload sdist
diff --git a/pyproject.toml b/pyproject.toml
index d5bddf80..bc2ac75f 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -25,7 +25,7 @@
[build-system]
# swig 4.5 drops the PyInt_* macros used in SWIG/
-requires = ["scikit-build-core>=0.11", "swig>=4.4,<4.6", "xad>=2.1,<2.3"]
+requires = ["scikit-build-core>=0.11", "swig>=4.4,<4.6", "xad>=2.2,<2.3"]
build-backend = "scikit_build_core.build"
[project]
@@ -71,7 +71,7 @@ classifiers = [
"Topic :: Software Development",
]
# the binary module links xad's libxad and shares its Real type (same pybind11)
-dependencies = ["xad>=2.1,<2.3"]
+dependencies = ["xad>=2.2,<2.3"]
[project.urls]
Homepage = "https://auto-differentiation.github.io"
@@ -112,14 +112,9 @@ build-verbosity = 1
# QuantLib (with QuantLibAAD) is built once per platform and reused by all
# wheels through QLR_QUANTLIB_PREFIX, against the XAD of a xad package that
# build-quantlib.cmake installs (XAD_REQUIREMENT)
-before-all = "cmake \"-DXAD_REQUIREMENT=xad @ git+https://github.com/auto-differentiation/xad-py.git@9ca179764907ba44038efd1beb3631d35a5e22ea\" -DPREFIX={project}/build/quantlib-prefix -P {project}/tools/build-quantlib.cmake"
+before-all = "cmake \"-DXAD_REQUIREMENT=xad>=2.2,<2.3\" -DPREFIX={project}/build/quantlib-prefix -P {project}/tools/build-quantlib.cmake"
config-settings = { "cmake.define.QLR_QUANTLIB_PREFIX" = "{project}/build/quantlib-prefix" }
test-groups = ["test"]
-# until xad 2.2 is on PyPI (then also xad>=2.2): xad-py from git (then drop before-build, build-frontend,
-# before-test and XAD_REQUIREMENT)
-before-build = "pip install \"scikit-build-core>=0.11\" \"swig>=4.4,<4.6\" cmake ninja \"xad @ git+https://github.com/auto-differentiation/xad-py.git@9ca179764907ba44038efd1beb3631d35a5e22ea\""
-build-frontend = { name = "build", args = ["--no-isolation"] }
-before-test = "pip install \"xad @ git+https://github.com/auto-differentiation/xad-py.git@9ca179764907ba44038efd1beb3631d35a5e22ea\""
# importlib mode tests the installed package; Python/test would clash with the stdlib test package
test-command = "pytest -p no:cacheprovider --import-mode=importlib {project}/Python/test"
From 49a0fde25d02fecd01a96eeb35e1ea8bd655c429 Mon Sep 17 00:00:00 2001
From: Auto Differentiation Dev Team
<107129969+auto-differentiation-dev@users.noreply.github.com>
Date: Thu, 12 Nov 2026 15:23:11 +0000
Subject: [PATCH 02/15] deps: QuantLib 1.44 with two temporary AD fixes,
QuantLibAAD main
patches/quantlib holds QuantLib PR #2934 (library part) and PR #3057, backported to 1.44
and applied before QuantLib is configured; both go with QuantLib 1.45.
---
cmake/QuantLibFromSource.cmake | 4 +-
cmake/QuantLibPatches.cmake | 58 ++++++++
lib/QuantLib | 2 +-
lib/QuantLibAAD | 2 +-
...on-with-expression-template-ad-types.patch | 124 ++++++++++++++++++
...rate-derivative-at-compound-factor-1.patch | 18 +++
6 files changed, 205 insertions(+), 3 deletions(-)
create mode 100644 cmake/QuantLibPatches.cmake
create mode 100644 patches/quantlib/0001-fix-compilation-with-expression-template-ad-types.patch
create mode 100644 patches/quantlib/0002-keep-implied-rate-derivative-at-compound-factor-1.patch
diff --git a/cmake/QuantLibFromSource.cmake b/cmake/QuantLibFromSource.cmake
index 87ababab..051d2a9e 100644
--- a/cmake/QuantLibFromSource.cmake
+++ b/cmake/QuantLibFromSource.cmake
@@ -22,7 +22,9 @@ if(MSVC AND CMAKE_GENERATOR MATCHES "Visual Studio")
add_compile_options(/MP) # parallel compilation within the QuantLib project
endif()
-add_subdirectory("${QLR_LIB_DIR}/QuantLib" "${CMAKE_BINARY_DIR}/QuantLib")
+set(QUANTLIB_SOURCE_DIR "${QLR_LIB_DIR}/QuantLib")
+include(QuantLibPatches)
+add_subdirectory("${QUANTLIB_SOURCE_DIR}" "${CMAKE_BINARY_DIR}/QuantLib")
# QuantLibAAD always adds its examples (which need a newer QuantLib) and test-suite
foreach(dir Examples test-suite)
if(EXISTS "${QLR_LIB_DIR}/QuantLibAAD/${dir}/CMakeLists.txt")
diff --git a/cmake/QuantLibPatches.cmake b/cmake/QuantLibPatches.cmake
new file mode 100644
index 00000000..08084005
--- /dev/null
+++ b/cmake/QuantLibPatches.cmake
@@ -0,0 +1,58 @@
+##############################################################################
+# Applies the temporary QuantLib fixes in patches/quantlib to the QuantLib
+# source in QUANTLIB_SOURCE_DIR, before it is configured. A patch already
+# applied is skipped; one that doesn't fit stops the build. Included by
+# QuantLibFromSource, or run alone:
+#
+# cmake -DQUANTLIB_SOURCE_DIR=
-P cmake/QuantLibPatches.cmake
+#
+# This file is part of QuantLibAAD, a Python wrapper for QuantLib enabled
+# for risk computation using automatic differentiation. It uses XAD,
+# a fast and comprehensive C++ library for automatic differentiation.
+#
+# Copyright (C) 2010-2026 Xcelerit Computing Ltd.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# 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
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+##############################################################################
+
+if(NOT QUANTLIB_SOURCE_DIR)
+ message(FATAL_ERROR "QUANTLIB_SOURCE_DIR is not set")
+endif()
+find_package(Git REQUIRED)
+file(GLOB qlr_patches "${CMAKE_CURRENT_LIST_DIR}/../patches/quantlib/*.patch")
+list(SORT qlr_patches)
+
+# git apply works relative to the enclosing repository: stop it from looking above the
+# QuantLib source (e.g. a source tree unpacked inside another checkout)
+get_filename_component(qlr_ceiling "${QUANTLIB_SOURCE_DIR}" DIRECTORY)
+set(qlr_git ${CMAKE_COMMAND} -E env "GIT_CEILING_DIRECTORIES=${qlr_ceiling}" "${GIT_EXECUTABLE}")
+
+foreach(patch IN LISTS qlr_patches)
+ get_filename_component(name "${patch}" NAME)
+ execute_process(COMMAND ${qlr_git} apply --check --reverse "${patch}"
+ WORKING_DIRECTORY "${QUANTLIB_SOURCE_DIR}"
+ RESULT_VARIABLE already_applied OUTPUT_QUIET ERROR_QUIET)
+ if(already_applied EQUAL 0)
+ message(STATUS "QuantLib patch already applied: ${name}")
+ continue()
+ endif()
+ execute_process(COMMAND ${qlr_git} apply "${patch}"
+ WORKING_DIRECTORY "${QUANTLIB_SOURCE_DIR}"
+ RESULT_VARIABLE rc ERROR_VARIABLE err)
+ if(NOT rc EQUAL 0)
+ message(FATAL_ERROR "QuantLib patch ${name} does not apply to ${QUANTLIB_SOURCE_DIR}:\n${err}")
+ endif()
+ message(STATUS "QuantLib patch applied: ${name}")
+endforeach()
diff --git a/lib/QuantLib b/lib/QuantLib
index 367ce80a..ab4cb476 160000
--- a/lib/QuantLib
+++ b/lib/QuantLib
@@ -1 +1 @@
-Subproject commit 367ce80ae4f285835ceb3bba97746bea239984ad
+Subproject commit ab4cb476d35f7275834bc05b47d1324403d1823b
diff --git a/lib/QuantLibAAD b/lib/QuantLibAAD
index 74a892e5..e78fc676 160000
--- a/lib/QuantLibAAD
+++ b/lib/QuantLibAAD
@@ -1 +1 @@
-Subproject commit 74a892e528db7bbb50d587c268a687a3a8fb032e
+Subproject commit e78fc676c192492b75f0277af9b9d8b2818feb1a
diff --git a/patches/quantlib/0001-fix-compilation-with-expression-template-ad-types.patch b/patches/quantlib/0001-fix-compilation-with-expression-template-ad-types.patch
new file mode 100644
index 00000000..721ebf7d
--- /dev/null
+++ b/patches/quantlib/0001-fix-compilation-with-expression-template-ad-types.patch
@@ -0,0 +1,124 @@
+Temporary: library part of QuantLib PR #2934 (Fix compilation with expression-template AD types), backported to QuantLib 1.44. Dropped with QuantLib 1.45.
+
+diff --git a/ql/math/distributions/normaldistribution.hpp b/ql/math/distributions/normaldistribution.hpp
+index ea029a536..10978de39 100644
+--- a/ql/math/distributions/normaldistribution.hpp
++++ b/ql/math/distributions/normaldistribution.hpp
+@@ -308,8 +308,8 @@ namespace QuantLib {
+
+ inline Real NormalDistribution::derivative(Real x) const {
+ const Real density = (*this)(x);
+- return density == 0.0 ? 0.0 :
+- (density * (average_ - x)) / derNormalizationFactor_;
++ return density == 0.0 ? Real(0.0) :
++ Real((density * (average_ - x)) / derNormalizationFactor_);
+ }
+
+ inline CumulativeNormalDistribution::CumulativeNormalDistribution(
+diff --git a/ql/math/functional.hpp b/ql/math/functional.hpp
+index b95547743..0bb60fdc5 100644
+--- a/ql/math/functional.hpp
++++ b/ql/math/functional.hpp
+@@ -33,8 +33,9 @@ namespace QuantLib {
+
+ // functions
+
++ // auto return type keeps expression-template types (e.g. AD) intact
+ template
+- inline T squared(T x) { return x * x; }
++ inline auto squared(const T& x) { return x * x; }
+
+ }
+
+diff --git a/ql/methods/finitedifferences/utilities/smilesectionrndcalculator.cpp b/ql/methods/finitedifferences/utilities/smilesectionrndcalculator.cpp
+index 251b1a97b..da2a51335 100644
+--- a/ql/methods/finitedifferences/utilities/smilesectionrndcalculator.cpp
++++ b/ql/methods/finitedifferences/utilities/smilesectionrndcalculator.cpp
+@@ -114,7 +114,7 @@ namespace QuantLib {
+ // Finer than the tabulated map can resolve.
+ const Real xTol = 1e-12;
+
+- const auto edge = [&](Real target) {
++ const auto edge = [&](Real target) -> Real {
+ Real x = 0.0;
+ Real step = firstStep;
+ const Real direction = target > zAtm ? 1.0 : -1.0;
+diff --git a/ql/pricingengines/vanilla/bjerksundstenslandengine.cpp b/ql/pricingengines/vanilla/bjerksundstenslandengine.cpp
+index b7e7d8f72..4757a45ca 100644
+--- a/ql/pricingengines/vanilla/bjerksundstenslandengine.cpp
++++ b/ql/pricingengines/vanilla/bjerksundstenslandengine.cpp
+@@ -200,8 +200,8 @@ namespace QuantLib {
+ const bool infiniteBoundary = std::fabs(beta - 1.0) < 1e-8;
+ const Real a = (bT + 2.0*std::sqrt(variance)) * B0;
+ const Real I = infiniteBoundary
+- ? B0 + a
+- : B0 + (BInfinity - B0) * (1 - std::exp(ht));
++ ? Real(B0 + a)
++ : Real(B0 + (BInfinity - B0) * (1 - std::exp(ht)));
+
+ const Real fwd = S * dD/rfD;
+ const Real q = std::log(I/fwd)/std::sqrt(variance);
+@@ -262,10 +262,10 @@ namespace QuantLib {
+ *(B0Dq*(BInfinity - B0) - B0*(BInfinityDq - B0Dq))
+ /squared(BInfinity - B0);
+ const Real IDq = infiniteBoundary
+- ? B0Dq - tq*B0 + (bT + 2.0*std::sqrt(variance))*B0Dq
+- - squared(a)*betaDq/(2*X)
+- : B0Dq + (BInfinityDq - B0Dq) * (1 - std::exp(ht))
+- - (BInfinity - B0) * std::exp(ht)*htDq;
++ ? Real(B0Dq - tq*B0 + (bT + 2.0*std::sqrt(variance))*B0Dq
++ - squared(a)*betaDq/(2*X))
++ : Real(B0Dq + (BInfinityDq - B0Dq) * (1 - std::exp(ht))
++ - (BInfinity - B0) * std::exp(ht)*htDq);
+
+ const Real phi_H_S_beta_I_I_rT_bT_v
+ = phi_H(S, beta, I, I, rT, bT, variance);
+@@ -329,10 +329,10 @@ namespace QuantLib {
+ *(B0Dr*(BInfinity - B0) - B0*(BInfinityDr - B0Dr))
+ /squared(BInfinity - B0);
+ const Real IDr = infiniteBoundary
+- ? B0Dr + tr*B0 + (bT + 2.0*std::sqrt(variance))*B0Dr
+- - squared(a)*betaDr/(2*X)
+- : B0Dr + (BInfinityDr - B0Dr) * (1 - std::exp(ht))
+- - (BInfinity - B0) * std::exp(ht)*htDr;
++ ? Real(B0Dr + tr*B0 + (bT + 2.0*std::sqrt(variance))*B0Dr
++ - squared(a)*betaDr/(2*X))
++ : Real(B0Dr + (BInfinityDr - B0Dr) * (1 - std::exp(ht))
++ - (BInfinity - B0) * std::exp(ht)*htDr);
+
+ results.rho =
+ (IDr*std::pow(S/I, beta)
+@@ -375,9 +375,9 @@ namespace QuantLib {
+ + (bT + 2*std::sqrt(variance))*B0/squared(BInfinity-B0)*BInfinityDv;
+
+ const Real IDv = infiniteBoundary
+- ? varianceDv/std::sqrt(variance)*B0 - squared(a)*betaDv/(2*X)
+- : BInfinityDv*(1-std::exp(ht))
+- - (BInfinity-B0)*std::exp(ht)*htDv;
++ ? Real(varianceDv/std::sqrt(variance)*B0 - squared(a)*betaDv/(2*X))
++ : Real(BInfinityDv*(1-std::exp(ht))
++ - (BInfinity-B0)*std::exp(ht)*htDv);
+
+ results.vega =
+ (IDv*std::pow(S/I, beta)
+diff --git a/ql/processes/hestonprocess.cpp b/ql/processes/hestonprocess.cpp
+index 810c94849..3538c22fb 100644
+--- a/ql/processes/hestonprocess.cpp
++++ b/ql/processes/hestonprocess.cpp
+@@ -267,14 +267,14 @@ namespace QuantLib {
+ {
+ const Real h = 0.05;
+
+- Real si = ExponentialIntegral::Si(0.5*h*x);
++ Real si = ExponentialIntegral::Si(Real(0.5*h*x));
+ Real s = M_2_PI*si;
+ std::complex f;
+ Size j = 0;
+ do {
+ ++j;
+ const Real u = h*j;
+- const Real si_n = ExponentialIntegral::Si(x*(u+0.5*h));
++ const Real si_n = ExponentialIntegral::Si(Real(x*(u+0.5*h)));
+
+ f = Phi(process, u, nu_0, nu_t, dt);
+ s+= M_2_PI*f.real()*(si_n-si);
diff --git a/patches/quantlib/0002-keep-implied-rate-derivative-at-compound-factor-1.patch b/patches/quantlib/0002-keep-implied-rate-derivative-at-compound-factor-1.patch
new file mode 100644
index 00000000..13470517
--- /dev/null
+++ b/patches/quantlib/0002-keep-implied-rate-derivative-at-compound-factor-1.patch
@@ -0,0 +1,18 @@
+Temporary: QuantLib PR #3057 (Keep the derivative of an implied rate when the compound factor is 1), backported to QuantLib 1.44. Dropped with QuantLib 1.45.
+
+diff --git a/ql/interestrate.cpp b/ql/interestrate.cpp
+index 83898640d..6ef9efa54 100644
+--- a/ql/interestrate.cpp
++++ b/ql/interestrate.cpp
+@@ -109,8 +109,9 @@ namespace QuantLib {
+ QL_REQUIRE(compound>0.0, "positive compound factor required");
+
+ Rate r = Null();
+- if (compound==1.0) {
+- QL_REQUIRE(t>=0.0, "non negative time (" << t << ") required");
++ // only at t == 0: for t > 0 the formulas below give 0 as well, and keep the
++ // dependence on the inputs that automatic differentiation needs
++ if (compound==1.0 && t==0.0) {
+ r = 0.0;
+ } else {
+ QL_REQUIRE(t>0.0, "positive time (" << t << ") required");
From aca89d1bbaceed41390aef46c0c6299c81b8df2a Mon Sep 17 00:00:00 2001
From: Auto Differentiation Dev Team
<107129969+auto-differentiation-dev@users.noreply.github.com>
Date: Thu, 12 Nov 2026 15:36:21 +0000
Subject: [PATCH 03/15] SWIG: sync with QuantLib-SWIG 1.44
New upstream interfaces fxforward.i, multicurve.i and varianceswaps.i; spreadoption.i is gone
upstream. XAD changes carried over to the new ArrayFunction and GlobalBootstrap code.
---
LICENSE.md | 13 +-
SWIG/barrieroptions.i | 3 +
SWIG/basketoptions.i | 25 ++
SWIG/bondfunctions.i | 21 ++
SWIG/bonds.i | 5 +-
SWIG/calendars.i | 71 +++-
SWIG/calibrationhelpers.i | 9 +-
SWIG/cashflows.i | 382 ++++++++++++++-------
SWIG/common.i | 35 +-
SWIG/creditdefaultswap.i | 53 ++-
SWIG/currencies.i | 6 +
SWIG/date.i | 39 ++-
SWIG/defaultprobability.i | 18 +-
SWIG/discountcurve.i | 23 ++
SWIG/fdm.i | 34 +-
SWIG/forwardcurve.i | 7 +
SWIG/functions.i | 28 +-
SWIG/fxforward.i | 62 ++++
SWIG/gaussian1dmodel.i | 7 +
SWIG/indexes.i | 63 ++--
SWIG/inflation.i | 94 ++----
SWIG/instruments.i | 2 +
SWIG/interpolation.i | 267 +++++++++------
SWIG/lazyobject.i | 1 +
SWIG/linearalgebra.i | 2 +-
SWIG/lmm.i | 7 +
SWIG/marketelements.i | 19 ++
SWIG/multicurve.i | 54 +++
SWIG/observer.i | 12 +
SWIG/old_volatility.i | 7 +-
SWIG/optimizers.i | 42 ++-
SWIG/options.i | 120 ++++---
SWIG/patches/creditdefaultswap.i.patch | 2 +-
SWIG/patches/date.i.patch | 4 +-
SWIG/patches/fdm.i.patch | 12 +-
SWIG/patches/functions.i.patch | 10 +-
SWIG/patches/inflation.i.patch | 15 -
SWIG/patches/options.i.patch | 2 +-
SWIG/patches/piecewiseyieldcurve.i.patch | 26 +-
SWIG/patches/ql.i.patch | 2 +-
SWIG/piecewiseyieldcurve.i | 142 ++++++--
SWIG/ql.i | 9 +-
SWIG/ratehelpers.i | 335 +++++++++++++++----
SWIG/scheduler.i | 14 +-
SWIG/settings.i | 8 +-
SWIG/spreaddiscountcurve.i | 8 +
SWIG/spreadoption.i | 43 ---
SWIG/stochasticprocess.i | 16 +-
SWIG/swap.i | 407 ++++++++++++++++++++++-
SWIG/swaption.i | 22 +-
SWIG/termstructures.i | 13 +-
SWIG/varianceswaps.i | 126 +++++++
SWIG/volatilities.i | 268 ++++++++++++---
SWIG/zerocurve.i | 62 +++-
54 files changed, 2378 insertions(+), 699 deletions(-)
create mode 100644 SWIG/fxforward.i
create mode 100644 SWIG/multicurve.i
delete mode 100644 SWIG/patches/inflation.i.patch
delete mode 100644 SWIG/spreadoption.i
create mode 100644 SWIG/varianceswaps.i
diff --git a/LICENSE.md b/LICENSE.md
index d65553d8..36040002 100644
--- a/LICENSE.md
+++ b/LICENSE.md
@@ -680,17 +680,17 @@ QuantLib-SWIG is
Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2014, 2020, 2022, 2023 StatPro Italia srl
Copyright (C) 2005 Dominic Thuillier
Copyright (C) 2005 Johan Witters
- Copyright (C) 2006, 2007, 2010 Joseph Wang
Copyright (C) 2007 Eric H. Jensen
Copyright (C) 2007 Luis Cota
Copyright (C) 2007 Richard Gomes
Copyright (C) 2007, 2008 Tito Ingargiola
+ Copyright (C) 2007, 2010 Joseph Wang
Copyright (C) 2008 Allen Kuo
Copyright (C) 2008 Florent Grenier
Copyright (C) 2009 Joseph Malicki
Copyright (C) 2010 Andrea Odetti
Copyright (C) 2010, 2011 Lluis Pujol Bajador
- Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2018, 2019, 2020, 2021, 2022, 2023 Klaus Spanderen
+ Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2018, 2019, 2020, 2021, 2022, 2023, 2024 Klaus Spanderen
Copyright (C) 2011, 2012 Tawanda Gwena
Copyright (C) 2012 Francis Duffy
Copyright (C) 2013 Simon Shakeshaft
@@ -710,10 +710,15 @@ QuantLib-SWIG is
Copyright (C) 2020 Gorazd Brumen
Copyright (C) 2020, 2021, 2022 Jack Gillett
Copyright (C) 2020, 2021, 2023 Marcin Rybacki
- Copyright (C) 2021 Ralf Konrad Eckel
+ Copyright (C) 2021, 2024 Ralf Konrad Eckel
Copyright (C) 2022 Ignacio Anguita
- Copyright (C) 2022, 2023 Skandinaviska Enskilda Banken AB (publ)
+ Copyright (C) 2022, 2023, 2024 Skandinaviska Enskilda Banken AB (publ)
Copyright (C) 2023 Francois Botha
+ Copyright (C) 2025 Hiroto Ogawa
+ Copyright (C) 2026 Arihant Lodha
+ Copyright (C) 2026 Kyrylo Protsenko
+ Copyright (C) 2026 Lawrenz Law
+ Copyright (C) 2026 Mahimn Patel
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
diff --git a/SWIG/barrieroptions.i b/SWIG/barrieroptions.i
index 30644b5b..b81bbd74 100644
--- a/SWIG/barrieroptions.i
+++ b/SWIG/barrieroptions.i
@@ -333,6 +333,9 @@ If max_steps is specified, it would limit binomial steps to this value.
template
class BinomialBarrierEngine : public PricingEngine {
+ #if !defined(SWIGJAVA) && !defined(SWIGCSHARP)
+ %feature("kwargs") BinomialBarrierEngine;
+ #endif
public:
BinomialBarrierEngine(const ext::shared_ptr&,
Size steps,
diff --git a/SWIG/basketoptions.i b/SWIG/basketoptions.i
index d3139ec5..7e47481d 100644
--- a/SWIG/basketoptions.i
+++ b/SWIG/basketoptions.i
@@ -4,6 +4,7 @@
Copyright (C) 2005 Dominic Thuillier
Copyright (C) 2007 Joseph Wang
Copyright (C) 2018, 2019 Matthias Lungwitz
+ Copyright (C) 2020 Gorazd Brumen
Copyright (C) 2024 Klaus Spanderen
This file is part of QuantLib, a free-software/open-source library
@@ -236,6 +237,8 @@ using QuantLib::KirkEngine;
using QuantLib::BjerksundStenslandSpreadEngine;
using QuantLib::OperatorSplittingSpreadEngine;
using QuantLib::Fd2dBlackScholesVanillaEngine;
+using QuantLib::PearsonSpreadEngine;
+using QuantLib::GaussianCopulaSpreadEngine;
%}
%shared_ptr(StulzEngine)
@@ -291,6 +294,28 @@ class Fd2dBlackScholesVanillaEngine : public PricingEngine {
Real illegalLocalVolOverwrite = -Null());
};
+%shared_ptr(PearsonSpreadEngine)
+class PearsonSpreadEngine : public PricingEngine {
+ public:
+ PearsonSpreadEngine(
+ ext::shared_ptr process1,
+ ext::shared_ptr process2,
+ Real correlation,
+ Real integrationTolerance = 1e-10,
+ Size maxIntegrationIterations = 10000,
+ Real nStd = 8.0);
+};
+
+%shared_ptr(GaussianCopulaSpreadEngine)
+class GaussianCopulaSpreadEngine : public PricingEngine {
+ public:
+ GaussianCopulaSpreadEngine(
+ ext::shared_ptr process1,
+ ext::shared_ptr process2,
+ Real correlation,
+ Size nPoints = 64);
+};
+
%{
using QuantLib::ChoiBasketEngine;
using QuantLib::DengLiZhouBasketEngine;
diff --git a/SWIG/bondfunctions.i b/SWIG/bondfunctions.i
index b24191d4..70f1c6a2 100644
--- a/SWIG/bondfunctions.i
+++ b/SWIG/bondfunctions.i
@@ -147,6 +147,12 @@ class BondFunctions {
Frequency frequency,
Date settlementDate = Date());
+ static Real cleanPrice(const Bond& bond,
+ const ext::shared_ptr& discount,
+ Spread zSpread,
+ Compounding compounding,
+ Frequency frequency,
+ Date settlementDate = Date());
static Real cleanPrice(const Bond& bond,
const ext::shared_ptr& discount,
Spread zSpread,
@@ -154,6 +160,12 @@ class BondFunctions {
Compounding compounding,
Frequency frequency,
Date settlementDate = Date());
+ static Real dirtyPrice(const Bond& bond,
+ const ext::shared_ptr& discount,
+ Spread zSpread,
+ Compounding compounding,
+ Frequency frequency,
+ Date settlementDate = Date());
static Real dirtyPrice(const Bond& bond,
const ext::shared_ptr& discount,
Spread zSpread,
@@ -161,6 +173,15 @@ class BondFunctions {
Compounding compounding,
Frequency frequency,
Date settlementDate = Date());
+ static Spread zSpread(const Bond& bond,
+ BondPrice price,
+ const ext::shared_ptr& discountCurve,
+ Compounding compounding,
+ Frequency frequency,
+ Date settlementDate = Date(),
+ Real accuracy = 1.0e-10,
+ Size maxIterations = 100,
+ Rate guess = 0.0);
static Spread zSpread(const Bond& bond,
BondPrice price,
const ext::shared_ptr& discountCurve,
diff --git a/SWIG/bonds.i b/SWIG/bonds.i
index 0da00bb6..c8aa53d3 100644
--- a/SWIG/bonds.i
+++ b/SWIG/bonds.i
@@ -31,6 +31,7 @@
%include cashflows.i
%include interestrate.i
%include indexes.i
+%shared_ptr(Bond)
%include inflation.i
%include shortratemodels.i
@@ -55,7 +56,6 @@ class BondPrice {
bool isValid() const;
};
-%shared_ptr(Bond)
class Bond : public Instrument {
#if defined(SWIGPYTHON)
%rename(bondYield) yield;
@@ -274,7 +274,8 @@ class FloatingRateBond : public Bond {
const Period& exCouponPeriod = Period(),
const Calendar& exCouponCalendar = Calendar(),
BusinessDayConvention exCouponConvention = Unadjusted,
- bool exCouponEndOfMonth = false);
+ bool exCouponEndOfMonth = false,
+ BusinessDayConvention fixingConvention = Preceding);
};
diff --git a/SWIG/calendars.i b/SWIG/calendars.i
index 56fb28d7..0fabb64d 100644
--- a/SWIG/calendars.i
+++ b/SWIG/calendars.i
@@ -5,6 +5,7 @@
Copyright (C) 2005 Johan Witters
Copyright (C) 2018 Matthias Groncki
Copyright (C) 2023 Skandinaviska Enskilda Banken AB (publ)
+ Copyright (C) 2026 Lawrenz Law
This file is part of QuantLib, a free-software/open-source library
for financial quantitative analysts and developers - http://quantlib.org/
@@ -63,15 +64,15 @@ using QuantLib::JoinBusinessDays;
enum JointCalendarRule { JoinHolidays, JoinBusinessDays };
#if defined(SWIGPYTHON)
-%typemap(in) ext::optional %{
+%typemap(in) std::optional %{
if ($input == Py_None)
- $1 = ext::nullopt;
+ $1 = std::nullopt;
else if (PyLong_Check($input))
$1 = (BusinessDayConvention)PyLong_AsLong($input);
else
SWIG_exception(SWIG_TypeError, "int expected");
%}
-%typecheck (QL_TYPECHECK_BUSINESSDAYCONVENTION) ext::optional %{
+%typecheck (QL_TYPECHECK_BUSINESSDAYCONVENTION) std::optional %{
$1 = (PyLong_Check($input) || $input == Py_None) ? 1 : 0;
%}
#endif
@@ -111,6 +112,14 @@ class Calendar {
std::string name();
bool empty();
%extend {
+ std::vector addedHolidays() {
+ return std::vector(self->addedHolidays().begin(),
+ self->addedHolidays().end());
+ }
+ std::vector removedHolidays() {
+ return std::vector(self->removedHolidays().begin(),
+ self->removedHolidays().end());
+ }
std::string __str__() {
return self->name()+" calendar";
}
@@ -172,6 +181,12 @@ namespace QuantLib {
Chile(Market m = SSE);
};
+ class Croatia : public Calendar {
+ public:
+ enum Market { ZSE };
+ Croatia(Market m = ZSE);
+ };
+
class China : public Calendar {
public:
enum Market { SSE, IB };
@@ -227,7 +242,7 @@ namespace QuantLib {
class Israel : public Calendar {
public:
- enum Market { Settlement, TASE, SHIR };
+ enum Market { Settlement = 0, TASE = 1, SHIR = 2, Telbor = 3 };
Israel(Market m = Settlement);
};
@@ -239,20 +254,50 @@ namespace QuantLib {
class Japan : public Calendar {};
+ class Malaysia : public Calendar {
+ public:
+ enum Market { KLSE };
+ Malaysia(Market m = KLSE);
+ };
+
+ class Malta : public Calendar {
+ public:
+ enum Market { MSE };
+ Malta(Market m = MSE);
+ };
+
class Mexico : public Calendar {
public:
enum Market { BMV };
Mexico(Market m = BMV);
};
+ class Montenegro : public Calendar {
+ public:
+ enum Market { MNSE };
+ Montenegro(Market m = MNSE);
+ };
+
class NewZealand : public Calendar {
public:
enum Market { Wellington, Auckland };
NewZealand(Market m = Wellington);
};
+ class NorthMacedonia : public Calendar {
+ public:
+ enum Market { MSE };
+ NorthMacedonia(Market m = MSE);
+ };
+
class Norway : public Calendar {};
+ class Philippines : public Calendar {
+ public:
+ enum Market { PSE };
+ Philippines(Market m = PSE);
+ };
+
class Poland : public Calendar {
public:
enum Market { Settlement, WSE };
@@ -277,6 +322,12 @@ namespace QuantLib {
SaudiArabia(Market m = Tadawul);
};
+ class Serbia : public Calendar {
+ public:
+ enum Market { BSE };
+ Serbia(Market m = BSE);
+ };
+
class Singapore : public Calendar {
public:
enum Market { SGX };
@@ -289,6 +340,12 @@ namespace QuantLib {
Slovakia(Market m = BSSE);
};
+ class Slovenia : public Calendar {
+ public:
+ enum Market { LSE };
+ Slovenia(Market m = LSE);
+ };
+
class SouthAfrica : public Calendar {};
class SouthKorea : public Calendar {
@@ -329,6 +386,12 @@ namespace QuantLib {
UnitedStates(Market m);
};
+ class Uzbekistan : public Calendar {
+ public:
+ enum Market { UZSE };
+ Uzbekistan(Market m = UZSE);
+ };
+
// others
class NullCalendar : public Calendar {};
diff --git a/SWIG/calibrationhelpers.i b/SWIG/calibrationhelpers.i
index a408efa4..2afee2e9 100644
--- a/SWIG/calibrationhelpers.i
+++ b/SWIG/calibrationhelpers.i
@@ -68,9 +68,6 @@ class BlackCalibrationHelper : public CalibrationHelper {
ext::shared_ptr as_black_helper(const ext::shared_ptr& h) {
return ext::dynamic_pointer_cast(h);
}
- ext::shared_ptr as_swaption_helper(const ext::shared_ptr& h) {
- return ext::dynamic_pointer_cast(h);
- }
%}
%shared_ptr(SwaptionHelper)
@@ -147,6 +144,12 @@ class SwaptionHelper : public BlackCalibrationHelper {
}
};
+%inline %{
+ ext::shared_ptr as_swaption_helper(const ext::shared_ptr& h) {
+ return ext::dynamic_pointer_cast(h);
+ }
+%}
+
%shared_ptr(CapHelper)
class CapHelper : public BlackCalibrationHelper {
public:
diff --git a/SWIG/cashflows.i b/SWIG/cashflows.i
index 773da414..6a41c8e6 100644
--- a/SWIG/cashflows.i
+++ b/SWIG/cashflows.i
@@ -5,6 +5,7 @@
Copyright (C) 2010, 2011 Lluis Pujol Bajador
Copyright (C) 2017, 2018, 2019, 2020 Matthias Lungwitz
Copyright (C) 2021 Marcin Rybacki
+ Copyright (C) 2026 Kyrylo Protsenko
This file is part of QuantLib, a free-software/open-source library
for financial quantitative analysts and developers - http://quantlib.org/
@@ -191,6 +192,7 @@ class FloatingRateCoupon : public Coupon {
public:
Date fixingDate() const;
Integer fixingDays() const;
+ BusinessDayConvention fixingConvention() const;
bool isInArrears() const;
Real gearing() const;
Rate spread() const;
@@ -200,6 +202,7 @@ class FloatingRateCoupon : public Coupon {
Real price(const Handle& discountCurve) const;
ext::shared_ptr index() const;
void setPricer(const ext::shared_ptr& p);
+ ext::shared_ptr pricer() const;
};
%inline %{
@@ -210,6 +213,130 @@ class FloatingRateCoupon : public Coupon {
%}
+%{
+using QuantLib::FxReset;
+using QuantLib::FxResetConvention;
+using QuantLib::FxResetPricer;
+using QuantLib::DiscountingFxResetPricer;
+using QuantLib::FxResetCoupon;
+using QuantLib::FxResetNotionalExchange;
+using QuantLib::setFxResetPricer;
+%}
+
+class FxReset {
+ public:
+ FxReset(const Date& fixingDate, const Date& valueDate);
+ Date fixingDate() const;
+ Date valueDate() const;
+};
+
+class FxResetConvention {
+ #if !defined(SWIGJAVA) && !defined(SWIGCSHARP)
+ %feature("kwargs") FxResetConvention;
+ #endif
+ public:
+ FxResetConvention(Natural fixingDays = 0,
+ const Calendar& fixingCalendar = Calendar());
+ FxReset reset(const Date& valueDate) const;
+ Date valueDate(const Date& fixingDate) const;
+ Natural fixingDays() const;
+ const Calendar& fixingCalendar() const;
+};
+
+%shared_ptr(FxResetPricer)
+class FxResetPricer {
+ private:
+ FxResetPricer();
+ public:
+ virtual Real fxRate(const FxReset& reset) const;
+};
+
+%shared_ptr(DiscountingFxResetPricer)
+class DiscountingFxResetPricer : public FxResetPricer {
+ #if !defined(SWIGJAVA) && !defined(SWIGCSHARP)
+ %feature("kwargs") DiscountingFxResetPricer;
+ #endif
+ public:
+ DiscountingFxResetPricer(
+ const Currency& constantLegCurrency,
+ const Currency& resettableLegCurrency,
+ const Handle& constantLegCurve,
+ const Handle& resettableLegCurve,
+ const Handle& spotFx,
+ bool spotIsResettablePerConstant,
+ const Date& spotFxSettleDate = Date());
+ Real fxRate(const FxReset& reset) const;
+ const Currency& constantLegCurrency() const;
+ const Currency& resettableLegCurrency() const;
+};
+
+%shared_ptr(FxResetCoupon)
+class FxResetCoupon : public FloatingRateCoupon {
+ public:
+ FxResetCoupon(const ext::shared_ptr& underlying,
+ Real constantLegNotional,
+ const FxReset& fxReset);
+ const ext::shared_ptr& underlying() const;
+ Real constantLegNotional() const;
+ const FxReset& fxReset() const;
+ Date fxResetDate() const;
+ Date fxResetValueDate() const;
+ const ext::shared_ptr& fxResetPricer() const;
+ void setFxResetPricer(const ext::shared_ptr& pricer);
+};
+
+%inline %{
+ ext::shared_ptr as_fx_reset_coupon(
+ const ext::shared_ptr& cf) {
+ return ext::dynamic_pointer_cast(cf);
+ }
+%}
+
+%shared_ptr(FxResetNotionalExchange)
+class FxResetNotionalExchange : public CashFlow {
+ public:
+ Real constantLegNotional() const;
+ const ext::shared_ptr& fxResetPricer() const;
+ void setFxResetPricer(const ext::shared_ptr& pricer);
+};
+
+%extend FxResetNotionalExchange {
+ FxResetNotionalExchange(const Date& paymentDate,
+ Real constantLegNotional,
+ const FxReset* previousReset,
+ const FxReset* currentReset) {
+ return new FxResetNotionalExchange(
+ paymentDate, constantLegNotional,
+ previousReset ? std::optional(*previousReset) : std::nullopt,
+ currentReset ? std::optional(*currentReset) : std::nullopt);
+ }
+ bool hasPreviousReset() const {
+ return self->previousReset().has_value();
+ }
+ FxReset previousReset() const {
+ QL_REQUIRE(self->previousReset(), "the notional exchange has no previous FX reset");
+ return *self->previousReset();
+ }
+ bool hasCurrentReset() const {
+ return self->currentReset().has_value();
+ }
+ FxReset currentReset() const {
+ QL_REQUIRE(self->currentReset(), "the notional exchange has no current FX reset");
+ return *self->currentReset();
+ }
+};
+
+%inline %{
+ ext::shared_ptr as_fx_reset_notional_exchange(
+ const ext::shared_ptr& cf) {
+ return ext::dynamic_pointer_cast(cf);
+ }
+%}
+
+void setFxResetPricer(const Leg&,
+ const ext::shared_ptr&);
+
+
%{
using QuantLib::CappedFlooredCoupon;
%}
@@ -274,7 +401,9 @@ class OvernightIndexedCoupon : public FloatingRateCoupon {
bool applyObservationShift = false,
bool compoundSpread = false,
const Date& rateComputationStartDate = Date(),
- const Date& rateComputationEndDate = Date());
+ const Date& rateComputationEndDate = Date(),
+ const Date& exCouponDate = Date(),
+ std::optional roundingPrecision = std::nullopt);
const std::vector& fixingDates() const;
const std::vector& interestDates() const;
const std::vector