You can subscribe to this list here.
| 2000 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
(1) |
Nov
|
Dec
(60) |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2001 |
Jan
(18) |
Feb
(4) |
Mar
(6) |
Apr
(2) |
May
|
Jun
(12) |
Jul
(48) |
Aug
(6) |
Sep
(3) |
Oct
(24) |
Nov
(15) |
Dec
(18) |
| 2002 |
Jan
(39) |
Feb
(12) |
Mar
(80) |
Apr
(72) |
May
(46) |
Jun
(27) |
Jul
(23) |
Aug
(34) |
Sep
(65) |
Oct
(71) |
Nov
(19) |
Dec
(14) |
| 2003 |
Jan
(44) |
Feb
(59) |
Mar
(18) |
Apr
(62) |
May
(54) |
Jun
(27) |
Jul
(46) |
Aug
(15) |
Sep
(44) |
Oct
(36) |
Nov
(19) |
Dec
(12) |
| 2004 |
Jan
(26) |
Feb
(33) |
Mar
(47) |
Apr
(63) |
May
(36) |
Jun
(65) |
Jul
(80) |
Aug
(163) |
Sep
(65) |
Oct
(39) |
Nov
(36) |
Dec
(39) |
| 2005 |
Jan
(97) |
Feb
(78) |
Mar
(64) |
Apr
(64) |
May
(48) |
Jun
(55) |
Jul
(89) |
Aug
(57) |
Sep
(51) |
Oct
(111) |
Nov
(86) |
Dec
(76) |
| 2006 |
Jan
(84) |
Feb
(103) |
Mar
(143) |
Apr
(92) |
May
(55) |
Jun
(58) |
Jul
(71) |
Aug
(57) |
Sep
(74) |
Oct
(59) |
Nov
(8) |
Dec
(32) |
| 2007 |
Jan
(60) |
Feb
(40) |
Mar
(50) |
Apr
(26) |
May
(61) |
Jun
(120) |
Jul
(119) |
Aug
(48) |
Sep
(121) |
Oct
(66) |
Nov
(103) |
Dec
(43) |
| 2008 |
Jan
(60) |
Feb
(109) |
Mar
(92) |
Apr
(106) |
May
(82) |
Jun
(59) |
Jul
(67) |
Aug
(118) |
Sep
(131) |
Oct
(56) |
Nov
(37) |
Dec
(69) |
| 2009 |
Jan
(75) |
Feb
(76) |
Mar
(103) |
Apr
(78) |
May
(61) |
Jun
(35) |
Jul
(66) |
Aug
(69) |
Sep
(166) |
Oct
(46) |
Nov
(72) |
Dec
(65) |
| 2010 |
Jan
(48) |
Feb
(57) |
Mar
(93) |
Apr
(85) |
May
(123) |
Jun
(82) |
Jul
(98) |
Aug
(121) |
Sep
(146) |
Oct
(86) |
Nov
(72) |
Dec
(34) |
| 2011 |
Jan
(96) |
Feb
(55) |
Mar
(73) |
Apr
(57) |
May
(33) |
Jun
(74) |
Jul
(89) |
Aug
(71) |
Sep
(103) |
Oct
(76) |
Nov
(52) |
Dec
(61) |
| 2012 |
Jan
(48) |
Feb
(54) |
Mar
(78) |
Apr
(60) |
May
(75) |
Jun
(59) |
Jul
(33) |
Aug
(66) |
Sep
(43) |
Oct
(46) |
Nov
(75) |
Dec
(51) |
| 2013 |
Jan
(112) |
Feb
(72) |
Mar
(49) |
Apr
(48) |
May
(42) |
Jun
(44) |
Jul
(80) |
Aug
(19) |
Sep
(33) |
Oct
(37) |
Nov
(38) |
Dec
(98) |
| 2014 |
Jan
(113) |
Feb
(93) |
Mar
(49) |
Apr
(106) |
May
(97) |
Jun
(155) |
Jul
(87) |
Aug
(127) |
Sep
(85) |
Oct
(48) |
Nov
(41) |
Dec
(37) |
| 2015 |
Jan
(34) |
Feb
(50) |
Mar
(104) |
Apr
(80) |
May
(82) |
Jun
(66) |
Jul
(41) |
Aug
(84) |
Sep
(37) |
Oct
(65) |
Nov
(83) |
Dec
(52) |
| 2016 |
Jan
(68) |
Feb
(35) |
Mar
(42) |
Apr
(35) |
May
(54) |
Jun
(75) |
Jul
(45) |
Aug
(52) |
Sep
(60) |
Oct
(52) |
Nov
(36) |
Dec
(64) |
| 2017 |
Jan
(92) |
Feb
(59) |
Mar
(35) |
Apr
(53) |
May
(83) |
Jun
(43) |
Jul
(65) |
Aug
(68) |
Sep
(46) |
Oct
(75) |
Nov
(40) |
Dec
(49) |
| 2018 |
Jan
(68) |
Feb
(54) |
Mar
(48) |
Apr
(58) |
May
(51) |
Jun
(44) |
Jul
(40) |
Aug
(68) |
Sep
(35) |
Oct
(15) |
Nov
(7) |
Dec
(37) |
| 2019 |
Jan
(43) |
Feb
(7) |
Mar
(22) |
Apr
(21) |
May
(31) |
Jun
(39) |
Jul
(73) |
Aug
(45) |
Sep
(47) |
Oct
(89) |
Nov
(19) |
Dec
(69) |
| 2020 |
Jan
(52) |
Feb
(63) |
Mar
(45) |
Apr
(59) |
May
(42) |
Jun
(57) |
Jul
(30) |
Aug
(29) |
Sep
(75) |
Oct
(64) |
Nov
(96) |
Dec
(22) |
| 2021 |
Jan
(14) |
Feb
(24) |
Mar
(35) |
Apr
(58) |
May
(36) |
Jun
(15) |
Jul
(18) |
Aug
(31) |
Sep
(30) |
Oct
(33) |
Nov
(27) |
Dec
(16) |
| 2022 |
Jan
(35) |
Feb
(22) |
Mar
(14) |
Apr
(20) |
May
(44) |
Jun
(53) |
Jul
(25) |
Aug
(56) |
Sep
(11) |
Oct
(47) |
Nov
(22) |
Dec
(36) |
| 2023 |
Jan
(30) |
Feb
(17) |
Mar
(31) |
Apr
(48) |
May
(31) |
Jun
(7) |
Jul
(25) |
Aug
(26) |
Sep
(61) |
Oct
(66) |
Nov
(19) |
Dec
(21) |
| 2024 |
Jan
(37) |
Feb
(29) |
Mar
(26) |
Apr
(26) |
May
(34) |
Jun
(9) |
Jul
(27) |
Aug
(13) |
Sep
(15) |
Oct
(25) |
Nov
(13) |
Dec
(8) |
| 2025 |
Jan
(13) |
Feb
(1) |
Mar
(16) |
Apr
(17) |
May
(8) |
Jun
(6) |
Jul
(9) |
Aug
|
Sep
(6) |
Oct
(15) |
Nov
(6) |
Dec
|
| 2026 |
Jan
(6) |
Feb
(4) |
Mar
(20) |
Apr
(19) |
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
|
From: Dmitri G. <dm...@ma...> - 2026-04-17 16:57:31
|
Hi Marco, Great to see ORE Studio — looks like a useful project. On the AAD side: we’ve had a working integration of AADC with ORE for several years now — transparent AAD valuation through the standard ORE workflow, including JIT compilation and replay for Monte Carlo paths. We have clients running this in production. Here’s a demo from 2022 <https://www.youtube.com/watch?v=1WN8gUF120M&utm_campaign=crm&utm_source=email&utm_medium=direct&tid=natwest-marco-craveiro-20260417-1> showing ORE+AADC in action. More details: live risk architecture <https://matlogica.com/technology/live-risk-architecture/?utm_campaign=crm&utm_source=email&utm_medium=direct&tid=natwest-marco-craveiro-20260417-2> and ORE LiveRisk <https://matlogica.com/technology/ore-liverisk/?utm_campaign=crm&utm_source=email&utm_medium=direct&tid=natwest-marco-craveiro-20260417-3> . Happy to show you a live demo or discuss how it might fit with ORE Studio. Kind regards, Dmitri Goloubentsev Head of Automatic Adjoint Differentiation, Matlogica LTD http://matlogica.com +447378414528 See my schedule and book <https://calendly.com/matlogica> a meeting with me On Thu, 16 Apr 2026 at 17:12, Marco Craveiro <mar...@gm...> wrote: > Hi QuantLib devs, > > A few months ago I started a new open source project called ORE Studio: > > - https://github.com/OreStudio/OreStudio > > This email is my first outreach attempt. ORE Studio's ultimate > objective will be to provide a CRUD and service layer for ORE [1]. > Note that the project is not directly affiliated with ORE, but it is > so named because it only targets ORE and no other risk engine. It may > be subject to change, If Roland or anyone from ORE has any qualms with > it. > > Releases are done when we accumulate enough commits; I am about to > release v0.0.16 over the next few days. I recommend always using main > and updating very frequently, as the code tends to move around a lot. > > I also feel compelled to provide some warnings and caveats: > > - Though I've been at it for over 6 months, it is *very* early days > for the project; many things don't work as expected, or don't work at > all. The code is stabilising somewhat but, in truth, *anything* may be > subject to change. For example, we just recently transitioned from a > monolith to a service-based architecture > - The documentation is not great, and it is certainly not up-to-date. > - ORE Studio is *100%* developed with AI - more specifically Claude > Code [2] - so the code is not exactly the absolute best. One of my > objectives with ORE Studio is to test the AI state of the art in anger > - the other being testing the suitability of ORE for Intraday Risk > management. If you do not like AI, I'm rather afraid ORE Studio won't > be to your liking. Contributions without AI are, of course, very > welcome. > > The main focus for the eventual v1.0.0 will be to get the end-to-end > workflow working correctly across the entire ORE instrument inventory > (as per ORE samples): > > - UI for entering trades (with instrument-specific handling), managing > curves, surfaces etc - effectively UIs covering all of ORE input data. > - reporting subsystem to create batches, send them for valuation; > - compute subsystem to value batches; > - UI to view report results. > > The skeleton for this structure is now in place, but has many (many) > limitations across the board. In fairness, I have somewhat rushed the > outreach; my objective was to get something basic finished by the ORE > user meet up but, given it is on the 11th of May, I ran out of time. I > wanted those interested to have time to have a play prior to the > event, and hopefully gather some feedback from the community at the > meetup, as to the usefulness of this project outside my use cases. > > Finally, I do not wish to abuse Luigi's patience and send too many > emails about ORE Studio on the QuantLib malinglist :-) so, if you want > to ask any questions or just have a chat, can I please ask you to pop > over to our discord: > > https://discord.gg/gcrYsjW3pd > > You can also contact me directly, either over email or Twitter. And, > as with any GitHub project, you can raise issues, discussions etc. > > I look forward to hearing from you. Many thanks for your time. > -- > Marco Craveiro, PhD > > about: https://mcraveiro.github.io/about.html > site: https://mcraveiro.github.io/ > twitter: https://twitter.com/MarcoCraveiro > > [1] https://github.com/OpenSourceRisk/Engine > [2] https://claude.com/product/claude-code > > > _______________________________________________ > QuantLib-users mailing list > Qua...@li... > https://lists.sourceforge.net/lists/listinfo/quantlib-users > |
|
From: Marco C. <mar...@gm...> - 2026-04-17 15:26:44
|
Hi Dmitri, > Great to see ORE Studio — looks like a useful project. Thanks for the kind words. Also, as requested by a few people, I created a quick video overview of the project; it gives a better idea of what we're aiming for (with apologies in advance for the sound quality): https://youtu.be/sgf7ybOpWxE > On the AAD side: we’ve had a working integration of AADC with ORE for several years now — transparent AAD valuation through the standard ORE workflow, including JIT compilation and replay for Monte Carlo paths. We have clients running this in production. > > Here’s a demo from 2022 showing ORE+AADC in action. > > More details: live risk architecture and ORE LiveRisk. Whilst I am very interested, I must confess I have not made much progress on the AAD front so far. I've had a brief conversation with Jorg regarding their QuantLibAAD approach [1]. It looked extremely interesting but required further work at my end to consume it. ORE Studio works best with something that "looks" like an ORE binary. On the other hand, from the links you provided [2, 3], ORE + AADC appears to integrate seamlessly with ORE Studio, requiring only config changes (the manifest of the compute package). > Happy to show you a live demo or discuss how it might fit with ORE Studio. I'll contact you directly - seems very promising and should be easy to test. Thanks very much for reaching out. Cheers -- Marco Craveiro, PhD about: https://mcraveiro.github.io/about.html site: https://mcraveiro.github.io/ twitter: https://twitter.com/MarcoCraveiro [1] https://sourceforge.net/p/quantlib/mailman/message/59293906/ [2] https://matlogica.com/technology/live-risk-architecture [3] https://matlogica.com/technology/ore-liverisk |
|
From: Luigi B. <lui...@gm...> - 2026-04-17 08:38:23
|
QuantLib 1.42.1 is now available for download at < https://www.quantlib.org/download.shtml>; precompiled binaries are also available from PyPI and NuGet for Python and C# respectively. This is a bug-fix release for the recently released QuantLib 1.42, removing a regression in the Gsr class possibly leading to an infinite notification loop. Thanks to Aleksis Ali Raza for the heads-up. If you have any problems with this release, please report them here on the QuantLib mailing list (<qua...@li...>) or open a GitHub issue at <https://github.com/lballabio/quantlib/issues>. |
|
From: Marco C. <mar...@gm...> - 2026-04-16 16:10:55
|
Hi QuantLib devs, A few months ago I started a new open source project called ORE Studio: - https://github.com/OreStudio/OreStudio This email is my first outreach attempt. ORE Studio's ultimate objective will be to provide a CRUD and service layer for ORE [1]. Note that the project is not directly affiliated with ORE, but it is so named because it only targets ORE and no other risk engine. It may be subject to change, If Roland or anyone from ORE has any qualms with it. Releases are done when we accumulate enough commits; I am about to release v0.0.16 over the next few days. I recommend always using main and updating very frequently, as the code tends to move around a lot. I also feel compelled to provide some warnings and caveats: - Though I've been at it for over 6 months, it is *very* early days for the project; many things don't work as expected, or don't work at all. The code is stabilising somewhat but, in truth, *anything* may be subject to change. For example, we just recently transitioned from a monolith to a service-based architecture - The documentation is not great, and it is certainly not up-to-date. - ORE Studio is *100%* developed with AI - more specifically Claude Code [2] - so the code is not exactly the absolute best. One of my objectives with ORE Studio is to test the AI state of the art in anger - the other being testing the suitability of ORE for Intraday Risk management. If you do not like AI, I'm rather afraid ORE Studio won't be to your liking. Contributions without AI are, of course, very welcome. The main focus for the eventual v1.0.0 will be to get the end-to-end workflow working correctly across the entire ORE instrument inventory (as per ORE samples): - UI for entering trades (with instrument-specific handling), managing curves, surfaces etc - effectively UIs covering all of ORE input data. - reporting subsystem to create batches, send them for valuation; - compute subsystem to value batches; - UI to view report results. The skeleton for this structure is now in place, but has many (many) limitations across the board. In fairness, I have somewhat rushed the outreach; my objective was to get something basic finished by the ORE user meet up but, given it is on the 11th of May, I ran out of time. I wanted those interested to have time to have a play prior to the event, and hopefully gather some feedback from the community at the meetup, as to the usefulness of this project outside my use cases. Finally, I do not wish to abuse Luigi's patience and send too many emails about ORE Studio on the QuantLib malinglist :-) so, if you want to ask any questions or just have a chat, can I please ask you to pop over to our discord: https://discord.gg/gcrYsjW3pd You can also contact me directly, either over email or Twitter. And, as with any GitHub project, you can raise issues, discussions etc. I look forward to hearing from you. Many thanks for your time. -- Marco Craveiro, PhD about: https://mcraveiro.github.io/about.html site: https://mcraveiro.github.io/ twitter: https://twitter.com/MarcoCraveiro [1] https://github.com/OpenSourceRisk/Engine [2] https://claude.com/product/claude-code |
|
From: Luigi B. <lui...@gm...> - 2026-04-16 09:46:25
|
Thanks!
On Thu, Apr 16, 2026 at 11:32 AM Aleksis Ali Raza <
ale...@go...> wrote:
> yes, the results tie with v1.40 after the code fix in 1.42.
>
> here's a comparison run for the test script i sent earlier.
>
> --- GSR live setValue bump test v1.42---
> base rate: 0.04875825
> base npv : 16.787915348295005
>
> setValue -> 0.04885825
> npv = 17.005154111913548
>
> setValue -> 0.04865825
> npv = 16.57057870270632
>
> setValue -> 0.04875825
> npv = 16.787915348295005
>
> starting stress loop...
> iter 000 rate=0.04885825 npv=17.005154111913548
> iter 020 rate=0.04885825 npv=17.005154111913548
> iter 040 rate=0.04885825 npv=17.005154111913548
> iter 060 rate=0.04885825 npv=17.005154111913548
> iter 080 rate=0.04885825 npv=17.005154111913548
> iter 100 rate=0.04885825 npv=17.005154111913548
> iter 120 rate=0.04885825 npv=17.005154111913548
> iter 140 rate=0.04885825 npv=17.005154111913548
> iter 160 rate=0.04885825 npv=17.005154111913548
> iter 180 rate=0.04885825 npv=17.005154111913548
>
> restored rate: 0.04875825
> restored npv : 16.787915348295005
>
>
> --- GSR live setValue bump test v1.40 ---
> base rate: 0.04875825
> base npv : 16.787915348295005
>
> setValue -> 0.04885825
> npv = 17.005154111913548
>
> setValue -> 0.04865825
> npv = 16.57057870270632
>
> setValue -> 0.04875825
> npv = 16.787915348295005
>
> starting stress loop...
> iter 000 rate=0.04885825 npv=17.005154111913548
> iter 020 rate=0.04885825 npv=17.005154111913548
> iter 040 rate=0.04885825 npv=17.005154111913548
> iter 060 rate=0.04885825 npv=17.005154111913548
> iter 080 rate=0.04885825 npv=17.005154111913548
> iter 100 rate=0.04885825 npv=17.005154111913548
> iter 120 rate=0.04885825 npv=17.005154111913548
> iter 140 rate=0.04885825 npv=17.005154111913548
> iter 160 rate=0.04885825 npv=17.005154111913548
> iter 180 rate=0.04885825 npv=17.005154111913548
>
> restored rate: 0.04875825
> restored npv : 16.787915348295005
>
>
> On Apr 16, 2026, at 12:38, Luigi Ballabio <lui...@gm...>
> wrote:
>
> May you check that it also reprices correctly after the bump? That is,
> that you get the same numerical results as 1.40?
>
> On Thu, Apr 16, 2026 at 9:04 AM Luigi Ballabio <lui...@gm...>
> wrote:
>
>> Thanks for the heads-up. Looks like I need to release a 1.42.1...
>>
>> Luigi
>>
>>
>> On Thu, Apr 16, 2026 at 8:57 AM Aleksis Ali Raza via QuantLib-users <
>> qua...@li...> wrote:
>>
>>> Yep, ditching registerWith(stateProcess_); works fine as well and is the
>>> cleaner fix.
>>>
>>> thanks.
>>>
>>> On Apr 16, 2026, at 11:34, Peter Caspers <pca...@gm...> wrote:
>>>
>>> Thanks. Since Gsr manages its stateProcess_, it does not need to and
>>> should not register with it as an Observer? I will take a closer look, but
>>> if you could test this change on your end, that would be valuable input.
>>> Best, Peter
>>>
>>>
>>> Aleksis Ali Raza via QuantLib-users <
>>> qua...@li...> schrieb am Do. 16. Apr. 2026 um
>>> 08:04:
>>>
>>>> Just to follow up on this, it's this line:
>>>>
>>>> void Gsr::update() {
>>>> if (stateProcess_ != nullptr) {
>>>>
>>>> ext::static_pointer_cast<GsrProcess>(stateProcess_)->flushCache();
>>>>
>>>> *ext::static_pointer_cast<GsrProcess>(stateProcess_)->notifyObservers();*
>>>> }
>>>> LazyObject::update();
>>>> }
>>>>
>>>> in the gsr class that seems to be the cause. in my case, commenting out
>>>> that line out and rebuilding from source fixes the problem of my gsr-based
>>>> swaption risk runs.
>>>>
>>>> That line by itself looks pretty harmless but i think
>>>>
>>>> registerWith(stateProcess_);
>>>>
>>>> further down leads into some sort of feedback loop (setValue() →
>>>> notifyObservers() → Gsr::update() → stateProcess_->notifyObservers() →
>>>> Gsr::update() → …) and ultimately a seg fault.
>>>>
>>>> On Apr 16, 2026, at 09:13, Aleksis Ali Raza <
>>>> ale...@go...> wrote:
>>>>
>>>> Hi,
>>>>
>>>> I’m encountering what appears to be a regression or stability issue in
>>>> QuantLib 1.42 related to the GSR (Gaussian short rate) model and the
>>>> observer framework.
>>>>
>>>> Calling SimpleQuote.setValue() on a quote that is observed by a term
>>>> structure used in a Gsr model causes a segmentation fault.
>>>>
>>>> This occurs even in a minimal standalone script (no threading, no
>>>> external frameworks).
>>>>
>>>> Minimal reproduction
>>>>
>>>> The following sequence is sufficient:
>>>> 1. Create a SimpleQuote
>>>> 2. Build a FlatForward curve using that quote
>>>> 3. Construct a Gsr model from that term structure
>>>> 4. Attach a Gaussian1dSwaptionEngine
>>>> 5. Call setValue() on the original SimpleQuote
>>>>
>>>> This results in a segmentation fault.
>>>>
>>>> I have modified the Python/examples/bermudan_swaption.py file and
>>>> attached it below to illustrate (along with error log below it).
>>>>
>>>> Has something been changed in how one shud enquire / update quote
>>>> objects that I missed? I am coming from 1.40, where this was not an issue.
>>>> I am running python 3.14.4 on macOS 26.1.4 and using a source build of
>>>> Quantlib/SWIG.
>>>>
>>>> Thanks, Aleksis
>>>>
>>>> # ---
>>>> # jupyter:
>>>> # jupytext:
>>>> # formats: py:light
>>>> # text_representation:
>>>> # extension: .py
>>>> # format_name: light
>>>> # format_version: '1.5'
>>>> # jupytext_version: 1.4.2
>>>> # kernelspec:
>>>> # display_name: Python 3
>>>> # language: python
>>>> # name: python3
>>>> # ---
>>>>
>>>> # # Bermudan swaptions
>>>> #
>>>> # Copyright (©) 2004, 2005, 2006, 2007 StatPro Italia srl
>>>> #
>>>> # This file is part of QuantLib, a free-software/open-source library
>>>> # for financial quantitative analysts and developers - https://www.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
>>>> # <qua...@li...>. The license is also available online at
>>>> # <https://www.quantlib.org/license.shtml>.
>>>> #
>>>> # 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.
>>>>
>>>> import QuantLib as ql
>>>> import pandas as pd
>>>>
>>>> # ### Setup
>>>>
>>>> todaysDate = ql.Date(15, ql.February, 2002)
>>>> ql.Settings.instance().evaluationDate = todaysDate
>>>> calendar = ql.TARGET()
>>>> settlementDate = ql.Date(19, ql.February, 2002)
>>>>
>>>>
>>>> def calibrate(model, helpers, l, name):
>>>> print("Model: %s" % name)
>>>>
>>>> method = ql.Simplex(l)
>>>> model.calibrate(helpers, method, ql.EndCriteria(1000, 250, 1e-7, 1e-7, 1e-7))
>>>>
>>>> print("Parameters: %s" % model.params())
>>>>
>>>> totalError = 0.0
>>>> data = []
>>>> for swaption, helper in zip(swaptionVols, helpers):
>>>> maturity, length, vol = swaption
>>>> NPV = helper.modelValue()
>>>> implied = helper.impliedVolatility(NPV, 1.0e-4, 1000, 0.05, 0.50)
>>>> error = implied - vol
>>>> totalError += abs(error)
>>>> data.append((maturity, length, vol, implied, error))
>>>> averageError = totalError / len(helpers)
>>>>
>>>> print(pd.DataFrame(data, columns=["maturity", "length", "volatility", "implied", "error"]))
>>>>
>>>> print("Average error: %.4f" % averageError)
>>>>
>>>>
>>>> # ### Market data
>>>>
>>>> swaptionVols = [
>>>> # maturity, length, volatility
>>>> (ql.Period(1, ql.Years), ql.Period(5, ql.Years), 0.1148),
>>>> (ql.Period(2, ql.Years), ql.Period(4, ql.Years), 0.1108),
>>>> (ql.Period(3, ql.Years), ql.Period(3, ql.Years), 0.1070),
>>>> (ql.Period(4, ql.Years), ql.Period(2, ql.Years), 0.1021),
>>>> (ql.Period(5, ql.Years), ql.Period(1, ql.Years), 0.1000),
>>>> ]
>>>>
>>>> # This is a flat yield term structure implying a 1x5 swap at 5%.
>>>>
>>>> rateQuote = ql.SimpleQuote(0.04875825)
>>>> rate = ql.QuoteHandle(rateQuote)
>>>> termStructure = ql.YieldTermStructureHandle(
>>>> ql.FlatForward(settlementDate, rate, ql.Actual365Fixed())
>>>> )
>>>>
>>>> # Define the ATM/OTM/ITM swaps:
>>>>
>>>> swapEngine = ql.DiscountingSwapEngine(termStructure)
>>>>
>>>> fixedLegFrequency = ql.Annual
>>>> fixedLegTenor = ql.Period(1, ql.Years)
>>>> fixedLegConvention = ql.Unadjusted
>>>> floatingLegConvention = ql.ModifiedFollowing
>>>> fixedLegDayCounter = ql.Thirty360(ql.Thirty360.European)
>>>> floatingLegFrequency = ql.Semiannual
>>>> floatingLegTenor = ql.Period(6, ql.Months)
>>>>
>>>> payFixed = ql.Swap.Payer
>>>> fixingDays = 2
>>>> index = ql.Euribor6M(termStructure)
>>>> floatingLegDayCounter = index.dayCounter()
>>>>
>>>> swapStart = calendar.advance(settlementDate, 1, ql.Years, floatingLegConvention)
>>>> swapEnd = calendar.advance(swapStart, 5, ql.Years, floatingLegConvention)
>>>>
>>>> fixedSchedule = ql.Schedule(
>>>> swapStart,
>>>> swapEnd,
>>>> fixedLegTenor,
>>>> calendar,
>>>> fixedLegConvention,
>>>> fixedLegConvention,
>>>> ql.DateGeneration.Forward,
>>>> False,
>>>> )
>>>> floatingSchedule = ql.Schedule(
>>>> swapStart,
>>>> swapEnd,
>>>> floatingLegTenor,
>>>> calendar,
>>>> floatingLegConvention,
>>>> floatingLegConvention,
>>>> ql.DateGeneration.Forward,
>>>> False,
>>>> )
>>>>
>>>> dummy = ql.VanillaSwap(
>>>> payFixed, 100.0, fixedSchedule, 0.0, fixedLegDayCounter, floatingSchedule, index, 0.0, floatingLegDayCounter
>>>> )
>>>> dummy.setPricingEngine(swapEngine)
>>>> atmRate = dummy.fairRate()
>>>>
>>>> atmSwap = ql.VanillaSwap(
>>>> payFixed, 1000.0, fixedSchedule, atmRate, fixedLegDayCounter,
>>>> floatingSchedule, index, 0.0, floatingLegDayCounter
>>>> )
>>>>
>>>> otmSwap = ql.VanillaSwap(
>>>> payFixed, 1000.0, fixedSchedule, atmRate * 1.2, fixedLegDayCounter,
>>>> floatingSchedule, index, 0.0, floatingLegDayCounter
>>>> )
>>>>
>>>> itmSwap = ql.VanillaSwap(
>>>> payFixed, 1000.0, fixedSchedule, atmRate * 0.8, fixedLegDayCounter,
>>>> floatingSchedule, index, 0.0, floatingLegDayCounter
>>>> )
>>>>
>>>> atmSwap.setPricingEngine(swapEngine)
>>>> otmSwap.setPricingEngine(swapEngine)
>>>> itmSwap.setPricingEngine(swapEngine)
>>>>
>>>> helpers = [
>>>> ql.SwaptionHelper(
>>>> maturity,
>>>> length,
>>>> ql.makeQuoteHandle(vol),
>>>> index,
>>>> index.tenor(),
>>>> index.dayCounter(),
>>>> index.dayCounter(),
>>>> termStructure,
>>>> )
>>>> for maturity, length, vol in swaptionVols
>>>> ]
>>>>
>>>> times = {}
>>>> for h in helpers:
>>>> for t in h.times():
>>>> times[t] = 1
>>>> times = sorted(times.keys())
>>>>
>>>> grid = ql.TimeGrid(times, 30)
>>>>
>>>> G2model = ql.G2(termStructure)
>>>> HWmodel = ql.HullWhite(termStructure)
>>>> HWmodel2 = ql.HullWhite(termStructure)
>>>> BKmodel = ql.BlackKarasinski(termStructure)
>>>>
>>>> # ### Calibrations
>>>>
>>>> for h in helpers:
>>>> h.setPricingEngine(ql.G2SwaptionEngine(G2model, 6.0, 16))
>>>> calibrate(G2model, helpers, 0.05, "G2 (analytic formulae)")
>>>>
>>>> for h in helpers:
>>>> h.setPricingEngine(ql.JamshidianSwaptionEngine(HWmodel))
>>>> calibrate(HWmodel, helpers, 0.05, "Hull-White (analytic formulae)")
>>>>
>>>> for h in helpers:
>>>> h.setPricingEngine(ql.TreeSwaptionEngine(HWmodel2, grid))
>>>> calibrate(HWmodel2, helpers, 0.05, "Hull-White (numerical calibration)")
>>>>
>>>> for h in helpers:
>>>> h.setPricingEngine(ql.TreeSwaptionEngine(BKmodel, grid))
>>>> calibrate(BKmodel, helpers, 0.05, "Black-Karasinski (numerical calibration)")
>>>>
>>>>
>>>> # ### Price Bermudan swaptions on defined swaps
>>>>
>>>> bermudanDates = [d for d in fixedSchedule][:-1]
>>>> exercise = ql.BermudanExercise(bermudanDates)
>>>>
>>>> atmSwaption = ql.Swaption(atmSwap, exercise)
>>>> otmSwaption = ql.Swaption(otmSwap, exercise)
>>>> itmSwaption = ql.Swaption(itmSwap, exercise)
>>>>
>>>> data = []
>>>>
>>>> # +
>>>> atmSwaption.setPricingEngine(ql.TreeSwaptionEngine(G2model, 50))
>>>> otmSwaption.setPricingEngine(ql.TreeSwaptionEngine(G2model, 50))
>>>> itmSwaption.setPricingEngine(ql.TreeSwaptionEngine(G2model, 50))
>>>>
>>>> data.append(("G2 analytic", itmSwaption.NPV(), atmSwaption.NPV(), otmSwaption.NPV()))
>>>>
>>>> # +
>>>> atmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel, 50))
>>>> otmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel, 50))
>>>> itmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel, 50))
>>>>
>>>> data.append(("HW analytic", itmSwaption.NPV(), atmSwaption.NPV(), otmSwaption.NPV()))
>>>>
>>>> # +
>>>> atmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel2, 50))
>>>> otmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel2, 50))
>>>> itmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel2, 50))
>>>>
>>>> data.append(("HW numerical", itmSwaption.NPV(), atmSwaption.NPV(), otmSwaption.NPV()))
>>>>
>>>> # +
>>>> atmSwaption.setPricingEngine(ql.TreeSwaptionEngine(BKmodel, 50))
>>>> otmSwaption.setPricingEngine(ql.TreeSwaptionEngine(BKmodel, 50))
>>>> itmSwaption.setPricingEngine(ql.TreeSwaptionEngine(BKmodel, 50))
>>>>
>>>> data.append(("BK numerical", itmSwaption.NPV(), atmSwaption.NPV(), otmSwaption.NPV()))
>>>> # -
>>>>
>>>> print(pd.DataFrame(data, columns=["model", "in-the-money", "at-the-money", "out-of-the-money"]))
>>>>
>>>> # ### GSR live setValue bump test
>>>>
>>>> import faulthandler
>>>> faulthandler.enable(all_threads=True)
>>>>
>>>> print("\n--- GSR live setValue bump test ---")
>>>>
>>>> # Minimal European swaption test on the same underlying ATM swap
>>>> euroExercise = ql.EuropeanExercise(swapStart)
>>>> euroSwaption = ql.Swaption(atmSwap, euroExercise)
>>>>
>>>> # For a European GSR setup: no step dates, one sigma, one reversion
>>>> stepDates = []
>>>> sigmas = [ql.QuoteHandle(ql.SimpleQuote(0.01))]
>>>> reversions = [ql.QuoteHandle(ql.SimpleQuote(0.01))]
>>>>
>>>> gsr = ql.Gsr(termStructure, stepDates, sigmas, reversions)
>>>> gsrEngine = ql.Gaussian1dSwaptionEngine(
>>>> gsr,
>>>> 64, # integration points
>>>> 7.0, # std devs
>>>> True,
>>>> False,
>>>> termStructure
>>>> )
>>>> euroSwaption.setPricingEngine(gsrEngine)
>>>>
>>>> base_rate = 0.04875825
>>>>
>>>> print("base rate:", base_rate)
>>>> print("base npv :", euroSwaption.NPV())
>>>>
>>>> # Single bump up/down
>>>> for bump in [1e-4, -1e-4, 0.0]:
>>>> bumped = base_rate + bump
>>>> print(f"\nsetValue -> {bumped:.8f}")
>>>> rateQuote.setValue(bumped)
>>>> print("npv =", euroSwaption.NPV())
>>>>
>>>> # Stress loop: repeatedly bump the observed quote and reprice
>>>> print("\nstarting stress loop...")
>>>> for i in range(200):
>>>> bumped = base_rate + (1e-4 if i % 2 == 0 else -1e-4)
>>>> rateQuote.setValue(bumped)
>>>> npv = euroSwaption.NPV()
>>>> if i % 20 == 0:
>>>> print(f"iter {i:03d} rate={bumped:.8f} npv={npv}")
>>>>
>>>> # Restore
>>>> rateQuote.setValue(base_rate)
>>>> print("\nrestored rate:", base_rate)
>>>> print("restored npv :", euroSwaption.NPV())
>>>>
>>>>
>>>> Log:
>>>>
>>>> aleksisaliraza@Alis-MacBook-Pro:~/QuantLib-SWIG-1.42/Python/examples$ python3.14 bermudan-swaption.py
>>>> Model: G2 (analytic formulae)
>>>> Parameters: [ 0.0765369; 0.00279407; 0.0619638; 0.00954892; -1 ]
>>>> maturity length volatility implied error
>>>> 0 1Y 5Y 0.1148 0.101974 -0.012826
>>>> 1 2Y 4Y 0.1108 0.105611 -0.005189
>>>> 2 3Y 3Y 0.1070 0.106950 -0.000050
>>>> 3 4Y 2Y 0.1021 0.107873 0.005773
>>>> 4 5Y 1Y 0.1000 0.108556 0.008556
>>>> Average error: 0.0065
>>>> Model: Hull-White (analytic formulae)
>>>> Parameters: [ 0.046379; 0.00586848 ]
>>>> maturity length volatility implied error
>>>> 0 1Y 5Y 0.1148 0.106223 -0.008577
>>>> 1 2Y 4Y 0.1108 0.106292 -0.004508
>>>> 2 3Y 3Y 0.1070 0.106338 -0.000662
>>>> 3 4Y 2Y 0.1021 0.106439 0.004339
>>>> 4 5Y 1Y 0.1000 0.106609 0.006609
>>>> Average error: 0.0049
>>>> Model: Hull-White (numerical calibration)
>>>> Parameters: [ 0.0559314; 0.00609858 ]
>>>> maturity length volatility implied error
>>>> 0 1Y 5Y 0.1148 0.102941 -0.011859
>>>> 1 2Y 4Y 0.1108 0.105452 -0.005348
>>>> 2 3Y 3Y 0.1070 0.106560 -0.000440
>>>> 3 4Y 2Y 0.1021 0.107365 0.005265
>>>> 4 5Y 1Y 0.1000 0.108223 0.008223
>>>> Average error: 0.0062
>>>> Model: Black-Karasinski (numerical calibration)
>>>> Parameters: [ 0.0442393; 0.120656 ]
>>>> maturity length volatility implied error
>>>> 0 1Y 5Y 0.1148 0.103094 -0.011706
>>>> 1 2Y 4Y 0.1108 0.105637 -0.005163
>>>> 2 3Y 3Y 0.1070 0.106656 -0.000344
>>>> 3 4Y 2Y 0.1021 0.107333 0.005233
>>>> 4 5Y 1Y 0.1000 0.108028 0.008028
>>>> Average error: 0.0061
>>>> model in-the-money at-the-money out-of-the-money
>>>> 0 G2 analytic 42.919620 14.581452 3.443306
>>>> 1 HW analytic 42.245449 12.924016 2.512541
>>>> 2 HW numerical 42.340450 13.141040 2.614228
>>>> 3 BK numerical 41.805673 13.012682 3.271628
>>>>
>>>> --- GSR live setValue bump test ---
>>>> base rate: 0.04875825
>>>> base npv : 16.787915348295005
>>>>
>>>> setValue -> 0.04885825
>>>> Fatal Python error: Segmentation fault
>>>>
>>>> Current thread 0x00000001f0afd8c0 (most recent call first):
>>>> File "/opt/homebrew/lib/python3.14/site-packages/QuantLib/QuantLib.py", line 6180 in setValue
>>>> File "/Users/aleksisaliraza/QuantLib-SWIG-1.42/Python/examples/bermudan-swaption.py", line 277 in <module>
>>>>
>>>> Current thread's C stack trace (most recent call first):
>>>> Binary file "/opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/Python", at _Py_DumpStack+0x44 [0x1036d79d8]
>>>> Binary file "/opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/Python", at faulthandler_dump_c_stack+0x58 [0x1036e9f0c]
>>>> Binary file "/opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/Python", at faulthandler_fatal_error+0x140 [0x1036e9dd0]
>>>> Binary file "/usr/lib/system/libsystem_platform.dylib", at _sigtramp+0x38 [0x184cfd7a4]
>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>>> <truncated rest of calls>
>>>>
>>>> Extension modules: QuantLib._QuantLib, numpy._core._multiarray_umath, numpy.linalg._umath_linalg, numpy.random._common, numpy.random.bit_generator, numpy.random._bounded_integers, numpy.random._pcg64, numpy.random._mt19937, numpy.random._generator, numpy.random._philox, numpy.random._sfc64, numpy.random.mtrand, pandas._libs.tslibs.ccalendar, pandas._libs.tslibs.np_datetime, pandas._libs.tslibs.dtypes, pandas._libs.tslibs.base, pandas._libs.tslibs.nattype, pandas._libs.tslibs.timezones, pandas._libs.tslibs.fields, pandas._libs.tslibs.timedeltas, pandas._libs.tslibs.tzconversion, pandas._libs.tslibs.timestamps, pandas._libs.properties, pandas._libs.tslibs.offsets, pandas._libs.tslibs.strptime, pandas._libs.tslibs.parsing, pandas._libs.tslibs.conversion, pandas._libs.tslibs.period, pandas._libs.tslibs.vectorized, pandas._libs.ops_dispatch, pandas._libs.missing, pandas._libs.hashtable, pandas._libs.algos, pandas._libs.interval, pandas._libs.lib, pandas._libs.ops, pandas._libs.hashing, pandas._libs.arrays, pandas._libs.tslib, pandas._libs.sparse, pandas._libs.internals, pandas._libs.indexing, pandas._libs.index, pandas._libs.writers, pandas._libs.join, pandas._libs.window.aggregations, pandas._libs.window.indexers, pandas._libs.reshape, pandas._libs.groupby, pandas._libs.json, pandas._libs.parsers, pandas._libs.testing (total: 52)
>>>> Segmentation fault: 11
>>>>
>>>>
>>>> _______________________________________________
>>>> QuantLib-users mailing list
>>>> Qua...@li...
>>>> https://lists.sourceforge.net/lists/listinfo/quantlib-users
>>>>
>>>
>>> _______________________________________________
>>> QuantLib-users mailing list
>>> Qua...@li...
>>> https://lists.sourceforge.net/lists/listinfo/quantlib-users
>>>
>>
>
|
|
From: Aleksis A. R. <ale...@go...> - 2026-04-16 09:32:36
|
yes, the results tie with v1.40 after the code fix in 1.42.
here's a comparison run for the test script i sent earlier.
--- GSR live setValue bump test v1.42---
base rate: 0.04875825
base npv : 16.787915348295005
setValue -> 0.04885825
npv = 17.005154111913548
setValue -> 0.04865825
npv = 16.57057870270632
setValue -> 0.04875825
npv = 16.787915348295005
starting stress loop...
iter 000 rate=0.04885825 npv=17.005154111913548
iter 020 rate=0.04885825 npv=17.005154111913548
iter 040 rate=0.04885825 npv=17.005154111913548
iter 060 rate=0.04885825 npv=17.005154111913548
iter 080 rate=0.04885825 npv=17.005154111913548
iter 100 rate=0.04885825 npv=17.005154111913548
iter 120 rate=0.04885825 npv=17.005154111913548
iter 140 rate=0.04885825 npv=17.005154111913548
iter 160 rate=0.04885825 npv=17.005154111913548
iter 180 rate=0.04885825 npv=17.005154111913548
restored rate: 0.04875825
restored npv : 16.787915348295005
--- GSR live setValue bump test v1.40 ---
base rate: 0.04875825
base npv : 16.787915348295005
setValue -> 0.04885825
npv = 17.005154111913548
setValue -> 0.04865825
npv = 16.57057870270632
setValue -> 0.04875825
npv = 16.787915348295005
starting stress loop...
iter 000 rate=0.04885825 npv=17.005154111913548
iter 020 rate=0.04885825 npv=17.005154111913548
iter 040 rate=0.04885825 npv=17.005154111913548
iter 060 rate=0.04885825 npv=17.005154111913548
iter 080 rate=0.04885825 npv=17.005154111913548
iter 100 rate=0.04885825 npv=17.005154111913548
iter 120 rate=0.04885825 npv=17.005154111913548
iter 140 rate=0.04885825 npv=17.005154111913548
iter 160 rate=0.04885825 npv=17.005154111913548
iter 180 rate=0.04885825 npv=17.005154111913548
restored rate: 0.04875825
restored npv : 16.787915348295005
> On Apr 16, 2026, at 12:38, Luigi Ballabio <lui...@gm...> wrote:
>
> May you check that it also reprices correctly after the bump? That is, that you get the same numerical results as 1.40?
>
> On Thu, Apr 16, 2026 at 9:04 AM Luigi Ballabio <lui...@gm... <mailto:lui...@gm...>> wrote:
>> Thanks for the heads-up. Looks like I need to release a 1.42.1...
>>
>> Luigi
>>
>>
>> On Thu, Apr 16, 2026 at 8:57 AM Aleksis Ali Raza via QuantLib-users <qua...@li... <mailto:qua...@li...>> wrote:
>>> Yep, ditching registerWith(stateProcess_); works fine as well and is the cleaner fix.
>>>
>>> thanks.
>>>
>>>> On Apr 16, 2026, at 11:34, Peter Caspers <pca...@gm... <mailto:pca...@gm...>> wrote:
>>>>
>>>> Thanks. Since Gsr manages its stateProcess_, it does not need to and should not register with it as an Observer? I will take a closer look, but if you could test this change on your end, that would be valuable input.
>>>> Best, Peter
>>>>
>>>>
>>>> Aleksis Ali Raza via QuantLib-users <qua...@li... <mailto:qua...@li...>> schrieb am Do. 16. Apr. 2026 um 08:04:
>>>>> Just to follow up on this, it's this line:
>>>>>
>>>>> void Gsr::update() {
>>>>> if (stateProcess_ != nullptr) {
>>>>> ext::static_pointer_cast<GsrProcess>(stateProcess_)->flushCache();
>>>>> ext::static_pointer_cast<GsrProcess>(stateProcess_)->notifyObservers();
>>>>> }
>>>>> LazyObject::update();
>>>>> }
>>>>>
>>>>> in the gsr class that seems to be the cause. in my case, commenting out that line out and rebuilding from source fixes the problem of my gsr-based swaption risk runs.
>>>>>
>>>>> That line by itself looks pretty harmless but i think
>>>>> registerWith(stateProcess_);
>>>>>
>>>>> further down leads into some sort of feedback loop (setValue() → notifyObservers() → Gsr::update() → stateProcess_->notifyObservers() → Gsr::update() → …) and ultimately a seg fault.
>>>>>
>>>>>
>>>>>> On Apr 16, 2026, at 09:13, Aleksis Ali Raza <ale...@go... <mailto:ale...@go...>> wrote:
>>>>>>
>>>>>> Hi,
>>>>>>
>>>>>> I’m encountering what appears to be a regression or stability issue in QuantLib 1.42 related to the GSR (Gaussian short rate) model and the observer framework.
>>>>>>
>>>>>> Calling SimpleQuote.setValue() on a quote that is observed by a term structure used in a Gsr model causes a segmentation fault.
>>>>>>
>>>>>> This occurs even in a minimal standalone script (no threading, no external frameworks).
>>>>>>
>>>>>> Minimal reproduction
>>>>>>
>>>>>> The following sequence is sufficient:
>>>>>> 1. Create a SimpleQuote
>>>>>> 2. Build a FlatForward curve using that quote
>>>>>> 3. Construct a Gsr model from that term structure
>>>>>> 4. Attach a Gaussian1dSwaptionEngine
>>>>>> 5. Call setValue() on the original SimpleQuote
>>>>>>
>>>>>> This results in a segmentation fault.
>>>>>>
>>>>>> I have modified the Python/examples/bermudan_swaption.py file and attached it below to illustrate (along with error log below it).
>>>>>>
>>>>>> Has something been changed in how one shud enquire / update quote objects that I missed? I am coming from 1.40, where this was not an issue. I am running python 3.14.4 on macOS 26.1.4 and using a source build of Quantlib/SWIG.
>>>>>>
>>>>>> Thanks, Aleksis
>>>>>>
>>>>>> # ---
>>>>>> # jupyter:
>>>>>> # jupytext:
>>>>>> # formats: py:light
>>>>>> # text_representation:
>>>>>> # extension: .py
>>>>>> # format_name: light
>>>>>> # format_version: '1.5'
>>>>>> # jupytext_version: 1.4.2
>>>>>> # kernelspec:
>>>>>> # display_name: Python 3
>>>>>> # language: python
>>>>>> # name: python3
>>>>>> # ---
>>>>>>
>>>>>> # # Bermudan swaptions
>>>>>> #
>>>>>> # Copyright (©) 2004, 2005, 2006, 2007 StatPro Italia srl
>>>>>> #
>>>>>> # This file is part of QuantLib, a free-software/open-source library
>>>>>> # for financial quantitative analysts and developers - https://www.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
>>>>>> # <qua...@li... <mailto:qua...@li...>>. The license is also available online at
>>>>>> # <https://www.quantlib.org/license.shtml>.
>>>>>> #
>>>>>> # 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.
>>>>>>
>>>>>> import QuantLib as ql
>>>>>> import pandas as pd
>>>>>>
>>>>>> # ### Setup
>>>>>>
>>>>>> todaysDate = ql.Date(15, ql.February, 2002)
>>>>>> ql.Settings.instance().evaluationDate = todaysDate
>>>>>> calendar = ql.TARGET()
>>>>>> settlementDate = ql.Date(19, ql.February, 2002)
>>>>>>
>>>>>>
>>>>>> def calibrate(model, helpers, l, name):
>>>>>> print("Model: %s" % name)
>>>>>>
>>>>>> method = ql.Simplex(l)
>>>>>> model.calibrate(helpers, method, ql.EndCriteria(1000, 250, 1e-7, 1e-7, 1e-7))
>>>>>>
>>>>>> print("Parameters: %s" % model.params())
>>>>>>
>>>>>> totalError = 0.0
>>>>>> data = []
>>>>>> for swaption, helper in zip(swaptionVols, helpers):
>>>>>> maturity, length, vol = swaption
>>>>>> NPV = helper.modelValue()
>>>>>> implied = helper.impliedVolatility(NPV, 1.0e-4, 1000, 0.05, 0.50)
>>>>>> error = implied - vol
>>>>>> totalError += abs(error)
>>>>>> data.append((maturity, length, vol, implied, error))
>>>>>> averageError = totalError / len(helpers)
>>>>>>
>>>>>> print(pd.DataFrame(data, columns=["maturity", "length", "volatility", "implied", "error"]))
>>>>>>
>>>>>> print("Average error: %.4f" % averageError)
>>>>>>
>>>>>>
>>>>>> # ### Market data
>>>>>>
>>>>>> swaptionVols = [
>>>>>> # maturity, length, volatility
>>>>>> (ql.Period(1, ql.Years), ql.Period(5, ql.Years), 0.1148),
>>>>>> (ql.Period(2, ql.Years), ql.Period(4, ql.Years), 0.1108),
>>>>>> (ql.Period(3, ql.Years), ql.Period(3, ql.Years), 0.1070),
>>>>>> (ql.Period(4, ql.Years), ql.Period(2, ql.Years), 0.1021),
>>>>>> (ql.Period(5, ql.Years), ql.Period(1, ql.Years), 0.1000),
>>>>>> ]
>>>>>>
>>>>>> # This is a flat yield term structure implying a 1x5 swap at 5%.
>>>>>>
>>>>>> rateQuote = ql.SimpleQuote(0.04875825)
>>>>>> rate = ql.QuoteHandle(rateQuote)
>>>>>> termStructure = ql.YieldTermStructureHandle(
>>>>>> ql.FlatForward(settlementDate, rate, ql.Actual365Fixed())
>>>>>> )
>>>>>>
>>>>>> # Define the ATM/OTM/ITM swaps:
>>>>>>
>>>>>> swapEngine = ql.DiscountingSwapEngine(termStructure)
>>>>>>
>>>>>> fixedLegFrequency = ql.Annual
>>>>>> fixedLegTenor = ql.Period(1, ql.Years)
>>>>>> fixedLegConvention = ql.Unadjusted
>>>>>> floatingLegConvention = ql.ModifiedFollowing
>>>>>> fixedLegDayCounter = ql.Thirty360(ql.Thirty360.European)
>>>>>> floatingLegFrequency = ql.Semiannual
>>>>>> floatingLegTenor = ql.Period(6, ql.Months)
>>>>>>
>>>>>> payFixed = ql.Swap.Payer
>>>>>> fixingDays = 2
>>>>>> index = ql.Euribor6M(termStructure)
>>>>>> floatingLegDayCounter = index.dayCounter()
>>>>>>
>>>>>> swapStart = calendar.advance(settlementDate, 1, ql.Years, floatingLegConvention)
>>>>>> swapEnd = calendar.advance(swapStart, 5, ql.Years, floatingLegConvention)
>>>>>>
>>>>>> fixedSchedule = ql.Schedule(
>>>>>> swapStart,
>>>>>> swapEnd,
>>>>>> fixedLegTenor,
>>>>>> calendar,
>>>>>> fixedLegConvention,
>>>>>> fixedLegConvention,
>>>>>> ql.DateGeneration.Forward,
>>>>>> False,
>>>>>> )
>>>>>> floatingSchedule = ql.Schedule(
>>>>>> swapStart,
>>>>>> swapEnd,
>>>>>> floatingLegTenor,
>>>>>> calendar,
>>>>>> floatingLegConvention,
>>>>>> floatingLegConvention,
>>>>>> ql.DateGeneration.Forward,
>>>>>> False,
>>>>>> )
>>>>>>
>>>>>> dummy = ql.VanillaSwap(
>>>>>> payFixed, 100.0, fixedSchedule, 0.0, fixedLegDayCounter, floatingSchedule, index, 0.0, floatingLegDayCounter
>>>>>> )
>>>>>> dummy.setPricingEngine(swapEngine)
>>>>>> atmRate = dummy.fairRate()
>>>>>>
>>>>>> atmSwap = ql.VanillaSwap(
>>>>>> payFixed, 1000.0, fixedSchedule, atmRate, fixedLegDayCounter,
>>>>>> floatingSchedule, index, 0.0, floatingLegDayCounter
>>>>>> )
>>>>>>
>>>>>> otmSwap = ql.VanillaSwap(
>>>>>> payFixed, 1000.0, fixedSchedule, atmRate * 1.2, fixedLegDayCounter,
>>>>>> floatingSchedule, index, 0.0, floatingLegDayCounter
>>>>>> )
>>>>>>
>>>>>> itmSwap = ql.VanillaSwap(
>>>>>> payFixed, 1000.0, fixedSchedule, atmRate * 0.8, fixedLegDayCounter,
>>>>>> floatingSchedule, index, 0.0, floatingLegDayCounter
>>>>>> )
>>>>>>
>>>>>> atmSwap.setPricingEngine(swapEngine)
>>>>>> otmSwap.setPricingEngine(swapEngine)
>>>>>> itmSwap.setPricingEngine(swapEngine)
>>>>>>
>>>>>> helpers = [
>>>>>> ql.SwaptionHelper(
>>>>>> maturity,
>>>>>> length,
>>>>>> ql.makeQuoteHandle(vol),
>>>>>> index,
>>>>>> index.tenor(),
>>>>>> index.dayCounter(),
>>>>>> index.dayCounter(),
>>>>>> termStructure,
>>>>>> )
>>>>>> for maturity, length, vol in swaptionVols
>>>>>> ]
>>>>>>
>>>>>> times = {}
>>>>>> for h in helpers:
>>>>>> for t in h.times():
>>>>>> times[t] = 1
>>>>>> times = sorted(times.keys())
>>>>>>
>>>>>> grid = ql.TimeGrid(times, 30)
>>>>>>
>>>>>> G2model = ql.G2(termStructure)
>>>>>> HWmodel = ql.HullWhite(termStructure)
>>>>>> HWmodel2 = ql.HullWhite(termStructure)
>>>>>> BKmodel = ql.BlackKarasinski(termStructure)
>>>>>>
>>>>>> # ### Calibrations
>>>>>>
>>>>>> for h in helpers:
>>>>>> h.setPricingEngine(ql.G2SwaptionEngine(G2model, 6.0, 16))
>>>>>> calibrate(G2model, helpers, 0.05, "G2 (analytic formulae)")
>>>>>>
>>>>>> for h in helpers:
>>>>>> h.setPricingEngine(ql.JamshidianSwaptionEngine(HWmodel))
>>>>>> calibrate(HWmodel, helpers, 0.05, "Hull-White (analytic formulae)")
>>>>>>
>>>>>> for h in helpers:
>>>>>> h.setPricingEngine(ql.TreeSwaptionEngine(HWmodel2, grid))
>>>>>> calibrate(HWmodel2, helpers, 0.05, "Hull-White (numerical calibration)")
>>>>>>
>>>>>> for h in helpers:
>>>>>> h.setPricingEngine(ql.TreeSwaptionEngine(BKmodel, grid))
>>>>>> calibrate(BKmodel, helpers, 0.05, "Black-Karasinski (numerical calibration)")
>>>>>>
>>>>>>
>>>>>> # ### Price Bermudan swaptions on defined swaps
>>>>>>
>>>>>> bermudanDates = [d for d in fixedSchedule][:-1]
>>>>>> exercise = ql.BermudanExercise(bermudanDates)
>>>>>>
>>>>>> atmSwaption = ql.Swaption(atmSwap, exercise)
>>>>>> otmSwaption = ql.Swaption(otmSwap, exercise)
>>>>>> itmSwaption = ql.Swaption(itmSwap, exercise)
>>>>>>
>>>>>> data = []
>>>>>>
>>>>>> # +
>>>>>> atmSwaption.setPricingEngine(ql.TreeSwaptionEngine(G2model, 50))
>>>>>> otmSwaption.setPricingEngine(ql.TreeSwaptionEngine(G2model, 50))
>>>>>> itmSwaption.setPricingEngine(ql.TreeSwaptionEngine(G2model, 50))
>>>>>>
>>>>>> data.append(("G2 analytic", itmSwaption.NPV(), atmSwaption.NPV(), otmSwaption.NPV()))
>>>>>>
>>>>>> # +
>>>>>> atmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel, 50))
>>>>>> otmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel, 50))
>>>>>> itmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel, 50))
>>>>>>
>>>>>> data.append(("HW analytic", itmSwaption.NPV(), atmSwaption.NPV(), otmSwaption.NPV()))
>>>>>>
>>>>>> # +
>>>>>> atmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel2, 50))
>>>>>> otmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel2, 50))
>>>>>> itmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel2, 50))
>>>>>>
>>>>>> data.append(("HW numerical", itmSwaption.NPV(), atmSwaption.NPV(), otmSwaption.NPV()))
>>>>>>
>>>>>> # +
>>>>>> atmSwaption.setPricingEngine(ql.TreeSwaptionEngine(BKmodel, 50))
>>>>>> otmSwaption.setPricingEngine(ql.TreeSwaptionEngine(BKmodel, 50))
>>>>>> itmSwaption.setPricingEngine(ql.TreeSwaptionEngine(BKmodel, 50))
>>>>>>
>>>>>> data.append(("BK numerical", itmSwaption.NPV(), atmSwaption.NPV(), otmSwaption.NPV()))
>>>>>> # -
>>>>>>
>>>>>> print(pd.DataFrame(data, columns=["model", "in-the-money", "at-the-money", "out-of-the-money"]))
>>>>>>
>>>>>> # ### GSR live setValue bump test
>>>>>>
>>>>>> import faulthandler
>>>>>> faulthandler.enable(all_threads=True)
>>>>>>
>>>>>> print("\n--- GSR live setValue bump test ---")
>>>>>>
>>>>>> # Minimal European swaption test on the same underlying ATM swap
>>>>>> euroExercise = ql.EuropeanExercise(swapStart)
>>>>>> euroSwaption = ql.Swaption(atmSwap, euroExercise)
>>>>>>
>>>>>> # For a European GSR setup: no step dates, one sigma, one reversion
>>>>>> stepDates = []
>>>>>> sigmas = [ql.QuoteHandle(ql.SimpleQuote(0.01))]
>>>>>> reversions = [ql.QuoteHandle(ql.SimpleQuote(0.01))]
>>>>>>
>>>>>> gsr = ql.Gsr(termStructure, stepDates, sigmas, reversions)
>>>>>> gsrEngine = ql.Gaussian1dSwaptionEngine(
>>>>>> gsr,
>>>>>> 64, # integration points
>>>>>> 7.0, # std devs
>>>>>> True,
>>>>>> False,
>>>>>> termStructure
>>>>>> )
>>>>>> euroSwaption.setPricingEngine(gsrEngine)
>>>>>>
>>>>>> base_rate = 0.04875825
>>>>>>
>>>>>> print("base rate:", base_rate)
>>>>>> print("base npv :", euroSwaption.NPV())
>>>>>>
>>>>>> # Single bump up/down
>>>>>> for bump in [1e-4, -1e-4, 0.0]:
>>>>>> bumped = base_rate + bump
>>>>>> print(f"\nsetValue -> {bumped:.8f}")
>>>>>> rateQuote.setValue(bumped)
>>>>>> print("npv =", euroSwaption.NPV())
>>>>>>
>>>>>> # Stress loop: repeatedly bump the observed quote and reprice
>>>>>> print("\nstarting stress loop...")
>>>>>> for i in range(200):
>>>>>> bumped = base_rate + (1e-4 if i % 2 == 0 else -1e-4)
>>>>>> rateQuote.setValue(bumped)
>>>>>> npv = euroSwaption.NPV()
>>>>>> if i % 20 == 0:
>>>>>> print(f"iter {i:03d} rate={bumped:.8f} npv={npv}")
>>>>>>
>>>>>> # Restore
>>>>>> rateQuote.setValue(base_rate)
>>>>>> print("\nrestored rate:", base_rate)
>>>>>> print("restored npv :", euroSwaption.NPV())
>>>>>>
>>>>>> Log:
>>>>>> aleksisaliraza@Alis-MacBook-Pro:~/QuantLib-SWIG-1.42/Python/examples$ python3.14 bermudan-swaption.py
>>>>>> Model: G2 (analytic formulae)
>>>>>> Parameters: [ 0.0765369; 0.00279407; 0.0619638; 0.00954892; -1 ]
>>>>>> maturity length volatility implied error
>>>>>> 0 1Y 5Y 0.1148 0.101974 -0.012826
>>>>>> 1 2Y 4Y 0.1108 0.105611 -0.005189
>>>>>> 2 3Y 3Y 0.1070 0.106950 -0.000050
>>>>>> 3 4Y 2Y 0.1021 0.107873 0.005773
>>>>>> 4 5Y 1Y 0.1000 0.108556 0.008556
>>>>>> Average error: 0.0065
>>>>>> Model: Hull-White (analytic formulae)
>>>>>> Parameters: [ 0.046379; 0.00586848 ]
>>>>>> maturity length volatility implied error
>>>>>> 0 1Y 5Y 0.1148 0.106223 -0.008577
>>>>>> 1 2Y 4Y 0.1108 0.106292 -0.004508
>>>>>> 2 3Y 3Y 0.1070 0.106338 -0.000662
>>>>>> 3 4Y 2Y 0.1021 0.106439 0.004339
>>>>>> 4 5Y 1Y 0.1000 0.106609 0.006609
>>>>>> Average error: 0.0049
>>>>>> Model: Hull-White (numerical calibration)
>>>>>> Parameters: [ 0.0559314; 0.00609858 ]
>>>>>> maturity length volatility implied error
>>>>>> 0 1Y 5Y 0.1148 0.102941 -0.011859
>>>>>> 1 2Y 4Y 0.1108 0.105452 -0.005348
>>>>>> 2 3Y 3Y 0.1070 0.106560 -0.000440
>>>>>> 3 4Y 2Y 0.1021 0.107365 0.005265
>>>>>> 4 5Y 1Y 0.1000 0.108223 0.008223
>>>>>> Average error: 0.0062
>>>>>> Model: Black-Karasinski (numerical calibration)
>>>>>> Parameters: [ 0.0442393; 0.120656 ]
>>>>>> maturity length volatility implied error
>>>>>> 0 1Y 5Y 0.1148 0.103094 -0.011706
>>>>>> 1 2Y 4Y 0.1108 0.105637 -0.005163
>>>>>> 2 3Y 3Y 0.1070 0.106656 -0.000344
>>>>>> 3 4Y 2Y 0.1021 0.107333 0.005233
>>>>>> 4 5Y 1Y 0.1000 0.108028 0.008028
>>>>>> Average error: 0.0061
>>>>>> model in-the-money at-the-money out-of-the-money
>>>>>> 0 G2 analytic 42.919620 14.581452 3.443306
>>>>>> 1 HW analytic 42.245449 12.924016 2.512541
>>>>>> 2 HW numerical 42.340450 13.141040 2.614228
>>>>>> 3 BK numerical 41.805673 13.012682 3.271628
>>>>>>
>>>>>> --- GSR live setValue bump test ---
>>>>>> base rate: 0.04875825
>>>>>> base npv : 16.787915348295005
>>>>>>
>>>>>> setValue -> 0.04885825
>>>>>> Fatal Python error: Segmentation fault
>>>>>>
>>>>>> Current thread 0x00000001f0afd8c0 (most recent call first):
>>>>>> File "/opt/homebrew/lib/python3.14/site-packages/QuantLib/QuantLib.py", line 6180 in setValue
>>>>>> File "/Users/aleksisaliraza/QuantLib-SWIG-1.42/Python/examples/bermudan-swaption.py", line 277 in <module>
>>>>>>
>>>>>> Current thread's C stack trace (most recent call first):
>>>>>> Binary file "/opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/Python", at _Py_DumpStack+0x44 [0x1036d79d8]
>>>>>> Binary file "/opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/Python", at faulthandler_dump_c_stack+0x58 [0x1036e9f0c]
>>>>>> Binary file "/opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/Python", at faulthandler_fatal_error+0x140 [0x1036e9dd0]
>>>>>> Binary file "/usr/lib/system/libsystem_platform.dylib", at _sigtramp+0x38 [0x184cfd7a4]
>>>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>>>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>>>>> <truncated rest of calls>
>>>>>>
>>>>>> Extension modules: QuantLib._QuantLib, numpy._core._multiarray_umath, numpy.linalg._umath_linalg, numpy.random._common, numpy.random.bit_generator, numpy.random._bounded_integers, numpy.random._pcg64, numpy.random._mt19937, numpy.random._generator, numpy.random._philox, numpy.random._sfc64, numpy.random.mtrand, pandas._libs.tslibs.ccalendar, pandas._libs.tslibs.np_datetime, pandas._libs.tslibs.dtypes, pandas._libs.tslibs.base, pandas._libs.tslibs.nattype, pandas._libs.tslibs.timezones, pandas._libs.tslibs.fields, pandas._libs.tslibs.timedeltas, pandas._libs.tslibs.tzconversion, pandas._libs.tslibs.timestamps, pandas._libs.properties, pandas._libs.tslibs.offsets, pandas._libs.tslibs.strptime, pandas._libs.tslibs.parsing, pandas._libs.tslibs.conversion, pandas._libs.tslibs.period, pandas._libs.tslibs.vectorized, pandas._libs.ops_dispatch, pandas._libs.missing, pandas._libs.hashtable, pandas._libs.algos, pandas._libs.interval, pandas._libs.lib, pandas._libs.ops, pandas._libs.hashing, pandas._libs.arrays, pandas._libs.tslib, pandas._libs.sparse, pandas._libs.internals, pandas._libs.indexing, pandas._libs.index, pandas._libs.writers, pandas._libs.join, pandas._libs.window.aggregations, pandas._libs.window.indexers, pandas._libs.reshape, pandas._libs.groupby, pandas._libs.json, pandas._libs.parsers, pandas._libs.testing (total: 52)
>>>>>> Segmentation fault: 11
>>>>>
>>>>> _______________________________________________
>>>>> QuantLib-users mailing list
>>>>> Qua...@li... <mailto:Qua...@li...>
>>>>> https://lists.sourceforge.net/lists/listinfo/quantlib-users
>>>
>>> _______________________________________________
>>> QuantLib-users mailing list
>>> Qua...@li... <mailto:Qua...@li...>
>>> https://lists.sourceforge.net/lists/listinfo/quantlib-users
|
|
From: Luigi B. <lui...@gm...> - 2026-04-16 07:39:24
|
May you check that it also reprices correctly after the bump? That is, that
you get the same numerical results as 1.40?
On Thu, Apr 16, 2026 at 9:04 AM Luigi Ballabio <lui...@gm...>
wrote:
> Thanks for the heads-up. Looks like I need to release a 1.42.1...
>
> Luigi
>
>
> On Thu, Apr 16, 2026 at 8:57 AM Aleksis Ali Raza via QuantLib-users <
> qua...@li...> wrote:
>
>> Yep, ditching registerWith(stateProcess_); works fine as well and is the
>> cleaner fix.
>>
>> thanks.
>>
>> On Apr 16, 2026, at 11:34, Peter Caspers <pca...@gm...> wrote:
>>
>> Thanks. Since Gsr manages its stateProcess_, it does not need to and
>> should not register with it as an Observer? I will take a closer look, but
>> if you could test this change on your end, that would be valuable input.
>> Best, Peter
>>
>>
>> Aleksis Ali Raza via QuantLib-users <qua...@li...>
>> schrieb am Do. 16. Apr. 2026 um 08:04:
>>
>>> Just to follow up on this, it's this line:
>>>
>>> void Gsr::update() {
>>> if (stateProcess_ != nullptr) {
>>>
>>> ext::static_pointer_cast<GsrProcess>(stateProcess_)->flushCache();
>>>
>>> *ext::static_pointer_cast<GsrProcess>(stateProcess_)->notifyObservers();*
>>> }
>>> LazyObject::update();
>>> }
>>>
>>> in the gsr class that seems to be the cause. in my case, commenting out
>>> that line out and rebuilding from source fixes the problem of my gsr-based
>>> swaption risk runs.
>>>
>>> That line by itself looks pretty harmless but i think
>>>
>>> registerWith(stateProcess_);
>>>
>>> further down leads into some sort of feedback loop (setValue() →
>>> notifyObservers() → Gsr::update() → stateProcess_->notifyObservers() →
>>> Gsr::update() → …) and ultimately a seg fault.
>>>
>>> On Apr 16, 2026, at 09:13, Aleksis Ali Raza <ale...@go...>
>>> wrote:
>>>
>>> Hi,
>>>
>>> I’m encountering what appears to be a regression or stability issue in
>>> QuantLib 1.42 related to the GSR (Gaussian short rate) model and the
>>> observer framework.
>>>
>>> Calling SimpleQuote.setValue() on a quote that is observed by a term
>>> structure used in a Gsr model causes a segmentation fault.
>>>
>>> This occurs even in a minimal standalone script (no threading, no
>>> external frameworks).
>>>
>>> Minimal reproduction
>>>
>>> The following sequence is sufficient:
>>> 1. Create a SimpleQuote
>>> 2. Build a FlatForward curve using that quote
>>> 3. Construct a Gsr model from that term structure
>>> 4. Attach a Gaussian1dSwaptionEngine
>>> 5. Call setValue() on the original SimpleQuote
>>>
>>> This results in a segmentation fault.
>>>
>>> I have modified the Python/examples/bermudan_swaption.py file and
>>> attached it below to illustrate (along with error log below it).
>>>
>>> Has something been changed in how one shud enquire / update quote
>>> objects that I missed? I am coming from 1.40, where this was not an issue.
>>> I am running python 3.14.4 on macOS 26.1.4 and using a source build of
>>> Quantlib/SWIG.
>>>
>>> Thanks, Aleksis
>>>
>>> # ---
>>> # jupyter:
>>> # jupytext:
>>> # formats: py:light
>>> # text_representation:
>>> # extension: .py
>>> # format_name: light
>>> # format_version: '1.5'
>>> # jupytext_version: 1.4.2
>>> # kernelspec:
>>> # display_name: Python 3
>>> # language: python
>>> # name: python3
>>> # ---
>>>
>>> # # Bermudan swaptions
>>> #
>>> # Copyright (©) 2004, 2005, 2006, 2007 StatPro Italia srl
>>> #
>>> # This file is part of QuantLib, a free-software/open-source library
>>> # for financial quantitative analysts and developers - https://www.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
>>> # <qua...@li...>. The license is also available online at
>>> # <https://www.quantlib.org/license.shtml>.
>>> #
>>> # 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.
>>>
>>> import QuantLib as ql
>>> import pandas as pd
>>>
>>> # ### Setup
>>>
>>> todaysDate = ql.Date(15, ql.February, 2002)
>>> ql.Settings.instance().evaluationDate = todaysDate
>>> calendar = ql.TARGET()
>>> settlementDate = ql.Date(19, ql.February, 2002)
>>>
>>>
>>> def calibrate(model, helpers, l, name):
>>> print("Model: %s" % name)
>>>
>>> method = ql.Simplex(l)
>>> model.calibrate(helpers, method, ql.EndCriteria(1000, 250, 1e-7, 1e-7, 1e-7))
>>>
>>> print("Parameters: %s" % model.params())
>>>
>>> totalError = 0.0
>>> data = []
>>> for swaption, helper in zip(swaptionVols, helpers):
>>> maturity, length, vol = swaption
>>> NPV = helper.modelValue()
>>> implied = helper.impliedVolatility(NPV, 1.0e-4, 1000, 0.05, 0.50)
>>> error = implied - vol
>>> totalError += abs(error)
>>> data.append((maturity, length, vol, implied, error))
>>> averageError = totalError / len(helpers)
>>>
>>> print(pd.DataFrame(data, columns=["maturity", "length", "volatility", "implied", "error"]))
>>>
>>> print("Average error: %.4f" % averageError)
>>>
>>>
>>> # ### Market data
>>>
>>> swaptionVols = [
>>> # maturity, length, volatility
>>> (ql.Period(1, ql.Years), ql.Period(5, ql.Years), 0.1148),
>>> (ql.Period(2, ql.Years), ql.Period(4, ql.Years), 0.1108),
>>> (ql.Period(3, ql.Years), ql.Period(3, ql.Years), 0.1070),
>>> (ql.Period(4, ql.Years), ql.Period(2, ql.Years), 0.1021),
>>> (ql.Period(5, ql.Years), ql.Period(1, ql.Years), 0.1000),
>>> ]
>>>
>>> # This is a flat yield term structure implying a 1x5 swap at 5%.
>>>
>>> rateQuote = ql.SimpleQuote(0.04875825)
>>> rate = ql.QuoteHandle(rateQuote)
>>> termStructure = ql.YieldTermStructureHandle(
>>> ql.FlatForward(settlementDate, rate, ql.Actual365Fixed())
>>> )
>>>
>>> # Define the ATM/OTM/ITM swaps:
>>>
>>> swapEngine = ql.DiscountingSwapEngine(termStructure)
>>>
>>> fixedLegFrequency = ql.Annual
>>> fixedLegTenor = ql.Period(1, ql.Years)
>>> fixedLegConvention = ql.Unadjusted
>>> floatingLegConvention = ql.ModifiedFollowing
>>> fixedLegDayCounter = ql.Thirty360(ql.Thirty360.European)
>>> floatingLegFrequency = ql.Semiannual
>>> floatingLegTenor = ql.Period(6, ql.Months)
>>>
>>> payFixed = ql.Swap.Payer
>>> fixingDays = 2
>>> index = ql.Euribor6M(termStructure)
>>> floatingLegDayCounter = index.dayCounter()
>>>
>>> swapStart = calendar.advance(settlementDate, 1, ql.Years, floatingLegConvention)
>>> swapEnd = calendar.advance(swapStart, 5, ql.Years, floatingLegConvention)
>>>
>>> fixedSchedule = ql.Schedule(
>>> swapStart,
>>> swapEnd,
>>> fixedLegTenor,
>>> calendar,
>>> fixedLegConvention,
>>> fixedLegConvention,
>>> ql.DateGeneration.Forward,
>>> False,
>>> )
>>> floatingSchedule = ql.Schedule(
>>> swapStart,
>>> swapEnd,
>>> floatingLegTenor,
>>> calendar,
>>> floatingLegConvention,
>>> floatingLegConvention,
>>> ql.DateGeneration.Forward,
>>> False,
>>> )
>>>
>>> dummy = ql.VanillaSwap(
>>> payFixed, 100.0, fixedSchedule, 0.0, fixedLegDayCounter, floatingSchedule, index, 0.0, floatingLegDayCounter
>>> )
>>> dummy.setPricingEngine(swapEngine)
>>> atmRate = dummy.fairRate()
>>>
>>> atmSwap = ql.VanillaSwap(
>>> payFixed, 1000.0, fixedSchedule, atmRate, fixedLegDayCounter,
>>> floatingSchedule, index, 0.0, floatingLegDayCounter
>>> )
>>>
>>> otmSwap = ql.VanillaSwap(
>>> payFixed, 1000.0, fixedSchedule, atmRate * 1.2, fixedLegDayCounter,
>>> floatingSchedule, index, 0.0, floatingLegDayCounter
>>> )
>>>
>>> itmSwap = ql.VanillaSwap(
>>> payFixed, 1000.0, fixedSchedule, atmRate * 0.8, fixedLegDayCounter,
>>> floatingSchedule, index, 0.0, floatingLegDayCounter
>>> )
>>>
>>> atmSwap.setPricingEngine(swapEngine)
>>> otmSwap.setPricingEngine(swapEngine)
>>> itmSwap.setPricingEngine(swapEngine)
>>>
>>> helpers = [
>>> ql.SwaptionHelper(
>>> maturity,
>>> length,
>>> ql.makeQuoteHandle(vol),
>>> index,
>>> index.tenor(),
>>> index.dayCounter(),
>>> index.dayCounter(),
>>> termStructure,
>>> )
>>> for maturity, length, vol in swaptionVols
>>> ]
>>>
>>> times = {}
>>> for h in helpers:
>>> for t in h.times():
>>> times[t] = 1
>>> times = sorted(times.keys())
>>>
>>> grid = ql.TimeGrid(times, 30)
>>>
>>> G2model = ql.G2(termStructure)
>>> HWmodel = ql.HullWhite(termStructure)
>>> HWmodel2 = ql.HullWhite(termStructure)
>>> BKmodel = ql.BlackKarasinski(termStructure)
>>>
>>> # ### Calibrations
>>>
>>> for h in helpers:
>>> h.setPricingEngine(ql.G2SwaptionEngine(G2model, 6.0, 16))
>>> calibrate(G2model, helpers, 0.05, "G2 (analytic formulae)")
>>>
>>> for h in helpers:
>>> h.setPricingEngine(ql.JamshidianSwaptionEngine(HWmodel))
>>> calibrate(HWmodel, helpers, 0.05, "Hull-White (analytic formulae)")
>>>
>>> for h in helpers:
>>> h.setPricingEngine(ql.TreeSwaptionEngine(HWmodel2, grid))
>>> calibrate(HWmodel2, helpers, 0.05, "Hull-White (numerical calibration)")
>>>
>>> for h in helpers:
>>> h.setPricingEngine(ql.TreeSwaptionEngine(BKmodel, grid))
>>> calibrate(BKmodel, helpers, 0.05, "Black-Karasinski (numerical calibration)")
>>>
>>>
>>> # ### Price Bermudan swaptions on defined swaps
>>>
>>> bermudanDates = [d for d in fixedSchedule][:-1]
>>> exercise = ql.BermudanExercise(bermudanDates)
>>>
>>> atmSwaption = ql.Swaption(atmSwap, exercise)
>>> otmSwaption = ql.Swaption(otmSwap, exercise)
>>> itmSwaption = ql.Swaption(itmSwap, exercise)
>>>
>>> data = []
>>>
>>> # +
>>> atmSwaption.setPricingEngine(ql.TreeSwaptionEngine(G2model, 50))
>>> otmSwaption.setPricingEngine(ql.TreeSwaptionEngine(G2model, 50))
>>> itmSwaption.setPricingEngine(ql.TreeSwaptionEngine(G2model, 50))
>>>
>>> data.append(("G2 analytic", itmSwaption.NPV(), atmSwaption.NPV(), otmSwaption.NPV()))
>>>
>>> # +
>>> atmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel, 50))
>>> otmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel, 50))
>>> itmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel, 50))
>>>
>>> data.append(("HW analytic", itmSwaption.NPV(), atmSwaption.NPV(), otmSwaption.NPV()))
>>>
>>> # +
>>> atmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel2, 50))
>>> otmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel2, 50))
>>> itmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel2, 50))
>>>
>>> data.append(("HW numerical", itmSwaption.NPV(), atmSwaption.NPV(), otmSwaption.NPV()))
>>>
>>> # +
>>> atmSwaption.setPricingEngine(ql.TreeSwaptionEngine(BKmodel, 50))
>>> otmSwaption.setPricingEngine(ql.TreeSwaptionEngine(BKmodel, 50))
>>> itmSwaption.setPricingEngine(ql.TreeSwaptionEngine(BKmodel, 50))
>>>
>>> data.append(("BK numerical", itmSwaption.NPV(), atmSwaption.NPV(), otmSwaption.NPV()))
>>> # -
>>>
>>> print(pd.DataFrame(data, columns=["model", "in-the-money", "at-the-money", "out-of-the-money"]))
>>>
>>> # ### GSR live setValue bump test
>>>
>>> import faulthandler
>>> faulthandler.enable(all_threads=True)
>>>
>>> print("\n--- GSR live setValue bump test ---")
>>>
>>> # Minimal European swaption test on the same underlying ATM swap
>>> euroExercise = ql.EuropeanExercise(swapStart)
>>> euroSwaption = ql.Swaption(atmSwap, euroExercise)
>>>
>>> # For a European GSR setup: no step dates, one sigma, one reversion
>>> stepDates = []
>>> sigmas = [ql.QuoteHandle(ql.SimpleQuote(0.01))]
>>> reversions = [ql.QuoteHandle(ql.SimpleQuote(0.01))]
>>>
>>> gsr = ql.Gsr(termStructure, stepDates, sigmas, reversions)
>>> gsrEngine = ql.Gaussian1dSwaptionEngine(
>>> gsr,
>>> 64, # integration points
>>> 7.0, # std devs
>>> True,
>>> False,
>>> termStructure
>>> )
>>> euroSwaption.setPricingEngine(gsrEngine)
>>>
>>> base_rate = 0.04875825
>>>
>>> print("base rate:", base_rate)
>>> print("base npv :", euroSwaption.NPV())
>>>
>>> # Single bump up/down
>>> for bump in [1e-4, -1e-4, 0.0]:
>>> bumped = base_rate + bump
>>> print(f"\nsetValue -> {bumped:.8f}")
>>> rateQuote.setValue(bumped)
>>> print("npv =", euroSwaption.NPV())
>>>
>>> # Stress loop: repeatedly bump the observed quote and reprice
>>> print("\nstarting stress loop...")
>>> for i in range(200):
>>> bumped = base_rate + (1e-4 if i % 2 == 0 else -1e-4)
>>> rateQuote.setValue(bumped)
>>> npv = euroSwaption.NPV()
>>> if i % 20 == 0:
>>> print(f"iter {i:03d} rate={bumped:.8f} npv={npv}")
>>>
>>> # Restore
>>> rateQuote.setValue(base_rate)
>>> print("\nrestored rate:", base_rate)
>>> print("restored npv :", euroSwaption.NPV())
>>>
>>>
>>> Log:
>>>
>>> aleksisaliraza@Alis-MacBook-Pro:~/QuantLib-SWIG-1.42/Python/examples$ python3.14 bermudan-swaption.py
>>> Model: G2 (analytic formulae)
>>> Parameters: [ 0.0765369; 0.00279407; 0.0619638; 0.00954892; -1 ]
>>> maturity length volatility implied error
>>> 0 1Y 5Y 0.1148 0.101974 -0.012826
>>> 1 2Y 4Y 0.1108 0.105611 -0.005189
>>> 2 3Y 3Y 0.1070 0.106950 -0.000050
>>> 3 4Y 2Y 0.1021 0.107873 0.005773
>>> 4 5Y 1Y 0.1000 0.108556 0.008556
>>> Average error: 0.0065
>>> Model: Hull-White (analytic formulae)
>>> Parameters: [ 0.046379; 0.00586848 ]
>>> maturity length volatility implied error
>>> 0 1Y 5Y 0.1148 0.106223 -0.008577
>>> 1 2Y 4Y 0.1108 0.106292 -0.004508
>>> 2 3Y 3Y 0.1070 0.106338 -0.000662
>>> 3 4Y 2Y 0.1021 0.106439 0.004339
>>> 4 5Y 1Y 0.1000 0.106609 0.006609
>>> Average error: 0.0049
>>> Model: Hull-White (numerical calibration)
>>> Parameters: [ 0.0559314; 0.00609858 ]
>>> maturity length volatility implied error
>>> 0 1Y 5Y 0.1148 0.102941 -0.011859
>>> 1 2Y 4Y 0.1108 0.105452 -0.005348
>>> 2 3Y 3Y 0.1070 0.106560 -0.000440
>>> 3 4Y 2Y 0.1021 0.107365 0.005265
>>> 4 5Y 1Y 0.1000 0.108223 0.008223
>>> Average error: 0.0062
>>> Model: Black-Karasinski (numerical calibration)
>>> Parameters: [ 0.0442393; 0.120656 ]
>>> maturity length volatility implied error
>>> 0 1Y 5Y 0.1148 0.103094 -0.011706
>>> 1 2Y 4Y 0.1108 0.105637 -0.005163
>>> 2 3Y 3Y 0.1070 0.106656 -0.000344
>>> 3 4Y 2Y 0.1021 0.107333 0.005233
>>> 4 5Y 1Y 0.1000 0.108028 0.008028
>>> Average error: 0.0061
>>> model in-the-money at-the-money out-of-the-money
>>> 0 G2 analytic 42.919620 14.581452 3.443306
>>> 1 HW analytic 42.245449 12.924016 2.512541
>>> 2 HW numerical 42.340450 13.141040 2.614228
>>> 3 BK numerical 41.805673 13.012682 3.271628
>>>
>>> --- GSR live setValue bump test ---
>>> base rate: 0.04875825
>>> base npv : 16.787915348295005
>>>
>>> setValue -> 0.04885825
>>> Fatal Python error: Segmentation fault
>>>
>>> Current thread 0x00000001f0afd8c0 (most recent call first):
>>> File "/opt/homebrew/lib/python3.14/site-packages/QuantLib/QuantLib.py", line 6180 in setValue
>>> File "/Users/aleksisaliraza/QuantLib-SWIG-1.42/Python/examples/bermudan-swaption.py", line 277 in <module>
>>>
>>> Current thread's C stack trace (most recent call first):
>>> Binary file "/opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/Python", at _Py_DumpStack+0x44 [0x1036d79d8]
>>> Binary file "/opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/Python", at faulthandler_dump_c_stack+0x58 [0x1036e9f0c]
>>> Binary file "/opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/Python", at faulthandler_fatal_error+0x140 [0x1036e9dd0]
>>> Binary file "/usr/lib/system/libsystem_platform.dylib", at _sigtramp+0x38 [0x184cfd7a4]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>> <truncated rest of calls>
>>>
>>> Extension modules: QuantLib._QuantLib, numpy._core._multiarray_umath, numpy.linalg._umath_linalg, numpy.random._common, numpy.random.bit_generator, numpy.random._bounded_integers, numpy.random._pcg64, numpy.random._mt19937, numpy.random._generator, numpy.random._philox, numpy.random._sfc64, numpy.random.mtrand, pandas._libs.tslibs.ccalendar, pandas._libs.tslibs.np_datetime, pandas._libs.tslibs.dtypes, pandas._libs.tslibs.base, pandas._libs.tslibs.nattype, pandas._libs.tslibs.timezones, pandas._libs.tslibs.fields, pandas._libs.tslibs.timedeltas, pandas._libs.tslibs.tzconversion, pandas._libs.tslibs.timestamps, pandas._libs.properties, pandas._libs.tslibs.offsets, pandas._libs.tslibs.strptime, pandas._libs.tslibs.parsing, pandas._libs.tslibs.conversion, pandas._libs.tslibs.period, pandas._libs.tslibs.vectorized, pandas._libs.ops_dispatch, pandas._libs.missing, pandas._libs.hashtable, pandas._libs.algos, pandas._libs.interval, pandas._libs.lib, pandas._libs.ops, pandas._libs.hashing, pandas._libs.arrays, pandas._libs.tslib, pandas._libs.sparse, pandas._libs.internals, pandas._libs.indexing, pandas._libs.index, pandas._libs.writers, pandas._libs.join, pandas._libs.window.aggregations, pandas._libs.window.indexers, pandas._libs.reshape, pandas._libs.groupby, pandas._libs.json, pandas._libs.parsers, pandas._libs.testing (total: 52)
>>> Segmentation fault: 11
>>>
>>>
>>> _______________________________________________
>>> QuantLib-users mailing list
>>> Qua...@li...
>>> https://lists.sourceforge.net/lists/listinfo/quantlib-users
>>>
>>
>> _______________________________________________
>> QuantLib-users mailing list
>> Qua...@li...
>> https://lists.sourceforge.net/lists/listinfo/quantlib-users
>>
>
|
|
From: Luigi B. <lui...@gm...> - 2026-04-16 07:04:48
|
Thanks for the heads-up. Looks like I need to release a 1.42.1...
Luigi
On Thu, Apr 16, 2026 at 8:57 AM Aleksis Ali Raza via QuantLib-users <
qua...@li...> wrote:
> Yep, ditching registerWith(stateProcess_); works fine as well and is the
> cleaner fix.
>
> thanks.
>
> On Apr 16, 2026, at 11:34, Peter Caspers <pca...@gm...> wrote:
>
> Thanks. Since Gsr manages its stateProcess_, it does not need to and
> should not register with it as an Observer? I will take a closer look, but
> if you could test this change on your end, that would be valuable input.
> Best, Peter
>
>
> Aleksis Ali Raza via QuantLib-users <qua...@li...>
> schrieb am Do. 16. Apr. 2026 um 08:04:
>
>> Just to follow up on this, it's this line:
>>
>> void Gsr::update() {
>> if (stateProcess_ != nullptr) {
>> ext::static_pointer_cast<GsrProcess>(stateProcess_)->flushCache();
>>
>> *ext::static_pointer_cast<GsrProcess>(stateProcess_)->notifyObservers();*
>> }
>> LazyObject::update();
>> }
>>
>> in the gsr class that seems to be the cause. in my case, commenting out
>> that line out and rebuilding from source fixes the problem of my gsr-based
>> swaption risk runs.
>>
>> That line by itself looks pretty harmless but i think
>>
>> registerWith(stateProcess_);
>>
>> further down leads into some sort of feedback loop (setValue() →
>> notifyObservers() → Gsr::update() → stateProcess_->notifyObservers() →
>> Gsr::update() → …) and ultimately a seg fault.
>>
>> On Apr 16, 2026, at 09:13, Aleksis Ali Raza <ale...@go...>
>> wrote:
>>
>> Hi,
>>
>> I’m encountering what appears to be a regression or stability issue in
>> QuantLib 1.42 related to the GSR (Gaussian short rate) model and the
>> observer framework.
>>
>> Calling SimpleQuote.setValue() on a quote that is observed by a term
>> structure used in a Gsr model causes a segmentation fault.
>>
>> This occurs even in a minimal standalone script (no threading, no
>> external frameworks).
>>
>> Minimal reproduction
>>
>> The following sequence is sufficient:
>> 1. Create a SimpleQuote
>> 2. Build a FlatForward curve using that quote
>> 3. Construct a Gsr model from that term structure
>> 4. Attach a Gaussian1dSwaptionEngine
>> 5. Call setValue() on the original SimpleQuote
>>
>> This results in a segmentation fault.
>>
>> I have modified the Python/examples/bermudan_swaption.py file and
>> attached it below to illustrate (along with error log below it).
>>
>> Has something been changed in how one shud enquire / update quote objects
>> that I missed? I am coming from 1.40, where this was not an issue. I am
>> running python 3.14.4 on macOS 26.1.4 and using a source build of
>> Quantlib/SWIG.
>>
>> Thanks, Aleksis
>>
>> # ---
>> # jupyter:
>> # jupytext:
>> # formats: py:light
>> # text_representation:
>> # extension: .py
>> # format_name: light
>> # format_version: '1.5'
>> # jupytext_version: 1.4.2
>> # kernelspec:
>> # display_name: Python 3
>> # language: python
>> # name: python3
>> # ---
>>
>> # # Bermudan swaptions
>> #
>> # Copyright (©) 2004, 2005, 2006, 2007 StatPro Italia srl
>> #
>> # This file is part of QuantLib, a free-software/open-source library
>> # for financial quantitative analysts and developers - https://www.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
>> # <qua...@li...>. The license is also available online at
>> # <https://www.quantlib.org/license.shtml>.
>> #
>> # 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.
>>
>> import QuantLib as ql
>> import pandas as pd
>>
>> # ### Setup
>>
>> todaysDate = ql.Date(15, ql.February, 2002)
>> ql.Settings.instance().evaluationDate = todaysDate
>> calendar = ql.TARGET()
>> settlementDate = ql.Date(19, ql.February, 2002)
>>
>>
>> def calibrate(model, helpers, l, name):
>> print("Model: %s" % name)
>>
>> method = ql.Simplex(l)
>> model.calibrate(helpers, method, ql.EndCriteria(1000, 250, 1e-7, 1e-7, 1e-7))
>>
>> print("Parameters: %s" % model.params())
>>
>> totalError = 0.0
>> data = []
>> for swaption, helper in zip(swaptionVols, helpers):
>> maturity, length, vol = swaption
>> NPV = helper.modelValue()
>> implied = helper.impliedVolatility(NPV, 1.0e-4, 1000, 0.05, 0.50)
>> error = implied - vol
>> totalError += abs(error)
>> data.append((maturity, length, vol, implied, error))
>> averageError = totalError / len(helpers)
>>
>> print(pd.DataFrame(data, columns=["maturity", "length", "volatility", "implied", "error"]))
>>
>> print("Average error: %.4f" % averageError)
>>
>>
>> # ### Market data
>>
>> swaptionVols = [
>> # maturity, length, volatility
>> (ql.Period(1, ql.Years), ql.Period(5, ql.Years), 0.1148),
>> (ql.Period(2, ql.Years), ql.Period(4, ql.Years), 0.1108),
>> (ql.Period(3, ql.Years), ql.Period(3, ql.Years), 0.1070),
>> (ql.Period(4, ql.Years), ql.Period(2, ql.Years), 0.1021),
>> (ql.Period(5, ql.Years), ql.Period(1, ql.Years), 0.1000),
>> ]
>>
>> # This is a flat yield term structure implying a 1x5 swap at 5%.
>>
>> rateQuote = ql.SimpleQuote(0.04875825)
>> rate = ql.QuoteHandle(rateQuote)
>> termStructure = ql.YieldTermStructureHandle(
>> ql.FlatForward(settlementDate, rate, ql.Actual365Fixed())
>> )
>>
>> # Define the ATM/OTM/ITM swaps:
>>
>> swapEngine = ql.DiscountingSwapEngine(termStructure)
>>
>> fixedLegFrequency = ql.Annual
>> fixedLegTenor = ql.Period(1, ql.Years)
>> fixedLegConvention = ql.Unadjusted
>> floatingLegConvention = ql.ModifiedFollowing
>> fixedLegDayCounter = ql.Thirty360(ql.Thirty360.European)
>> floatingLegFrequency = ql.Semiannual
>> floatingLegTenor = ql.Period(6, ql.Months)
>>
>> payFixed = ql.Swap.Payer
>> fixingDays = 2
>> index = ql.Euribor6M(termStructure)
>> floatingLegDayCounter = index.dayCounter()
>>
>> swapStart = calendar.advance(settlementDate, 1, ql.Years, floatingLegConvention)
>> swapEnd = calendar.advance(swapStart, 5, ql.Years, floatingLegConvention)
>>
>> fixedSchedule = ql.Schedule(
>> swapStart,
>> swapEnd,
>> fixedLegTenor,
>> calendar,
>> fixedLegConvention,
>> fixedLegConvention,
>> ql.DateGeneration.Forward,
>> False,
>> )
>> floatingSchedule = ql.Schedule(
>> swapStart,
>> swapEnd,
>> floatingLegTenor,
>> calendar,
>> floatingLegConvention,
>> floatingLegConvention,
>> ql.DateGeneration.Forward,
>> False,
>> )
>>
>> dummy = ql.VanillaSwap(
>> payFixed, 100.0, fixedSchedule, 0.0, fixedLegDayCounter, floatingSchedule, index, 0.0, floatingLegDayCounter
>> )
>> dummy.setPricingEngine(swapEngine)
>> atmRate = dummy.fairRate()
>>
>> atmSwap = ql.VanillaSwap(
>> payFixed, 1000.0, fixedSchedule, atmRate, fixedLegDayCounter,
>> floatingSchedule, index, 0.0, floatingLegDayCounter
>> )
>>
>> otmSwap = ql.VanillaSwap(
>> payFixed, 1000.0, fixedSchedule, atmRate * 1.2, fixedLegDayCounter,
>> floatingSchedule, index, 0.0, floatingLegDayCounter
>> )
>>
>> itmSwap = ql.VanillaSwap(
>> payFixed, 1000.0, fixedSchedule, atmRate * 0.8, fixedLegDayCounter,
>> floatingSchedule, index, 0.0, floatingLegDayCounter
>> )
>>
>> atmSwap.setPricingEngine(swapEngine)
>> otmSwap.setPricingEngine(swapEngine)
>> itmSwap.setPricingEngine(swapEngine)
>>
>> helpers = [
>> ql.SwaptionHelper(
>> maturity,
>> length,
>> ql.makeQuoteHandle(vol),
>> index,
>> index.tenor(),
>> index.dayCounter(),
>> index.dayCounter(),
>> termStructure,
>> )
>> for maturity, length, vol in swaptionVols
>> ]
>>
>> times = {}
>> for h in helpers:
>> for t in h.times():
>> times[t] = 1
>> times = sorted(times.keys())
>>
>> grid = ql.TimeGrid(times, 30)
>>
>> G2model = ql.G2(termStructure)
>> HWmodel = ql.HullWhite(termStructure)
>> HWmodel2 = ql.HullWhite(termStructure)
>> BKmodel = ql.BlackKarasinski(termStructure)
>>
>> # ### Calibrations
>>
>> for h in helpers:
>> h.setPricingEngine(ql.G2SwaptionEngine(G2model, 6.0, 16))
>> calibrate(G2model, helpers, 0.05, "G2 (analytic formulae)")
>>
>> for h in helpers:
>> h.setPricingEngine(ql.JamshidianSwaptionEngine(HWmodel))
>> calibrate(HWmodel, helpers, 0.05, "Hull-White (analytic formulae)")
>>
>> for h in helpers:
>> h.setPricingEngine(ql.TreeSwaptionEngine(HWmodel2, grid))
>> calibrate(HWmodel2, helpers, 0.05, "Hull-White (numerical calibration)")
>>
>> for h in helpers:
>> h.setPricingEngine(ql.TreeSwaptionEngine(BKmodel, grid))
>> calibrate(BKmodel, helpers, 0.05, "Black-Karasinski (numerical calibration)")
>>
>>
>> # ### Price Bermudan swaptions on defined swaps
>>
>> bermudanDates = [d for d in fixedSchedule][:-1]
>> exercise = ql.BermudanExercise(bermudanDates)
>>
>> atmSwaption = ql.Swaption(atmSwap, exercise)
>> otmSwaption = ql.Swaption(otmSwap, exercise)
>> itmSwaption = ql.Swaption(itmSwap, exercise)
>>
>> data = []
>>
>> # +
>> atmSwaption.setPricingEngine(ql.TreeSwaptionEngine(G2model, 50))
>> otmSwaption.setPricingEngine(ql.TreeSwaptionEngine(G2model, 50))
>> itmSwaption.setPricingEngine(ql.TreeSwaptionEngine(G2model, 50))
>>
>> data.append(("G2 analytic", itmSwaption.NPV(), atmSwaption.NPV(), otmSwaption.NPV()))
>>
>> # +
>> atmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel, 50))
>> otmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel, 50))
>> itmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel, 50))
>>
>> data.append(("HW analytic", itmSwaption.NPV(), atmSwaption.NPV(), otmSwaption.NPV()))
>>
>> # +
>> atmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel2, 50))
>> otmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel2, 50))
>> itmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel2, 50))
>>
>> data.append(("HW numerical", itmSwaption.NPV(), atmSwaption.NPV(), otmSwaption.NPV()))
>>
>> # +
>> atmSwaption.setPricingEngine(ql.TreeSwaptionEngine(BKmodel, 50))
>> otmSwaption.setPricingEngine(ql.TreeSwaptionEngine(BKmodel, 50))
>> itmSwaption.setPricingEngine(ql.TreeSwaptionEngine(BKmodel, 50))
>>
>> data.append(("BK numerical", itmSwaption.NPV(), atmSwaption.NPV(), otmSwaption.NPV()))
>> # -
>>
>> print(pd.DataFrame(data, columns=["model", "in-the-money", "at-the-money", "out-of-the-money"]))
>>
>> # ### GSR live setValue bump test
>>
>> import faulthandler
>> faulthandler.enable(all_threads=True)
>>
>> print("\n--- GSR live setValue bump test ---")
>>
>> # Minimal European swaption test on the same underlying ATM swap
>> euroExercise = ql.EuropeanExercise(swapStart)
>> euroSwaption = ql.Swaption(atmSwap, euroExercise)
>>
>> # For a European GSR setup: no step dates, one sigma, one reversion
>> stepDates = []
>> sigmas = [ql.QuoteHandle(ql.SimpleQuote(0.01))]
>> reversions = [ql.QuoteHandle(ql.SimpleQuote(0.01))]
>>
>> gsr = ql.Gsr(termStructure, stepDates, sigmas, reversions)
>> gsrEngine = ql.Gaussian1dSwaptionEngine(
>> gsr,
>> 64, # integration points
>> 7.0, # std devs
>> True,
>> False,
>> termStructure
>> )
>> euroSwaption.setPricingEngine(gsrEngine)
>>
>> base_rate = 0.04875825
>>
>> print("base rate:", base_rate)
>> print("base npv :", euroSwaption.NPV())
>>
>> # Single bump up/down
>> for bump in [1e-4, -1e-4, 0.0]:
>> bumped = base_rate + bump
>> print(f"\nsetValue -> {bumped:.8f}")
>> rateQuote.setValue(bumped)
>> print("npv =", euroSwaption.NPV())
>>
>> # Stress loop: repeatedly bump the observed quote and reprice
>> print("\nstarting stress loop...")
>> for i in range(200):
>> bumped = base_rate + (1e-4 if i % 2 == 0 else -1e-4)
>> rateQuote.setValue(bumped)
>> npv = euroSwaption.NPV()
>> if i % 20 == 0:
>> print(f"iter {i:03d} rate={bumped:.8f} npv={npv}")
>>
>> # Restore
>> rateQuote.setValue(base_rate)
>> print("\nrestored rate:", base_rate)
>> print("restored npv :", euroSwaption.NPV())
>>
>>
>> Log:
>>
>> aleksisaliraza@Alis-MacBook-Pro:~/QuantLib-SWIG-1.42/Python/examples$ python3.14 bermudan-swaption.py
>> Model: G2 (analytic formulae)
>> Parameters: [ 0.0765369; 0.00279407; 0.0619638; 0.00954892; -1 ]
>> maturity length volatility implied error
>> 0 1Y 5Y 0.1148 0.101974 -0.012826
>> 1 2Y 4Y 0.1108 0.105611 -0.005189
>> 2 3Y 3Y 0.1070 0.106950 -0.000050
>> 3 4Y 2Y 0.1021 0.107873 0.005773
>> 4 5Y 1Y 0.1000 0.108556 0.008556
>> Average error: 0.0065
>> Model: Hull-White (analytic formulae)
>> Parameters: [ 0.046379; 0.00586848 ]
>> maturity length volatility implied error
>> 0 1Y 5Y 0.1148 0.106223 -0.008577
>> 1 2Y 4Y 0.1108 0.106292 -0.004508
>> 2 3Y 3Y 0.1070 0.106338 -0.000662
>> 3 4Y 2Y 0.1021 0.106439 0.004339
>> 4 5Y 1Y 0.1000 0.106609 0.006609
>> Average error: 0.0049
>> Model: Hull-White (numerical calibration)
>> Parameters: [ 0.0559314; 0.00609858 ]
>> maturity length volatility implied error
>> 0 1Y 5Y 0.1148 0.102941 -0.011859
>> 1 2Y 4Y 0.1108 0.105452 -0.005348
>> 2 3Y 3Y 0.1070 0.106560 -0.000440
>> 3 4Y 2Y 0.1021 0.107365 0.005265
>> 4 5Y 1Y 0.1000 0.108223 0.008223
>> Average error: 0.0062
>> Model: Black-Karasinski (numerical calibration)
>> Parameters: [ 0.0442393; 0.120656 ]
>> maturity length volatility implied error
>> 0 1Y 5Y 0.1148 0.103094 -0.011706
>> 1 2Y 4Y 0.1108 0.105637 -0.005163
>> 2 3Y 3Y 0.1070 0.106656 -0.000344
>> 3 4Y 2Y 0.1021 0.107333 0.005233
>> 4 5Y 1Y 0.1000 0.108028 0.008028
>> Average error: 0.0061
>> model in-the-money at-the-money out-of-the-money
>> 0 G2 analytic 42.919620 14.581452 3.443306
>> 1 HW analytic 42.245449 12.924016 2.512541
>> 2 HW numerical 42.340450 13.141040 2.614228
>> 3 BK numerical 41.805673 13.012682 3.271628
>>
>> --- GSR live setValue bump test ---
>> base rate: 0.04875825
>> base npv : 16.787915348295005
>>
>> setValue -> 0.04885825
>> Fatal Python error: Segmentation fault
>>
>> Current thread 0x00000001f0afd8c0 (most recent call first):
>> File "/opt/homebrew/lib/python3.14/site-packages/QuantLib/QuantLib.py", line 6180 in setValue
>> File "/Users/aleksisaliraza/QuantLib-SWIG-1.42/Python/examples/bermudan-swaption.py", line 277 in <module>
>>
>> Current thread's C stack trace (most recent call first):
>> Binary file "/opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/Python", at _Py_DumpStack+0x44 [0x1036d79d8]
>> Binary file "/opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/Python", at faulthandler_dump_c_stack+0x58 [0x1036e9f0c]
>> Binary file "/opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/Python", at faulthandler_fatal_error+0x140 [0x1036e9dd0]
>> Binary file "/usr/lib/system/libsystem_platform.dylib", at _sigtramp+0x38 [0x184cfd7a4]
>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>> <truncated rest of calls>
>>
>> Extension modules: QuantLib._QuantLib, numpy._core._multiarray_umath, numpy.linalg._umath_linalg, numpy.random._common, numpy.random.bit_generator, numpy.random._bounded_integers, numpy.random._pcg64, numpy.random._mt19937, numpy.random._generator, numpy.random._philox, numpy.random._sfc64, numpy.random.mtrand, pandas._libs.tslibs.ccalendar, pandas._libs.tslibs.np_datetime, pandas._libs.tslibs.dtypes, pandas._libs.tslibs.base, pandas._libs.tslibs.nattype, pandas._libs.tslibs.timezones, pandas._libs.tslibs.fields, pandas._libs.tslibs.timedeltas, pandas._libs.tslibs.tzconversion, pandas._libs.tslibs.timestamps, pandas._libs.properties, pandas._libs.tslibs.offsets, pandas._libs.tslibs.strptime, pandas._libs.tslibs.parsing, pandas._libs.tslibs.conversion, pandas._libs.tslibs.period, pandas._libs.tslibs.vectorized, pandas._libs.ops_dispatch, pandas._libs.missing, pandas._libs.hashtable, pandas._libs.algos, pandas._libs.interval, pandas._libs.lib, pandas._libs.ops, pandas._libs.hashing, pandas._libs.arrays, pandas._libs.tslib, pandas._libs.sparse, pandas._libs.internals, pandas._libs.indexing, pandas._libs.index, pandas._libs.writers, pandas._libs.join, pandas._libs.window.aggregations, pandas._libs.window.indexers, pandas._libs.reshape, pandas._libs.groupby, pandas._libs.json, pandas._libs.parsers, pandas._libs.testing (total: 52)
>> Segmentation fault: 11
>>
>>
>> _______________________________________________
>> QuantLib-users mailing list
>> Qua...@li...
>> https://lists.sourceforge.net/lists/listinfo/quantlib-users
>>
>
> _______________________________________________
> QuantLib-users mailing list
> Qua...@li...
> https://lists.sourceforge.net/lists/listinfo/quantlib-users
>
|
|
From: Aleksis A. R. <ale...@go...> - 2026-04-16 06:57:17
|
Yep, ditching registerWith(stateProcess_); works fine as well and is the cleaner fix.
thanks.
> On Apr 16, 2026, at 11:34, Peter Caspers <pca...@gm...> wrote:
>
> Thanks. Since Gsr manages its stateProcess_, it does not need to and should not register with it as an Observer? I will take a closer look, but if you could test this change on your end, that would be valuable input.
> Best, Peter
>
>
> Aleksis Ali Raza via QuantLib-users <qua...@li... <mailto:qua...@li...>> schrieb am Do. 16. Apr. 2026 um 08:04:
>> Just to follow up on this, it's this line:
>>
>> void Gsr::update() {
>> if (stateProcess_ != nullptr) {
>> ext::static_pointer_cast<GsrProcess>(stateProcess_)->flushCache();
>> ext::static_pointer_cast<GsrProcess>(stateProcess_)->notifyObservers();
>> }
>> LazyObject::update();
>> }
>>
>> in the gsr class that seems to be the cause. in my case, commenting out that line out and rebuilding from source fixes the problem of my gsr-based swaption risk runs.
>>
>> That line by itself looks pretty harmless but i think
>> registerWith(stateProcess_);
>>
>> further down leads into some sort of feedback loop (setValue() → notifyObservers() → Gsr::update() → stateProcess_->notifyObservers() → Gsr::update() → …) and ultimately a seg fault.
>>
>>
>>> On Apr 16, 2026, at 09:13, Aleksis Ali Raza <ale...@go... <mailto:ale...@go...>> wrote:
>>>
>>> Hi,
>>>
>>> I’m encountering what appears to be a regression or stability issue in QuantLib 1.42 related to the GSR (Gaussian short rate) model and the observer framework.
>>>
>>> Calling SimpleQuote.setValue() on a quote that is observed by a term structure used in a Gsr model causes a segmentation fault.
>>>
>>> This occurs even in a minimal standalone script (no threading, no external frameworks).
>>>
>>> Minimal reproduction
>>>
>>> The following sequence is sufficient:
>>> 1. Create a SimpleQuote
>>> 2. Build a FlatForward curve using that quote
>>> 3. Construct a Gsr model from that term structure
>>> 4. Attach a Gaussian1dSwaptionEngine
>>> 5. Call setValue() on the original SimpleQuote
>>>
>>> This results in a segmentation fault.
>>>
>>> I have modified the Python/examples/bermudan_swaption.py file and attached it below to illustrate (along with error log below it).
>>>
>>> Has something been changed in how one shud enquire / update quote objects that I missed? I am coming from 1.40, where this was not an issue. I am running python 3.14.4 on macOS 26.1.4 and using a source build of Quantlib/SWIG.
>>>
>>> Thanks, Aleksis
>>>
>>> # ---
>>> # jupyter:
>>> # jupytext:
>>> # formats: py:light
>>> # text_representation:
>>> # extension: .py
>>> # format_name: light
>>> # format_version: '1.5'
>>> # jupytext_version: 1.4.2
>>> # kernelspec:
>>> # display_name: Python 3
>>> # language: python
>>> # name: python3
>>> # ---
>>>
>>> # # Bermudan swaptions
>>> #
>>> # Copyright (©) 2004, 2005, 2006, 2007 StatPro Italia srl
>>> #
>>> # This file is part of QuantLib, a free-software/open-source library
>>> # for financial quantitative analysts and developers - https://www.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
>>> # <qua...@li... <mailto:qua...@li...>>. The license is also available online at
>>> # <https://www.quantlib.org/license.shtml>.
>>> #
>>> # 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.
>>>
>>> import QuantLib as ql
>>> import pandas as pd
>>>
>>> # ### Setup
>>>
>>> todaysDate = ql.Date(15, ql.February, 2002)
>>> ql.Settings.instance().evaluationDate = todaysDate
>>> calendar = ql.TARGET()
>>> settlementDate = ql.Date(19, ql.February, 2002)
>>>
>>>
>>> def calibrate(model, helpers, l, name):
>>> print("Model: %s" % name)
>>>
>>> method = ql.Simplex(l)
>>> model.calibrate(helpers, method, ql.EndCriteria(1000, 250, 1e-7, 1e-7, 1e-7))
>>>
>>> print("Parameters: %s" % model.params())
>>>
>>> totalError = 0.0
>>> data = []
>>> for swaption, helper in zip(swaptionVols, helpers):
>>> maturity, length, vol = swaption
>>> NPV = helper.modelValue()
>>> implied = helper.impliedVolatility(NPV, 1.0e-4, 1000, 0.05, 0.50)
>>> error = implied - vol
>>> totalError += abs(error)
>>> data.append((maturity, length, vol, implied, error))
>>> averageError = totalError / len(helpers)
>>>
>>> print(pd.DataFrame(data, columns=["maturity", "length", "volatility", "implied", "error"]))
>>>
>>> print("Average error: %.4f" % averageError)
>>>
>>>
>>> # ### Market data
>>>
>>> swaptionVols = [
>>> # maturity, length, volatility
>>> (ql.Period(1, ql.Years), ql.Period(5, ql.Years), 0.1148),
>>> (ql.Period(2, ql.Years), ql.Period(4, ql.Years), 0.1108),
>>> (ql.Period(3, ql.Years), ql.Period(3, ql.Years), 0.1070),
>>> (ql.Period(4, ql.Years), ql.Period(2, ql.Years), 0.1021),
>>> (ql.Period(5, ql.Years), ql.Period(1, ql.Years), 0.1000),
>>> ]
>>>
>>> # This is a flat yield term structure implying a 1x5 swap at 5%.
>>>
>>> rateQuote = ql.SimpleQuote(0.04875825)
>>> rate = ql.QuoteHandle(rateQuote)
>>> termStructure = ql.YieldTermStructureHandle(
>>> ql.FlatForward(settlementDate, rate, ql.Actual365Fixed())
>>> )
>>>
>>> # Define the ATM/OTM/ITM swaps:
>>>
>>> swapEngine = ql.DiscountingSwapEngine(termStructure)
>>>
>>> fixedLegFrequency = ql.Annual
>>> fixedLegTenor = ql.Period(1, ql.Years)
>>> fixedLegConvention = ql.Unadjusted
>>> floatingLegConvention = ql.ModifiedFollowing
>>> fixedLegDayCounter = ql.Thirty360(ql.Thirty360.European)
>>> floatingLegFrequency = ql.Semiannual
>>> floatingLegTenor = ql.Period(6, ql.Months)
>>>
>>> payFixed = ql.Swap.Payer
>>> fixingDays = 2
>>> index = ql.Euribor6M(termStructure)
>>> floatingLegDayCounter = index.dayCounter()
>>>
>>> swapStart = calendar.advance(settlementDate, 1, ql.Years, floatingLegConvention)
>>> swapEnd = calendar.advance(swapStart, 5, ql.Years, floatingLegConvention)
>>>
>>> fixedSchedule = ql.Schedule(
>>> swapStart,
>>> swapEnd,
>>> fixedLegTenor,
>>> calendar,
>>> fixedLegConvention,
>>> fixedLegConvention,
>>> ql.DateGeneration.Forward,
>>> False,
>>> )
>>> floatingSchedule = ql.Schedule(
>>> swapStart,
>>> swapEnd,
>>> floatingLegTenor,
>>> calendar,
>>> floatingLegConvention,
>>> floatingLegConvention,
>>> ql.DateGeneration.Forward,
>>> False,
>>> )
>>>
>>> dummy = ql.VanillaSwap(
>>> payFixed, 100.0, fixedSchedule, 0.0, fixedLegDayCounter, floatingSchedule, index, 0.0, floatingLegDayCounter
>>> )
>>> dummy.setPricingEngine(swapEngine)
>>> atmRate = dummy.fairRate()
>>>
>>> atmSwap = ql.VanillaSwap(
>>> payFixed, 1000.0, fixedSchedule, atmRate, fixedLegDayCounter,
>>> floatingSchedule, index, 0.0, floatingLegDayCounter
>>> )
>>>
>>> otmSwap = ql.VanillaSwap(
>>> payFixed, 1000.0, fixedSchedule, atmRate * 1.2, fixedLegDayCounter,
>>> floatingSchedule, index, 0.0, floatingLegDayCounter
>>> )
>>>
>>> itmSwap = ql.VanillaSwap(
>>> payFixed, 1000.0, fixedSchedule, atmRate * 0.8, fixedLegDayCounter,
>>> floatingSchedule, index, 0.0, floatingLegDayCounter
>>> )
>>>
>>> atmSwap.setPricingEngine(swapEngine)
>>> otmSwap.setPricingEngine(swapEngine)
>>> itmSwap.setPricingEngine(swapEngine)
>>>
>>> helpers = [
>>> ql.SwaptionHelper(
>>> maturity,
>>> length,
>>> ql.makeQuoteHandle(vol),
>>> index,
>>> index.tenor(),
>>> index.dayCounter(),
>>> index.dayCounter(),
>>> termStructure,
>>> )
>>> for maturity, length, vol in swaptionVols
>>> ]
>>>
>>> times = {}
>>> for h in helpers:
>>> for t in h.times():
>>> times[t] = 1
>>> times = sorted(times.keys())
>>>
>>> grid = ql.TimeGrid(times, 30)
>>>
>>> G2model = ql.G2(termStructure)
>>> HWmodel = ql.HullWhite(termStructure)
>>> HWmodel2 = ql.HullWhite(termStructure)
>>> BKmodel = ql.BlackKarasinski(termStructure)
>>>
>>> # ### Calibrations
>>>
>>> for h in helpers:
>>> h.setPricingEngine(ql.G2SwaptionEngine(G2model, 6.0, 16))
>>> calibrate(G2model, helpers, 0.05, "G2 (analytic formulae)")
>>>
>>> for h in helpers:
>>> h.setPricingEngine(ql.JamshidianSwaptionEngine(HWmodel))
>>> calibrate(HWmodel, helpers, 0.05, "Hull-White (analytic formulae)")
>>>
>>> for h in helpers:
>>> h.setPricingEngine(ql.TreeSwaptionEngine(HWmodel2, grid))
>>> calibrate(HWmodel2, helpers, 0.05, "Hull-White (numerical calibration)")
>>>
>>> for h in helpers:
>>> h.setPricingEngine(ql.TreeSwaptionEngine(BKmodel, grid))
>>> calibrate(BKmodel, helpers, 0.05, "Black-Karasinski (numerical calibration)")
>>>
>>>
>>> # ### Price Bermudan swaptions on defined swaps
>>>
>>> bermudanDates = [d for d in fixedSchedule][:-1]
>>> exercise = ql.BermudanExercise(bermudanDates)
>>>
>>> atmSwaption = ql.Swaption(atmSwap, exercise)
>>> otmSwaption = ql.Swaption(otmSwap, exercise)
>>> itmSwaption = ql.Swaption(itmSwap, exercise)
>>>
>>> data = []
>>>
>>> # +
>>> atmSwaption.setPricingEngine(ql.TreeSwaptionEngine(G2model, 50))
>>> otmSwaption.setPricingEngine(ql.TreeSwaptionEngine(G2model, 50))
>>> itmSwaption.setPricingEngine(ql.TreeSwaptionEngine(G2model, 50))
>>>
>>> data.append(("G2 analytic", itmSwaption.NPV(), atmSwaption.NPV(), otmSwaption.NPV()))
>>>
>>> # +
>>> atmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel, 50))
>>> otmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel, 50))
>>> itmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel, 50))
>>>
>>> data.append(("HW analytic", itmSwaption.NPV(), atmSwaption.NPV(), otmSwaption.NPV()))
>>>
>>> # +
>>> atmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel2, 50))
>>> otmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel2, 50))
>>> itmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel2, 50))
>>>
>>> data.append(("HW numerical", itmSwaption.NPV(), atmSwaption.NPV(), otmSwaption.NPV()))
>>>
>>> # +
>>> atmSwaption.setPricingEngine(ql.TreeSwaptionEngine(BKmodel, 50))
>>> otmSwaption.setPricingEngine(ql.TreeSwaptionEngine(BKmodel, 50))
>>> itmSwaption.setPricingEngine(ql.TreeSwaptionEngine(BKmodel, 50))
>>>
>>> data.append(("BK numerical", itmSwaption.NPV(), atmSwaption.NPV(), otmSwaption.NPV()))
>>> # -
>>>
>>> print(pd.DataFrame(data, columns=["model", "in-the-money", "at-the-money", "out-of-the-money"]))
>>>
>>> # ### GSR live setValue bump test
>>>
>>> import faulthandler
>>> faulthandler.enable(all_threads=True)
>>>
>>> print("\n--- GSR live setValue bump test ---")
>>>
>>> # Minimal European swaption test on the same underlying ATM swap
>>> euroExercise = ql.EuropeanExercise(swapStart)
>>> euroSwaption = ql.Swaption(atmSwap, euroExercise)
>>>
>>> # For a European GSR setup: no step dates, one sigma, one reversion
>>> stepDates = []
>>> sigmas = [ql.QuoteHandle(ql.SimpleQuote(0.01))]
>>> reversions = [ql.QuoteHandle(ql.SimpleQuote(0.01))]
>>>
>>> gsr = ql.Gsr(termStructure, stepDates, sigmas, reversions)
>>> gsrEngine = ql.Gaussian1dSwaptionEngine(
>>> gsr,
>>> 64, # integration points
>>> 7.0, # std devs
>>> True,
>>> False,
>>> termStructure
>>> )
>>> euroSwaption.setPricingEngine(gsrEngine)
>>>
>>> base_rate = 0.04875825
>>>
>>> print("base rate:", base_rate)
>>> print("base npv :", euroSwaption.NPV())
>>>
>>> # Single bump up/down
>>> for bump in [1e-4, -1e-4, 0.0]:
>>> bumped = base_rate + bump
>>> print(f"\nsetValue -> {bumped:.8f}")
>>> rateQuote.setValue(bumped)
>>> print("npv =", euroSwaption.NPV())
>>>
>>> # Stress loop: repeatedly bump the observed quote and reprice
>>> print("\nstarting stress loop...")
>>> for i in range(200):
>>> bumped = base_rate + (1e-4 if i % 2 == 0 else -1e-4)
>>> rateQuote.setValue(bumped)
>>> npv = euroSwaption.NPV()
>>> if i % 20 == 0:
>>> print(f"iter {i:03d} rate={bumped:.8f} npv={npv}")
>>>
>>> # Restore
>>> rateQuote.setValue(base_rate)
>>> print("\nrestored rate:", base_rate)
>>> print("restored npv :", euroSwaption.NPV())
>>>
>>> Log:
>>> aleksisaliraza@Alis-MacBook-Pro:~/QuantLib-SWIG-1.42/Python/examples$ python3.14 bermudan-swaption.py
>>> Model: G2 (analytic formulae)
>>> Parameters: [ 0.0765369; 0.00279407; 0.0619638; 0.00954892; -1 ]
>>> maturity length volatility implied error
>>> 0 1Y 5Y 0.1148 0.101974 -0.012826
>>> 1 2Y 4Y 0.1108 0.105611 -0.005189
>>> 2 3Y 3Y 0.1070 0.106950 -0.000050
>>> 3 4Y 2Y 0.1021 0.107873 0.005773
>>> 4 5Y 1Y 0.1000 0.108556 0.008556
>>> Average error: 0.0065
>>> Model: Hull-White (analytic formulae)
>>> Parameters: [ 0.046379; 0.00586848 ]
>>> maturity length volatility implied error
>>> 0 1Y 5Y 0.1148 0.106223 -0.008577
>>> 1 2Y 4Y 0.1108 0.106292 -0.004508
>>> 2 3Y 3Y 0.1070 0.106338 -0.000662
>>> 3 4Y 2Y 0.1021 0.106439 0.004339
>>> 4 5Y 1Y 0.1000 0.106609 0.006609
>>> Average error: 0.0049
>>> Model: Hull-White (numerical calibration)
>>> Parameters: [ 0.0559314; 0.00609858 ]
>>> maturity length volatility implied error
>>> 0 1Y 5Y 0.1148 0.102941 -0.011859
>>> 1 2Y 4Y 0.1108 0.105452 -0.005348
>>> 2 3Y 3Y 0.1070 0.106560 -0.000440
>>> 3 4Y 2Y 0.1021 0.107365 0.005265
>>> 4 5Y 1Y 0.1000 0.108223 0.008223
>>> Average error: 0.0062
>>> Model: Black-Karasinski (numerical calibration)
>>> Parameters: [ 0.0442393; 0.120656 ]
>>> maturity length volatility implied error
>>> 0 1Y 5Y 0.1148 0.103094 -0.011706
>>> 1 2Y 4Y 0.1108 0.105637 -0.005163
>>> 2 3Y 3Y 0.1070 0.106656 -0.000344
>>> 3 4Y 2Y 0.1021 0.107333 0.005233
>>> 4 5Y 1Y 0.1000 0.108028 0.008028
>>> Average error: 0.0061
>>> model in-the-money at-the-money out-of-the-money
>>> 0 G2 analytic 42.919620 14.581452 3.443306
>>> 1 HW analytic 42.245449 12.924016 2.512541
>>> 2 HW numerical 42.340450 13.141040 2.614228
>>> 3 BK numerical 41.805673 13.012682 3.271628
>>>
>>> --- GSR live setValue bump test ---
>>> base rate: 0.04875825
>>> base npv : 16.787915348295005
>>>
>>> setValue -> 0.04885825
>>> Fatal Python error: Segmentation fault
>>>
>>> Current thread 0x00000001f0afd8c0 (most recent call first):
>>> File "/opt/homebrew/lib/python3.14/site-packages/QuantLib/QuantLib.py", line 6180 in setValue
>>> File "/Users/aleksisaliraza/QuantLib-SWIG-1.42/Python/examples/bermudan-swaption.py", line 277 in <module>
>>>
>>> Current thread's C stack trace (most recent call first):
>>> Binary file "/opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/Python", at _Py_DumpStack+0x44 [0x1036d79d8]
>>> Binary file "/opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/Python", at faulthandler_dump_c_stack+0x58 [0x1036e9f0c]
>>> Binary file "/opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/Python", at faulthandler_fatal_error+0x140 [0x1036e9dd0]
>>> Binary file "/usr/lib/system/libsystem_platform.dylib", at _sigtramp+0x38 [0x184cfd7a4]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
>>> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
>>> <truncated rest of calls>
>>>
>>> Extension modules: QuantLib._QuantLib, numpy._core._multiarray_umath, numpy.linalg._umath_linalg, numpy.random._common, numpy.random.bit_generator, numpy.random._bounded_integers, numpy.random._pcg64, numpy.random._mt19937, numpy.random._generator, numpy.random._philox, numpy.random._sfc64, numpy.random.mtrand, pandas._libs.tslibs.ccalendar, pandas._libs.tslibs.np_datetime, pandas._libs.tslibs.dtypes, pandas._libs.tslibs.base, pandas._libs.tslibs.nattype, pandas._libs.tslibs.timezones, pandas._libs.tslibs.fields, pandas._libs.tslibs.timedeltas, pandas._libs.tslibs.tzconversion, pandas._libs.tslibs.timestamps, pandas._libs.properties, pandas._libs.tslibs.offsets, pandas._libs.tslibs.strptime, pandas._libs.tslibs.parsing, pandas._libs.tslibs.conversion, pandas._libs.tslibs.period, pandas._libs.tslibs.vectorized, pandas._libs.ops_dispatch, pandas._libs.missing, pandas._libs.hashtable, pandas._libs.algos, pandas._libs.interval, pandas._libs.lib, pandas._libs.ops, pandas._libs.hashing, pandas._libs.arrays, pandas._libs.tslib, pandas._libs.sparse, pandas._libs.internals, pandas._libs.indexing, pandas._libs.index, pandas._libs.writers, pandas._libs.join, pandas._libs.window.aggregations, pandas._libs.window.indexers, pandas._libs.reshape, pandas._libs.groupby, pandas._libs.json, pandas._libs.parsers, pandas._libs.testing (total: 52)
>>> Segmentation fault: 11
>>
>> _______________________________________________
>> QuantLib-users mailing list
>> Qua...@li... <mailto:Qua...@li...>
>> https://lists.sourceforge.net/lists/listinfo/quantlib-users
|
|
From: Peter C. <pca...@gm...> - 2026-04-16 06:34:56
|
Thanks. Since Gsr manages its stateProcess_, it does not need to and should
not register with it as an Observer? I will take a closer look, but if you
could test this change on your end, that would be valuable input.
Best, Peter
Aleksis Ali Raza via QuantLib-users <qua...@li...>
schrieb am Do. 16. Apr. 2026 um 08:04:
> Just to follow up on this, it's this line:
>
> void Gsr::update() {
> if (stateProcess_ != nullptr) {
> ext::static_pointer_cast<GsrProcess>(stateProcess_)->flushCache();
>
> *ext::static_pointer_cast<GsrProcess>(stateProcess_)->notifyObservers();*
> }
> LazyObject::update();
> }
>
> in the gsr class that seems to be the cause. in my case, commenting out
> that line out and rebuilding from source fixes the problem of my gsr-based
> swaption risk runs.
>
> That line by itself looks pretty harmless but i think
>
> registerWith(stateProcess_);
>
> further down leads into some sort of feedback loop (setValue() →
> notifyObservers() → Gsr::update() → stateProcess_->notifyObservers() →
> Gsr::update() → …) and ultimately a seg fault.
>
> On Apr 16, 2026, at 09:13, Aleksis Ali Raza <ale...@go...>
> wrote:
>
> Hi,
>
> I’m encountering what appears to be a regression or stability issue in
> QuantLib 1.42 related to the GSR (Gaussian short rate) model and the
> observer framework.
>
> Calling SimpleQuote.setValue() on a quote that is observed by a term
> structure used in a Gsr model causes a segmentation fault.
>
> This occurs even in a minimal standalone script (no threading, no external
> frameworks).
>
> Minimal reproduction
>
> The following sequence is sufficient:
> 1. Create a SimpleQuote
> 2. Build a FlatForward curve using that quote
> 3. Construct a Gsr model from that term structure
> 4. Attach a Gaussian1dSwaptionEngine
> 5. Call setValue() on the original SimpleQuote
>
> This results in a segmentation fault.
>
> I have modified the Python/examples/bermudan_swaption.py file and attached
> it below to illustrate (along with error log below it).
>
> Has something been changed in how one shud enquire / update quote objects
> that I missed? I am coming from 1.40, where this was not an issue. I am
> running python 3.14.4 on macOS 26.1.4 and using a source build of
> Quantlib/SWIG.
>
> Thanks, Aleksis
>
> # ---
> # jupyter:
> # jupytext:
> # formats: py:light
> # text_representation:
> # extension: .py
> # format_name: light
> # format_version: '1.5'
> # jupytext_version: 1.4.2
> # kernelspec:
> # display_name: Python 3
> # language: python
> # name: python3
> # ---
>
> # # Bermudan swaptions
> #
> # Copyright (©) 2004, 2005, 2006, 2007 StatPro Italia srl
> #
> # This file is part of QuantLib, a free-software/open-source library
> # for financial quantitative analysts and developers - https://www.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
> # <qua...@li...>. The license is also available online at
> # <https://www.quantlib.org/license.shtml>.
> #
> # 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.
>
> import QuantLib as ql
> import pandas as pd
>
> # ### Setup
>
> todaysDate = ql.Date(15, ql.February, 2002)
> ql.Settings.instance().evaluationDate = todaysDate
> calendar = ql.TARGET()
> settlementDate = ql.Date(19, ql.February, 2002)
>
>
> def calibrate(model, helpers, l, name):
> print("Model: %s" % name)
>
> method = ql.Simplex(l)
> model.calibrate(helpers, method, ql.EndCriteria(1000, 250, 1e-7, 1e-7, 1e-7))
>
> print("Parameters: %s" % model.params())
>
> totalError = 0.0
> data = []
> for swaption, helper in zip(swaptionVols, helpers):
> maturity, length, vol = swaption
> NPV = helper.modelValue()
> implied = helper.impliedVolatility(NPV, 1.0e-4, 1000, 0.05, 0.50)
> error = implied - vol
> totalError += abs(error)
> data.append((maturity, length, vol, implied, error))
> averageError = totalError / len(helpers)
>
> print(pd.DataFrame(data, columns=["maturity", "length", "volatility", "implied", "error"]))
>
> print("Average error: %.4f" % averageError)
>
>
> # ### Market data
>
> swaptionVols = [
> # maturity, length, volatility
> (ql.Period(1, ql.Years), ql.Period(5, ql.Years), 0.1148),
> (ql.Period(2, ql.Years), ql.Period(4, ql.Years), 0.1108),
> (ql.Period(3, ql.Years), ql.Period(3, ql.Years), 0.1070),
> (ql.Period(4, ql.Years), ql.Period(2, ql.Years), 0.1021),
> (ql.Period(5, ql.Years), ql.Period(1, ql.Years), 0.1000),
> ]
>
> # This is a flat yield term structure implying a 1x5 swap at 5%.
>
> rateQuote = ql.SimpleQuote(0.04875825)
> rate = ql.QuoteHandle(rateQuote)
> termStructure = ql.YieldTermStructureHandle(
> ql.FlatForward(settlementDate, rate, ql.Actual365Fixed())
> )
>
> # Define the ATM/OTM/ITM swaps:
>
> swapEngine = ql.DiscountingSwapEngine(termStructure)
>
> fixedLegFrequency = ql.Annual
> fixedLegTenor = ql.Period(1, ql.Years)
> fixedLegConvention = ql.Unadjusted
> floatingLegConvention = ql.ModifiedFollowing
> fixedLegDayCounter = ql.Thirty360(ql.Thirty360.European)
> floatingLegFrequency = ql.Semiannual
> floatingLegTenor = ql.Period(6, ql.Months)
>
> payFixed = ql.Swap.Payer
> fixingDays = 2
> index = ql.Euribor6M(termStructure)
> floatingLegDayCounter = index.dayCounter()
>
> swapStart = calendar.advance(settlementDate, 1, ql.Years, floatingLegConvention)
> swapEnd = calendar.advance(swapStart, 5, ql.Years, floatingLegConvention)
>
> fixedSchedule = ql.Schedule(
> swapStart,
> swapEnd,
> fixedLegTenor,
> calendar,
> fixedLegConvention,
> fixedLegConvention,
> ql.DateGeneration.Forward,
> False,
> )
> floatingSchedule = ql.Schedule(
> swapStart,
> swapEnd,
> floatingLegTenor,
> calendar,
> floatingLegConvention,
> floatingLegConvention,
> ql.DateGeneration.Forward,
> False,
> )
>
> dummy = ql.VanillaSwap(
> payFixed, 100.0, fixedSchedule, 0.0, fixedLegDayCounter, floatingSchedule, index, 0.0, floatingLegDayCounter
> )
> dummy.setPricingEngine(swapEngine)
> atmRate = dummy.fairRate()
>
> atmSwap = ql.VanillaSwap(
> payFixed, 1000.0, fixedSchedule, atmRate, fixedLegDayCounter,
> floatingSchedule, index, 0.0, floatingLegDayCounter
> )
>
> otmSwap = ql.VanillaSwap(
> payFixed, 1000.0, fixedSchedule, atmRate * 1.2, fixedLegDayCounter,
> floatingSchedule, index, 0.0, floatingLegDayCounter
> )
>
> itmSwap = ql.VanillaSwap(
> payFixed, 1000.0, fixedSchedule, atmRate * 0.8, fixedLegDayCounter,
> floatingSchedule, index, 0.0, floatingLegDayCounter
> )
>
> atmSwap.setPricingEngine(swapEngine)
> otmSwap.setPricingEngine(swapEngine)
> itmSwap.setPricingEngine(swapEngine)
>
> helpers = [
> ql.SwaptionHelper(
> maturity,
> length,
> ql.makeQuoteHandle(vol),
> index,
> index.tenor(),
> index.dayCounter(),
> index.dayCounter(),
> termStructure,
> )
> for maturity, length, vol in swaptionVols
> ]
>
> times = {}
> for h in helpers:
> for t in h.times():
> times[t] = 1
> times = sorted(times.keys())
>
> grid = ql.TimeGrid(times, 30)
>
> G2model = ql.G2(termStructure)
> HWmodel = ql.HullWhite(termStructure)
> HWmodel2 = ql.HullWhite(termStructure)
> BKmodel = ql.BlackKarasinski(termStructure)
>
> # ### Calibrations
>
> for h in helpers:
> h.setPricingEngine(ql.G2SwaptionEngine(G2model, 6.0, 16))
> calibrate(G2model, helpers, 0.05, "G2 (analytic formulae)")
>
> for h in helpers:
> h.setPricingEngine(ql.JamshidianSwaptionEngine(HWmodel))
> calibrate(HWmodel, helpers, 0.05, "Hull-White (analytic formulae)")
>
> for h in helpers:
> h.setPricingEngine(ql.TreeSwaptionEngine(HWmodel2, grid))
> calibrate(HWmodel2, helpers, 0.05, "Hull-White (numerical calibration)")
>
> for h in helpers:
> h.setPricingEngine(ql.TreeSwaptionEngine(BKmodel, grid))
> calibrate(BKmodel, helpers, 0.05, "Black-Karasinski (numerical calibration)")
>
>
> # ### Price Bermudan swaptions on defined swaps
>
> bermudanDates = [d for d in fixedSchedule][:-1]
> exercise = ql.BermudanExercise(bermudanDates)
>
> atmSwaption = ql.Swaption(atmSwap, exercise)
> otmSwaption = ql.Swaption(otmSwap, exercise)
> itmSwaption = ql.Swaption(itmSwap, exercise)
>
> data = []
>
> # +
> atmSwaption.setPricingEngine(ql.TreeSwaptionEngine(G2model, 50))
> otmSwaption.setPricingEngine(ql.TreeSwaptionEngine(G2model, 50))
> itmSwaption.setPricingEngine(ql.TreeSwaptionEngine(G2model, 50))
>
> data.append(("G2 analytic", itmSwaption.NPV(), atmSwaption.NPV(), otmSwaption.NPV()))
>
> # +
> atmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel, 50))
> otmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel, 50))
> itmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel, 50))
>
> data.append(("HW analytic", itmSwaption.NPV(), atmSwaption.NPV(), otmSwaption.NPV()))
>
> # +
> atmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel2, 50))
> otmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel2, 50))
> itmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel2, 50))
>
> data.append(("HW numerical", itmSwaption.NPV(), atmSwaption.NPV(), otmSwaption.NPV()))
>
> # +
> atmSwaption.setPricingEngine(ql.TreeSwaptionEngine(BKmodel, 50))
> otmSwaption.setPricingEngine(ql.TreeSwaptionEngine(BKmodel, 50))
> itmSwaption.setPricingEngine(ql.TreeSwaptionEngine(BKmodel, 50))
>
> data.append(("BK numerical", itmSwaption.NPV(), atmSwaption.NPV(), otmSwaption.NPV()))
> # -
>
> print(pd.DataFrame(data, columns=["model", "in-the-money", "at-the-money", "out-of-the-money"]))
>
> # ### GSR live setValue bump test
>
> import faulthandler
> faulthandler.enable(all_threads=True)
>
> print("\n--- GSR live setValue bump test ---")
>
> # Minimal European swaption test on the same underlying ATM swap
> euroExercise = ql.EuropeanExercise(swapStart)
> euroSwaption = ql.Swaption(atmSwap, euroExercise)
>
> # For a European GSR setup: no step dates, one sigma, one reversion
> stepDates = []
> sigmas = [ql.QuoteHandle(ql.SimpleQuote(0.01))]
> reversions = [ql.QuoteHandle(ql.SimpleQuote(0.01))]
>
> gsr = ql.Gsr(termStructure, stepDates, sigmas, reversions)
> gsrEngine = ql.Gaussian1dSwaptionEngine(
> gsr,
> 64, # integration points
> 7.0, # std devs
> True,
> False,
> termStructure
> )
> euroSwaption.setPricingEngine(gsrEngine)
>
> base_rate = 0.04875825
>
> print("base rate:", base_rate)
> print("base npv :", euroSwaption.NPV())
>
> # Single bump up/down
> for bump in [1e-4, -1e-4, 0.0]:
> bumped = base_rate + bump
> print(f"\nsetValue -> {bumped:.8f}")
> rateQuote.setValue(bumped)
> print("npv =", euroSwaption.NPV())
>
> # Stress loop: repeatedly bump the observed quote and reprice
> print("\nstarting stress loop...")
> for i in range(200):
> bumped = base_rate + (1e-4 if i % 2 == 0 else -1e-4)
> rateQuote.setValue(bumped)
> npv = euroSwaption.NPV()
> if i % 20 == 0:
> print(f"iter {i:03d} rate={bumped:.8f} npv={npv}")
>
> # Restore
> rateQuote.setValue(base_rate)
> print("\nrestored rate:", base_rate)
> print("restored npv :", euroSwaption.NPV())
>
>
> Log:
>
> aleksisaliraza@Alis-MacBook-Pro:~/QuantLib-SWIG-1.42/Python/examples$ python3.14 bermudan-swaption.py
> Model: G2 (analytic formulae)
> Parameters: [ 0.0765369; 0.00279407; 0.0619638; 0.00954892; -1 ]
> maturity length volatility implied error
> 0 1Y 5Y 0.1148 0.101974 -0.012826
> 1 2Y 4Y 0.1108 0.105611 -0.005189
> 2 3Y 3Y 0.1070 0.106950 -0.000050
> 3 4Y 2Y 0.1021 0.107873 0.005773
> 4 5Y 1Y 0.1000 0.108556 0.008556
> Average error: 0.0065
> Model: Hull-White (analytic formulae)
> Parameters: [ 0.046379; 0.00586848 ]
> maturity length volatility implied error
> 0 1Y 5Y 0.1148 0.106223 -0.008577
> 1 2Y 4Y 0.1108 0.106292 -0.004508
> 2 3Y 3Y 0.1070 0.106338 -0.000662
> 3 4Y 2Y 0.1021 0.106439 0.004339
> 4 5Y 1Y 0.1000 0.106609 0.006609
> Average error: 0.0049
> Model: Hull-White (numerical calibration)
> Parameters: [ 0.0559314; 0.00609858 ]
> maturity length volatility implied error
> 0 1Y 5Y 0.1148 0.102941 -0.011859
> 1 2Y 4Y 0.1108 0.105452 -0.005348
> 2 3Y 3Y 0.1070 0.106560 -0.000440
> 3 4Y 2Y 0.1021 0.107365 0.005265
> 4 5Y 1Y 0.1000 0.108223 0.008223
> Average error: 0.0062
> Model: Black-Karasinski (numerical calibration)
> Parameters: [ 0.0442393; 0.120656 ]
> maturity length volatility implied error
> 0 1Y 5Y 0.1148 0.103094 -0.011706
> 1 2Y 4Y 0.1108 0.105637 -0.005163
> 2 3Y 3Y 0.1070 0.106656 -0.000344
> 3 4Y 2Y 0.1021 0.107333 0.005233
> 4 5Y 1Y 0.1000 0.108028 0.008028
> Average error: 0.0061
> model in-the-money at-the-money out-of-the-money
> 0 G2 analytic 42.919620 14.581452 3.443306
> 1 HW analytic 42.245449 12.924016 2.512541
> 2 HW numerical 42.340450 13.141040 2.614228
> 3 BK numerical 41.805673 13.012682 3.271628
>
> --- GSR live setValue bump test ---
> base rate: 0.04875825
> base npv : 16.787915348295005
>
> setValue -> 0.04885825
> Fatal Python error: Segmentation fault
>
> Current thread 0x00000001f0afd8c0 (most recent call first):
> File "/opt/homebrew/lib/python3.14/site-packages/QuantLib/QuantLib.py", line 6180 in setValue
> File "/Users/aleksisaliraza/QuantLib-SWIG-1.42/Python/examples/bermudan-swaption.py", line 277 in <module>
>
> Current thread's C stack trace (most recent call first):
> Binary file "/opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/Python", at _Py_DumpStack+0x44 [0x1036d79d8]
> Binary file "/opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/Python", at faulthandler_dump_c_stack+0x58 [0x1036e9f0c]
> Binary file "/opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/Python", at faulthandler_fatal_error+0x140 [0x1036e9dd0]
> Binary file "/usr/lib/system/libsystem_platform.dylib", at _sigtramp+0x38 [0x184cfd7a4]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
> <truncated rest of calls>
>
> Extension modules: QuantLib._QuantLib, numpy._core._multiarray_umath, numpy.linalg._umath_linalg, numpy.random._common, numpy.random.bit_generator, numpy.random._bounded_integers, numpy.random._pcg64, numpy.random._mt19937, numpy.random._generator, numpy.random._philox, numpy.random._sfc64, numpy.random.mtrand, pandas._libs.tslibs.ccalendar, pandas._libs.tslibs.np_datetime, pandas._libs.tslibs.dtypes, pandas._libs.tslibs.base, pandas._libs.tslibs.nattype, pandas._libs.tslibs.timezones, pandas._libs.tslibs.fields, pandas._libs.tslibs.timedeltas, pandas._libs.tslibs.tzconversion, pandas._libs.tslibs.timestamps, pandas._libs.properties, pandas._libs.tslibs.offsets, pandas._libs.tslibs.strptime, pandas._libs.tslibs.parsing, pandas._libs.tslibs.conversion, pandas._libs.tslibs.period, pandas._libs.tslibs.vectorized, pandas._libs.ops_dispatch, pandas._libs.missing, pandas._libs.hashtable, pandas._libs.algos, pandas._libs.interval, pandas._libs.lib, pandas._libs.ops, pandas._libs.hashing, pandas._libs.arrays, pandas._libs.tslib, pandas._libs.sparse, pandas._libs.internals, pandas._libs.indexing, pandas._libs.index, pandas._libs.writers, pandas._libs.join, pandas._libs.window.aggregations, pandas._libs.window.indexers, pandas._libs.reshape, pandas._libs.groupby, pandas._libs.json, pandas._libs.parsers, pandas._libs.testing (total: 52)
> Segmentation fault: 11
>
>
> _______________________________________________
> QuantLib-users mailing list
> Qua...@li...
> https://lists.sourceforge.net/lists/listinfo/quantlib-users
>
|
|
From: Aleksis A. R. <ale...@go...> - 2026-04-16 06:03:17
|
Just to follow up on this, it's this line:
void Gsr::update() {
if (stateProcess_ != nullptr) {
ext::static_pointer_cast<GsrProcess>(stateProcess_)->flushCache();
ext::static_pointer_cast<GsrProcess>(stateProcess_)->notifyObservers();
}
LazyObject::update();
}
in the gsr class that seems to be the cause. in my case, commenting out that line out and rebuilding from source fixes the problem of my gsr-based swaption risk runs.
That line by itself looks pretty harmless but i think
registerWith(stateProcess_);
further down leads into some sort of feedback loop (setValue() → notifyObservers() → Gsr::update() → stateProcess_->notifyObservers() → Gsr::update() → …) and ultimately a seg fault.
> On Apr 16, 2026, at 09:13, Aleksis Ali Raza <ale...@go...> wrote:
>
> Hi,
>
> I’m encountering what appears to be a regression or stability issue in QuantLib 1.42 related to the GSR (Gaussian short rate) model and the observer framework.
>
> Calling SimpleQuote.setValue() on a quote that is observed by a term structure used in a Gsr model causes a segmentation fault.
>
> This occurs even in a minimal standalone script (no threading, no external frameworks).
>
> Minimal reproduction
>
> The following sequence is sufficient:
> 1. Create a SimpleQuote
> 2. Build a FlatForward curve using that quote
> 3. Construct a Gsr model from that term structure
> 4. Attach a Gaussian1dSwaptionEngine
> 5. Call setValue() on the original SimpleQuote
>
> This results in a segmentation fault.
>
> I have modified the Python/examples/bermudan_swaption.py file and attached it below to illustrate (along with error log below it).
>
> Has something been changed in how one shud enquire / update quote objects that I missed? I am coming from 1.40, where this was not an issue. I am running python 3.14.4 on macOS 26.1.4 and using a source build of Quantlib/SWIG.
>
> Thanks, Aleksis
>
> # ---
> # jupyter:
> # jupytext:
> # formats: py:light
> # text_representation:
> # extension: .py
> # format_name: light
> # format_version: '1.5'
> # jupytext_version: 1.4.2
> # kernelspec:
> # display_name: Python 3
> # language: python
> # name: python3
> # ---
>
> # # Bermudan swaptions
> #
> # Copyright (©) 2004, 2005, 2006, 2007 StatPro Italia srl
> #
> # This file is part of QuantLib, a free-software/open-source library
> # for financial quantitative analysts and developers - https://www.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
> # <qua...@li...>. The license is also available online at
> # <https://www.quantlib.org/license.shtml>.
> #
> # 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.
>
> import QuantLib as ql
> import pandas as pd
>
> # ### Setup
>
> todaysDate = ql.Date(15, ql.February, 2002)
> ql.Settings.instance().evaluationDate = todaysDate
> calendar = ql.TARGET()
> settlementDate = ql.Date(19, ql.February, 2002)
>
>
> def calibrate(model, helpers, l, name):
> print("Model: %s" % name)
>
> method = ql.Simplex(l)
> model.calibrate(helpers, method, ql.EndCriteria(1000, 250, 1e-7, 1e-7, 1e-7))
>
> print("Parameters: %s" % model.params())
>
> totalError = 0.0
> data = []
> for swaption, helper in zip(swaptionVols, helpers):
> maturity, length, vol = swaption
> NPV = helper.modelValue()
> implied = helper.impliedVolatility(NPV, 1.0e-4, 1000, 0.05, 0.50)
> error = implied - vol
> totalError += abs(error)
> data.append((maturity, length, vol, implied, error))
> averageError = totalError / len(helpers)
>
> print(pd.DataFrame(data, columns=["maturity", "length", "volatility", "implied", "error"]))
>
> print("Average error: %.4f" % averageError)
>
>
> # ### Market data
>
> swaptionVols = [
> # maturity, length, volatility
> (ql.Period(1, ql.Years), ql.Period(5, ql.Years), 0.1148),
> (ql.Period(2, ql.Years), ql.Period(4, ql.Years), 0.1108),
> (ql.Period(3, ql.Years), ql.Period(3, ql.Years), 0.1070),
> (ql.Period(4, ql.Years), ql.Period(2, ql.Years), 0.1021),
> (ql.Period(5, ql.Years), ql.Period(1, ql.Years), 0.1000),
> ]
>
> # This is a flat yield term structure implying a 1x5 swap at 5%.
>
> rateQuote = ql.SimpleQuote(0.04875825)
> rate = ql.QuoteHandle(rateQuote)
> termStructure = ql.YieldTermStructureHandle(
> ql.FlatForward(settlementDate, rate, ql.Actual365Fixed())
> )
>
> # Define the ATM/OTM/ITM swaps:
>
> swapEngine = ql.DiscountingSwapEngine(termStructure)
>
> fixedLegFrequency = ql.Annual
> fixedLegTenor = ql.Period(1, ql.Years)
> fixedLegConvention = ql.Unadjusted
> floatingLegConvention = ql.ModifiedFollowing
> fixedLegDayCounter = ql.Thirty360(ql.Thirty360.European)
> floatingLegFrequency = ql.Semiannual
> floatingLegTenor = ql.Period(6, ql.Months)
>
> payFixed = ql.Swap.Payer
> fixingDays = 2
> index = ql.Euribor6M(termStructure)
> floatingLegDayCounter = index.dayCounter()
>
> swapStart = calendar.advance(settlementDate, 1, ql.Years, floatingLegConvention)
> swapEnd = calendar.advance(swapStart, 5, ql.Years, floatingLegConvention)
>
> fixedSchedule = ql.Schedule(
> swapStart,
> swapEnd,
> fixedLegTenor,
> calendar,
> fixedLegConvention,
> fixedLegConvention,
> ql.DateGeneration.Forward,
> False,
> )
> floatingSchedule = ql.Schedule(
> swapStart,
> swapEnd,
> floatingLegTenor,
> calendar,
> floatingLegConvention,
> floatingLegConvention,
> ql.DateGeneration.Forward,
> False,
> )
>
> dummy = ql.VanillaSwap(
> payFixed, 100.0, fixedSchedule, 0.0, fixedLegDayCounter, floatingSchedule, index, 0.0, floatingLegDayCounter
> )
> dummy.setPricingEngine(swapEngine)
> atmRate = dummy.fairRate()
>
> atmSwap = ql.VanillaSwap(
> payFixed, 1000.0, fixedSchedule, atmRate, fixedLegDayCounter,
> floatingSchedule, index, 0.0, floatingLegDayCounter
> )
>
> otmSwap = ql.VanillaSwap(
> payFixed, 1000.0, fixedSchedule, atmRate * 1.2, fixedLegDayCounter,
> floatingSchedule, index, 0.0, floatingLegDayCounter
> )
>
> itmSwap = ql.VanillaSwap(
> payFixed, 1000.0, fixedSchedule, atmRate * 0.8, fixedLegDayCounter,
> floatingSchedule, index, 0.0, floatingLegDayCounter
> )
>
> atmSwap.setPricingEngine(swapEngine)
> otmSwap.setPricingEngine(swapEngine)
> itmSwap.setPricingEngine(swapEngine)
>
> helpers = [
> ql.SwaptionHelper(
> maturity,
> length,
> ql.makeQuoteHandle(vol),
> index,
> index.tenor(),
> index.dayCounter(),
> index.dayCounter(),
> termStructure,
> )
> for maturity, length, vol in swaptionVols
> ]
>
> times = {}
> for h in helpers:
> for t in h.times():
> times[t] = 1
> times = sorted(times.keys())
>
> grid = ql.TimeGrid(times, 30)
>
> G2model = ql.G2(termStructure)
> HWmodel = ql.HullWhite(termStructure)
> HWmodel2 = ql.HullWhite(termStructure)
> BKmodel = ql.BlackKarasinski(termStructure)
>
> # ### Calibrations
>
> for h in helpers:
> h.setPricingEngine(ql.G2SwaptionEngine(G2model, 6.0, 16))
> calibrate(G2model, helpers, 0.05, "G2 (analytic formulae)")
>
> for h in helpers:
> h.setPricingEngine(ql.JamshidianSwaptionEngine(HWmodel))
> calibrate(HWmodel, helpers, 0.05, "Hull-White (analytic formulae)")
>
> for h in helpers:
> h.setPricingEngine(ql.TreeSwaptionEngine(HWmodel2, grid))
> calibrate(HWmodel2, helpers, 0.05, "Hull-White (numerical calibration)")
>
> for h in helpers:
> h.setPricingEngine(ql.TreeSwaptionEngine(BKmodel, grid))
> calibrate(BKmodel, helpers, 0.05, "Black-Karasinski (numerical calibration)")
>
>
> # ### Price Bermudan swaptions on defined swaps
>
> bermudanDates = [d for d in fixedSchedule][:-1]
> exercise = ql.BermudanExercise(bermudanDates)
>
> atmSwaption = ql.Swaption(atmSwap, exercise)
> otmSwaption = ql.Swaption(otmSwap, exercise)
> itmSwaption = ql.Swaption(itmSwap, exercise)
>
> data = []
>
> # +
> atmSwaption.setPricingEngine(ql.TreeSwaptionEngine(G2model, 50))
> otmSwaption.setPricingEngine(ql.TreeSwaptionEngine(G2model, 50))
> itmSwaption.setPricingEngine(ql.TreeSwaptionEngine(G2model, 50))
>
> data.append(("G2 analytic", itmSwaption.NPV(), atmSwaption.NPV(), otmSwaption.NPV()))
>
> # +
> atmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel, 50))
> otmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel, 50))
> itmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel, 50))
>
> data.append(("HW analytic", itmSwaption.NPV(), atmSwaption.NPV(), otmSwaption.NPV()))
>
> # +
> atmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel2, 50))
> otmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel2, 50))
> itmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel2, 50))
>
> data.append(("HW numerical", itmSwaption.NPV(), atmSwaption.NPV(), otmSwaption.NPV()))
>
> # +
> atmSwaption.setPricingEngine(ql.TreeSwaptionEngine(BKmodel, 50))
> otmSwaption.setPricingEngine(ql.TreeSwaptionEngine(BKmodel, 50))
> itmSwaption.setPricingEngine(ql.TreeSwaptionEngine(BKmodel, 50))
>
> data.append(("BK numerical", itmSwaption.NPV(), atmSwaption.NPV(), otmSwaption.NPV()))
> # -
>
> print(pd.DataFrame(data, columns=["model", "in-the-money", "at-the-money", "out-of-the-money"]))
>
> # ### GSR live setValue bump test
>
> import faulthandler
> faulthandler.enable(all_threads=True)
>
> print("\n--- GSR live setValue bump test ---")
>
> # Minimal European swaption test on the same underlying ATM swap
> euroExercise = ql.EuropeanExercise(swapStart)
> euroSwaption = ql.Swaption(atmSwap, euroExercise)
>
> # For a European GSR setup: no step dates, one sigma, one reversion
> stepDates = []
> sigmas = [ql.QuoteHandle(ql.SimpleQuote(0.01))]
> reversions = [ql.QuoteHandle(ql.SimpleQuote(0.01))]
>
> gsr = ql.Gsr(termStructure, stepDates, sigmas, reversions)
> gsrEngine = ql.Gaussian1dSwaptionEngine(
> gsr,
> 64, # integration points
> 7.0, # std devs
> True,
> False,
> termStructure
> )
> euroSwaption.setPricingEngine(gsrEngine)
>
> base_rate = 0.04875825
>
> print("base rate:", base_rate)
> print("base npv :", euroSwaption.NPV())
>
> # Single bump up/down
> for bump in [1e-4, -1e-4, 0.0]:
> bumped = base_rate + bump
> print(f"\nsetValue -> {bumped:.8f}")
> rateQuote.setValue(bumped)
> print("npv =", euroSwaption.NPV())
>
> # Stress loop: repeatedly bump the observed quote and reprice
> print("\nstarting stress loop...")
> for i in range(200):
> bumped = base_rate + (1e-4 if i % 2 == 0 else -1e-4)
> rateQuote.setValue(bumped)
> npv = euroSwaption.NPV()
> if i % 20 == 0:
> print(f"iter {i:03d} rate={bumped:.8f} npv={npv}")
>
> # Restore
> rateQuote.setValue(base_rate)
> print("\nrestored rate:", base_rate)
> print("restored npv :", euroSwaption.NPV())
>
> Log:
> aleksisaliraza@Alis-MacBook-Pro:~/QuantLib-SWIG-1.42/Python/examples$ python3.14 bermudan-swaption.py
> Model: G2 (analytic formulae)
> Parameters: [ 0.0765369; 0.00279407; 0.0619638; 0.00954892; -1 ]
> maturity length volatility implied error
> 0 1Y 5Y 0.1148 0.101974 -0.012826
> 1 2Y 4Y 0.1108 0.105611 -0.005189
> 2 3Y 3Y 0.1070 0.106950 -0.000050
> 3 4Y 2Y 0.1021 0.107873 0.005773
> 4 5Y 1Y 0.1000 0.108556 0.008556
> Average error: 0.0065
> Model: Hull-White (analytic formulae)
> Parameters: [ 0.046379; 0.00586848 ]
> maturity length volatility implied error
> 0 1Y 5Y 0.1148 0.106223 -0.008577
> 1 2Y 4Y 0.1108 0.106292 -0.004508
> 2 3Y 3Y 0.1070 0.106338 -0.000662
> 3 4Y 2Y 0.1021 0.106439 0.004339
> 4 5Y 1Y 0.1000 0.106609 0.006609
> Average error: 0.0049
> Model: Hull-White (numerical calibration)
> Parameters: [ 0.0559314; 0.00609858 ]
> maturity length volatility implied error
> 0 1Y 5Y 0.1148 0.102941 -0.011859
> 1 2Y 4Y 0.1108 0.105452 -0.005348
> 2 3Y 3Y 0.1070 0.106560 -0.000440
> 3 4Y 2Y 0.1021 0.107365 0.005265
> 4 5Y 1Y 0.1000 0.108223 0.008223
> Average error: 0.0062
> Model: Black-Karasinski (numerical calibration)
> Parameters: [ 0.0442393; 0.120656 ]
> maturity length volatility implied error
> 0 1Y 5Y 0.1148 0.103094 -0.011706
> 1 2Y 4Y 0.1108 0.105637 -0.005163
> 2 3Y 3Y 0.1070 0.106656 -0.000344
> 3 4Y 2Y 0.1021 0.107333 0.005233
> 4 5Y 1Y 0.1000 0.108028 0.008028
> Average error: 0.0061
> model in-the-money at-the-money out-of-the-money
> 0 G2 analytic 42.919620 14.581452 3.443306
> 1 HW analytic 42.245449 12.924016 2.512541
> 2 HW numerical 42.340450 13.141040 2.614228
> 3 BK numerical 41.805673 13.012682 3.271628
>
> --- GSR live setValue bump test ---
> base rate: 0.04875825
> base npv : 16.787915348295005
>
> setValue -> 0.04885825
> Fatal Python error: Segmentation fault
>
> Current thread 0x00000001f0afd8c0 (most recent call first):
> File "/opt/homebrew/lib/python3.14/site-packages/QuantLib/QuantLib.py", line 6180 in setValue
> File "/Users/aleksisaliraza/QuantLib-SWIG-1.42/Python/examples/bermudan-swaption.py", line 277 in <module>
>
> Current thread's C stack trace (most recent call first):
> Binary file "/opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/Python", at _Py_DumpStack+0x44 [0x1036d79d8]
> Binary file "/opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/Python", at faulthandler_dump_c_stack+0x58 [0x1036e9f0c]
> Binary file "/opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/Python", at faulthandler_fatal_error+0x140 [0x1036e9dd0]
> Binary file "/usr/lib/system/libsystem_platform.dylib", at _sigtramp+0x38 [0x184cfd7a4]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
> Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
> <truncated rest of calls>
>
> Extension modules: QuantLib._QuantLib, numpy._core._multiarray_umath, numpy.linalg._umath_linalg, numpy.random._common, numpy.random.bit_generator, numpy.random._bounded_integers, numpy.random._pcg64, numpy.random._mt19937, numpy.random._generator, numpy.random._philox, numpy.random._sfc64, numpy.random.mtrand, pandas._libs.tslibs.ccalendar, pandas._libs.tslibs.np_datetime, pandas._libs.tslibs.dtypes, pandas._libs.tslibs.base, pandas._libs.tslibs.nattype, pandas._libs.tslibs.timezones, pandas._libs.tslibs.fields, pandas._libs.tslibs.timedeltas, pandas._libs.tslibs.tzconversion, pandas._libs.tslibs.timestamps, pandas._libs.properties, pandas._libs.tslibs.offsets, pandas._libs.tslibs.strptime, pandas._libs.tslibs.parsing, pandas._libs.tslibs.conversion, pandas._libs.tslibs.period, pandas._libs.tslibs.vectorized, pandas._libs.ops_dispatch, pandas._libs.missing, pandas._libs.hashtable, pandas._libs.algos, pandas._libs.interval, pandas._libs.lib, pandas._libs.ops, pandas._libs.hashing, pandas._libs.arrays, pandas._libs.tslib, pandas._libs.sparse, pandas._libs.internals, pandas._libs.indexing, pandas._libs.index, pandas._libs.writers, pandas._libs.join, pandas._libs.window.aggregations, pandas._libs.window.indexers, pandas._libs.reshape, pandas._libs.groupby, pandas._libs.json, pandas._libs.parsers, pandas._libs.testing (total: 52)
> Segmentation fault: 11
|
|
From: Aleksis A. R. <ale...@go...> - 2026-04-16 04:13:51
|
Hi,
I’m encountering what appears to be a regression or stability issue in QuantLib 1.42 related to the GSR (Gaussian short rate) model and the observer framework.
Calling SimpleQuote.setValue() on a quote that is observed by a term structure used in a Gsr model causes a segmentation fault.
This occurs even in a minimal standalone script (no threading, no external frameworks).
Minimal reproduction
The following sequence is sufficient:
1. Create a SimpleQuote
2. Build a FlatForward curve using that quote
3. Construct a Gsr model from that term structure
4. Attach a Gaussian1dSwaptionEngine
5. Call setValue() on the original SimpleQuote
This results in a segmentation fault.
I have modified the Python/examples/bermudan_swaption.py file and attached it below to illustrate (along with error log below it).
Has something been changed in how one shud enquire / update quote objects that I missed? I am coming from 1.40, where this was not an issue. I am running python 3.14.4 on macOS 26.1.4 and using a source build of Quantlib/SWIG.
Thanks, Aleksis
# ---
# jupyter:
# jupytext:
# formats: py:light
# text_representation:
# extension: .py
# format_name: light
# format_version: '1.5'
# jupytext_version: 1.4.2
# kernelspec:
# display_name: Python 3
# language: python
# name: python3
# ---
# # Bermudan swaptions
#
# Copyright (©) 2004, 2005, 2006, 2007 StatPro Italia srl
#
# This file is part of QuantLib, a free-software/open-source library
# for financial quantitative analysts and developers - https://www.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
# <qua...@li...>. The license is also available online at
# <https://www.quantlib.org/license.shtml>.
#
# 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.
import QuantLib as ql
import pandas as pd
# ### Setup
todaysDate = ql.Date(15, ql.February, 2002)
ql.Settings.instance().evaluationDate = todaysDate
calendar = ql.TARGET()
settlementDate = ql.Date(19, ql.February, 2002)
def calibrate(model, helpers, l, name):
print("Model: %s" % name)
method = ql.Simplex(l)
model.calibrate(helpers, method, ql.EndCriteria(1000, 250, 1e-7, 1e-7, 1e-7))
print("Parameters: %s" % model.params())
totalError = 0.0
data = []
for swaption, helper in zip(swaptionVols, helpers):
maturity, length, vol = swaption
NPV = helper.modelValue()
implied = helper.impliedVolatility(NPV, 1.0e-4, 1000, 0.05, 0.50)
error = implied - vol
totalError += abs(error)
data.append((maturity, length, vol, implied, error))
averageError = totalError / len(helpers)
print(pd.DataFrame(data, columns=["maturity", "length", "volatility", "implied", "error"]))
print("Average error: %.4f" % averageError)
# ### Market data
swaptionVols = [
# maturity, length, volatility
(ql.Period(1, ql.Years), ql.Period(5, ql.Years), 0.1148),
(ql.Period(2, ql.Years), ql.Period(4, ql.Years), 0.1108),
(ql.Period(3, ql.Years), ql.Period(3, ql.Years), 0.1070),
(ql.Period(4, ql.Years), ql.Period(2, ql.Years), 0.1021),
(ql.Period(5, ql.Years), ql.Period(1, ql.Years), 0.1000),
]
# This is a flat yield term structure implying a 1x5 swap at 5%.
rateQuote = ql.SimpleQuote(0.04875825)
rate = ql.QuoteHandle(rateQuote)
termStructure = ql.YieldTermStructureHandle(
ql.FlatForward(settlementDate, rate, ql.Actual365Fixed())
)
# Define the ATM/OTM/ITM swaps:
swapEngine = ql.DiscountingSwapEngine(termStructure)
fixedLegFrequency = ql.Annual
fixedLegTenor = ql.Period(1, ql.Years)
fixedLegConvention = ql.Unadjusted
floatingLegConvention = ql.ModifiedFollowing
fixedLegDayCounter = ql.Thirty360(ql.Thirty360.European)
floatingLegFrequency = ql.Semiannual
floatingLegTenor = ql.Period(6, ql.Months)
payFixed = ql.Swap.Payer
fixingDays = 2
index = ql.Euribor6M(termStructure)
floatingLegDayCounter = index.dayCounter()
swapStart = calendar.advance(settlementDate, 1, ql.Years, floatingLegConvention)
swapEnd = calendar.advance(swapStart, 5, ql.Years, floatingLegConvention)
fixedSchedule = ql.Schedule(
swapStart,
swapEnd,
fixedLegTenor,
calendar,
fixedLegConvention,
fixedLegConvention,
ql.DateGeneration.Forward,
False,
)
floatingSchedule = ql.Schedule(
swapStart,
swapEnd,
floatingLegTenor,
calendar,
floatingLegConvention,
floatingLegConvention,
ql.DateGeneration.Forward,
False,
)
dummy = ql.VanillaSwap(
payFixed, 100.0, fixedSchedule, 0.0, fixedLegDayCounter, floatingSchedule, index, 0.0, floatingLegDayCounter
)
dummy.setPricingEngine(swapEngine)
atmRate = dummy.fairRate()
atmSwap = ql.VanillaSwap(
payFixed, 1000.0, fixedSchedule, atmRate, fixedLegDayCounter,
floatingSchedule, index, 0.0, floatingLegDayCounter
)
otmSwap = ql.VanillaSwap(
payFixed, 1000.0, fixedSchedule, atmRate * 1.2, fixedLegDayCounter,
floatingSchedule, index, 0.0, floatingLegDayCounter
)
itmSwap = ql.VanillaSwap(
payFixed, 1000.0, fixedSchedule, atmRate * 0.8, fixedLegDayCounter,
floatingSchedule, index, 0.0, floatingLegDayCounter
)
atmSwap.setPricingEngine(swapEngine)
otmSwap.setPricingEngine(swapEngine)
itmSwap.setPricingEngine(swapEngine)
helpers = [
ql.SwaptionHelper(
maturity,
length,
ql.makeQuoteHandle(vol),
index,
index.tenor(),
index.dayCounter(),
index.dayCounter(),
termStructure,
)
for maturity, length, vol in swaptionVols
]
times = {}
for h in helpers:
for t in h.times():
times[t] = 1
times = sorted(times.keys())
grid = ql.TimeGrid(times, 30)
G2model = ql.G2(termStructure)
HWmodel = ql.HullWhite(termStructure)
HWmodel2 = ql.HullWhite(termStructure)
BKmodel = ql.BlackKarasinski(termStructure)
# ### Calibrations
for h in helpers:
h.setPricingEngine(ql.G2SwaptionEngine(G2model, 6.0, 16))
calibrate(G2model, helpers, 0.05, "G2 (analytic formulae)")
for h in helpers:
h.setPricingEngine(ql.JamshidianSwaptionEngine(HWmodel))
calibrate(HWmodel, helpers, 0.05, "Hull-White (analytic formulae)")
for h in helpers:
h.setPricingEngine(ql.TreeSwaptionEngine(HWmodel2, grid))
calibrate(HWmodel2, helpers, 0.05, "Hull-White (numerical calibration)")
for h in helpers:
h.setPricingEngine(ql.TreeSwaptionEngine(BKmodel, grid))
calibrate(BKmodel, helpers, 0.05, "Black-Karasinski (numerical calibration)")
# ### Price Bermudan swaptions on defined swaps
bermudanDates = [d for d in fixedSchedule][:-1]
exercise = ql.BermudanExercise(bermudanDates)
atmSwaption = ql.Swaption(atmSwap, exercise)
otmSwaption = ql.Swaption(otmSwap, exercise)
itmSwaption = ql.Swaption(itmSwap, exercise)
data = []
# +
atmSwaption.setPricingEngine(ql.TreeSwaptionEngine(G2model, 50))
otmSwaption.setPricingEngine(ql.TreeSwaptionEngine(G2model, 50))
itmSwaption.setPricingEngine(ql.TreeSwaptionEngine(G2model, 50))
data.append(("G2 analytic", itmSwaption.NPV(), atmSwaption.NPV(), otmSwaption.NPV()))
# +
atmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel, 50))
otmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel, 50))
itmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel, 50))
data.append(("HW analytic", itmSwaption.NPV(), atmSwaption.NPV(), otmSwaption.NPV()))
# +
atmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel2, 50))
otmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel2, 50))
itmSwaption.setPricingEngine(ql.TreeSwaptionEngine(HWmodel2, 50))
data.append(("HW numerical", itmSwaption.NPV(), atmSwaption.NPV(), otmSwaption.NPV()))
# +
atmSwaption.setPricingEngine(ql.TreeSwaptionEngine(BKmodel, 50))
otmSwaption.setPricingEngine(ql.TreeSwaptionEngine(BKmodel, 50))
itmSwaption.setPricingEngine(ql.TreeSwaptionEngine(BKmodel, 50))
data.append(("BK numerical", itmSwaption.NPV(), atmSwaption.NPV(), otmSwaption.NPV()))
# -
print(pd.DataFrame(data, columns=["model", "in-the-money", "at-the-money", "out-of-the-money"]))
# ### GSR live setValue bump test
import faulthandler
faulthandler.enable(all_threads=True)
print("\n--- GSR live setValue bump test ---")
# Minimal European swaption test on the same underlying ATM swap
euroExercise = ql.EuropeanExercise(swapStart)
euroSwaption = ql.Swaption(atmSwap, euroExercise)
# For a European GSR setup: no step dates, one sigma, one reversion
stepDates = []
sigmas = [ql.QuoteHandle(ql.SimpleQuote(0.01))]
reversions = [ql.QuoteHandle(ql.SimpleQuote(0.01))]
gsr = ql.Gsr(termStructure, stepDates, sigmas, reversions)
gsrEngine = ql.Gaussian1dSwaptionEngine(
gsr,
64, # integration points
7.0, # std devs
True,
False,
termStructure
)
euroSwaption.setPricingEngine(gsrEngine)
base_rate = 0.04875825
print("base rate:", base_rate)
print("base npv :", euroSwaption.NPV())
# Single bump up/down
for bump in [1e-4, -1e-4, 0.0]:
bumped = base_rate + bump
print(f"\nsetValue -> {bumped:.8f}")
rateQuote.setValue(bumped)
print("npv =", euroSwaption.NPV())
# Stress loop: repeatedly bump the observed quote and reprice
print("\nstarting stress loop...")
for i in range(200):
bumped = base_rate + (1e-4 if i % 2 == 0 else -1e-4)
rateQuote.setValue(bumped)
npv = euroSwaption.NPV()
if i % 20 == 0:
print(f"iter {i:03d} rate={bumped:.8f} npv={npv}")
# Restore
rateQuote.setValue(base_rate)
print("\nrestored rate:", base_rate)
print("restored npv :", euroSwaption.NPV())
Log:
aleksisaliraza@Alis-MacBook-Pro:~/QuantLib-SWIG-1.42/Python/examples$ python3.14 bermudan-swaption.py
Model: G2 (analytic formulae)
Parameters: [ 0.0765369; 0.00279407; 0.0619638; 0.00954892; -1 ]
maturity length volatility implied error
0 1Y 5Y 0.1148 0.101974 -0.012826
1 2Y 4Y 0.1108 0.105611 -0.005189
2 3Y 3Y 0.1070 0.106950 -0.000050
3 4Y 2Y 0.1021 0.107873 0.005773
4 5Y 1Y 0.1000 0.108556 0.008556
Average error: 0.0065
Model: Hull-White (analytic formulae)
Parameters: [ 0.046379; 0.00586848 ]
maturity length volatility implied error
0 1Y 5Y 0.1148 0.106223 -0.008577
1 2Y 4Y 0.1108 0.106292 -0.004508
2 3Y 3Y 0.1070 0.106338 -0.000662
3 4Y 2Y 0.1021 0.106439 0.004339
4 5Y 1Y 0.1000 0.106609 0.006609
Average error: 0.0049
Model: Hull-White (numerical calibration)
Parameters: [ 0.0559314; 0.00609858 ]
maturity length volatility implied error
0 1Y 5Y 0.1148 0.102941 -0.011859
1 2Y 4Y 0.1108 0.105452 -0.005348
2 3Y 3Y 0.1070 0.106560 -0.000440
3 4Y 2Y 0.1021 0.107365 0.005265
4 5Y 1Y 0.1000 0.108223 0.008223
Average error: 0.0062
Model: Black-Karasinski (numerical calibration)
Parameters: [ 0.0442393; 0.120656 ]
maturity length volatility implied error
0 1Y 5Y 0.1148 0.103094 -0.011706
1 2Y 4Y 0.1108 0.105637 -0.005163
2 3Y 3Y 0.1070 0.106656 -0.000344
3 4Y 2Y 0.1021 0.107333 0.005233
4 5Y 1Y 0.1000 0.108028 0.008028
Average error: 0.0061
model in-the-money at-the-money out-of-the-money
0 G2 analytic 42.919620 14.581452 3.443306
1 HW analytic 42.245449 12.924016 2.512541
2 HW numerical 42.340450 13.141040 2.614228
3 BK numerical 41.805673 13.012682 3.271628
--- GSR live setValue bump test ---
base rate: 0.04875825
base npv : 16.787915348295005
setValue -> 0.04885825
Fatal Python error: Segmentation fault
Current thread 0x00000001f0afd8c0 (most recent call first):
File "/opt/homebrew/lib/python3.14/site-packages/QuantLib/QuantLib.py", line 6180 in setValue
File "/Users/aleksisaliraza/QuantLib-SWIG-1.42/Python/examples/bermudan-swaption.py", line 277 in <module>
Current thread's C stack trace (most recent call first):
Binary file "/opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/Python", at _Py_DumpStack+0x44 [0x1036d79d8]
Binary file "/opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/Python", at faulthandler_dump_c_stack+0x58 [0x1036e9f0c]
Binary file "/opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/Python", at faulthandler_fatal_error+0x140 [0x1036e9dd0]
Binary file "/usr/lib/system/libsystem_platform.dylib", at _sigtramp+0x38 [0x184cfd7a4]
Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib10Observable15notifyObserversEv+0x80 [0x10df9c4ec]
Binary file "/usr/local/lib/libQuantLib.0.dylib", at _ZN8QuantLib3Gsr6updateEv+0xb4 [0x10df754d8]
<truncated rest of calls>
Extension modules: QuantLib._QuantLib, numpy._core._multiarray_umath, numpy.linalg._umath_linalg, numpy.random._common, numpy.random.bit_generator, numpy.random._bounded_integers, numpy.random._pcg64, numpy.random._mt19937, numpy.random._generator, numpy.random._philox, numpy.random._sfc64, numpy.random.mtrand, pandas._libs.tslibs.ccalendar, pandas._libs.tslibs.np_datetime, pandas._libs.tslibs.dtypes, pandas._libs.tslibs.base, pandas._libs.tslibs.nattype, pandas._libs.tslibs.timezones, pandas._libs.tslibs.fields, pandas._libs.tslibs.timedeltas, pandas._libs.tslibs.tzconversion, pandas._libs.tslibs.timestamps, pandas._libs.properties, pandas._libs.tslibs.offsets, pandas._libs.tslibs.strptime, pandas._libs.tslibs.parsing, pandas._libs.tslibs.conversion, pandas._libs.tslibs.period, pandas._libs.tslibs.vectorized, pandas._libs.ops_dispatch, pandas._libs.missing, pandas._libs.hashtable, pandas._libs.algos, pandas._libs.interval, pandas._libs.lib, pandas._libs.ops, pandas._libs.hashing, pandas._libs.arrays, pandas._libs.tslib, pandas._libs.sparse, pandas._libs.internals, pandas._libs.indexing, pandas._libs.index, pandas._libs.writers, pandas._libs.join, pandas._libs.window.aggregations, pandas._libs.window.indexers, pandas._libs.reshape, pandas._libs.groupby, pandas._libs.json, pandas._libs.parsers, pandas._libs.testing (total: 52)
Segmentation fault: 11 |
|
From: Luigi B. <lui...@gm...> - 2026-04-14 07:48:42
|
QuantLib 1.42 is now available for download at < https://www.quantlib.org/download.shtml>; precompiled binaries are also available from PyPI and NuGet for Python and C# respectively. The list of changes for this release is at < https://github.com/lballabio/QuantLib/releases/tag/v1.42>. If you have any problems with this release, please report them here on the QuantLib mailing list (<qua...@li...>) or open a GitHub issue at <https://github.com/lballabio/quantlib/issues>. |
|
From: Dirk E. <ed...@de...> - 2026-04-13 17:13:44
|
On 13 April 2026 at 16:14, Luigi Ballabio wrote: | Ok, I see the problem — apt is still giving you SWIG 4.4.0: see < | https://buildd.debian.org/status/package.php?p=swig&suite=sid>. And quite | simply, 4.4.0 had a bug. Ahh. Easy enough. That explains it. | I would have thought sid had 4.4.1 at this point, given that it was | released last December. Upgrading to that SWIG version would fix the | build. If you can get hold of the maintainer, maybe you can nudge them? I will file a courtesy bug report gentlu nudging the maintainer(s). [ Some time passes. ] Done, see #1133688. Dirk -- Dirk Eddelbuettel | ed...@de... | http://dirk.eddelbuettel.com |
|
From: Luigi B. <lui...@gm...> - 2026-04-13 14:15:04
|
Ok, I see the problem — apt is still giving you SWIG 4.4.0: see < https://buildd.debian.org/status/package.php?p=swig&suite=sid>. And quite simply, 4.4.0 had a bug. I would have thought sid had 4.4.1 at this point, given that it was released last December. Upgrading to that SWIG version would fix the build. If you can get hold of the maintainer, maybe you can nudge them? Luigi On Mon, Apr 13, 2026 at 3:27 PM Dirk Eddelbuettel <ed...@de...> wrote: > > Hi Luigi, > > On 13 April 2026 at 14:54, Luigi Ballabio wrote: > | Hi Dirk, as usual thanks for the support. > | > | > I did once again have to carry the 'make Python 3.10 the minimum' patch > | forward as I had for the last two (?) cycles. > | > | Meaning that you tried not applying it and the build failed? > > Yes. There were really two problems here. First, once a patch is placed > into > debian/patches/ in the sources and referenced in debian/patches/series, it > is > always applied (using a system called `quilt`, which AFAIK is a general > tool > from outside Debian). Usually that 'just works' as the sources do not > change. Here it failed, I believe the last and this time. So with the > patch > NOT applied the build then failed. > > And am I guessing correctly that there even was/is the intent to automate > this as there are Python/setup.py and Python/setup.py.in and I may well be > doing something wrong if I have to patch both? > > | I thought it was no longer necessary if Debian picked up SWIG 4.4.1 (the > upgrade to > | 4.4.0 made it necessary, 4.4.1 should have fixed it). > > I would not know -- you are the swig expert here. > > | > I presume you keep it 3.8 for a reason? > | > | Well, since it doesn't require us to create extra wheels (the ones we > | create with the stable ABI work with all versions) it's supposed to be > | zero-cost and possibly make some people happier. But if it still > requires > | you to patch the build, I'll probably drop it next time. > > Can setup.py somehow pick up how/where it builds and could we condition on > that? There must be a way to support both 'very conservative Python 3.8' > for > manywheels and all that as well as our (and presumably other people's) use > on > newer systems that do not go back as far? In my case 'manually' adjusting > is > not a big deal. If we could make the patch auto-apply it would be even > easier. Could setup.py 'read a file' we patch (and that does not change) > or > 'listen to an env var' (that I could set) ? > > Dirk > > -- > Dirk Eddelbuettel | ed...@de... | http://dirk.eddelbuettel.com > |
|
From: Dirk E. <ed...@de...> - 2026-04-13 13:27:50
|
Hi Luigi, On 13 April 2026 at 14:54, Luigi Ballabio wrote: | Hi Dirk, as usual thanks for the support. | | > I did once again have to carry the 'make Python 3.10 the minimum' patch | forward as I had for the last two (?) cycles. | | Meaning that you tried not applying it and the build failed? Yes. There were really two problems here. First, once a patch is placed into debian/patches/ in the sources and referenced in debian/patches/series, it is always applied (using a system called `quilt`, which AFAIK is a general tool from outside Debian). Usually that 'just works' as the sources do not change. Here it failed, I believe the last and this time. So with the patch NOT applied the build then failed. And am I guessing correctly that there even was/is the intent to automate this as there are Python/setup.py and Python/setup.py.in and I may well be doing something wrong if I have to patch both? | I thought it was no longer necessary if Debian picked up SWIG 4.4.1 (the upgrade to | 4.4.0 made it necessary, 4.4.1 should have fixed it). I would not know -- you are the swig expert here. | > I presume you keep it 3.8 for a reason? | | Well, since it doesn't require us to create extra wheels (the ones we | create with the stable ABI work with all versions) it's supposed to be | zero-cost and possibly make some people happier. But if it still requires | you to patch the build, I'll probably drop it next time. Can setup.py somehow pick up how/where it builds and could we condition on that? There must be a way to support both 'very conservative Python 3.8' for manywheels and all that as well as our (and presumably other people's) use on newer systems that do not go back as far? In my case 'manually' adjusting is not a big deal. If we could make the patch auto-apply it would be even easier. Could setup.py 'read a file' we patch (and that does not change) or 'listen to an env var' (that I could set) ? Dirk -- Dirk Eddelbuettel | ed...@de... | http://dirk.eddelbuettel.com |
|
From: Luigi B. <lui...@gm...> - 2026-04-13 12:55:18
|
Hi Dirk, as usual thanks for the support. > I did once again have to carry the 'make Python 3.10 the minimum' patch forward as I had for the last two (?) cycles. Meaning that you tried not applying it and the build failed? I thought it was no longer necessary if Debian picked up SWIG 4.4.1 (the upgrade to 4.4.0 made it necessary, 4.4.1 should have fixed it). > I presume you keep it 3.8 for a reason? Well, since it doesn't require us to create extra wheels (the ones we create with the stable ABI work with all versions) it's supposed to be zero-cost and possibly make some people happier. But if it still requires you to patch the build, I'll probably drop it next time. Luigi On Fri, Apr 10, 2026 at 3:17 PM Dirk Eddelbuettel <ed...@de...> wrote: > > On 2 April 2026 at 12:29, Luigi Ballabio wrote: > | Hi all, > | a release candidate for QuantLib 1.42 is available at < > | https://github.com/lballabio/QuantLib/releases/tag/v1.42-rc>. Python > | wheels are also available at < > | https://test.pypi.org/project/QuantLib/1.42rc0/>, and a C# NuGet > package is > | at <https://int.nugettest.org/packages/QuantLib/1.42.0-rc>. If you have > | some time, please try it out and report any issues on GitHub (or here on > | the mailing list). Thanks! > > All good from here on the Debian side. Built (and uploaded) QuantLib > yesterday, followed up with QuantLib-SWIG (for just Python) now. I did once > again have to carry the 'make Python 3.10 the minimum' patch forward as I > had > for the last two (?) cycles. > > I presume you keep it 3.8 for a reason? If not a gentle nudge to adjust :) > The patch basically is still the same as > > > https://sources.debian.org/src/quantlib-swig/1.41-1/debian/patches/upgrade-minimum-python-version-3.10.patch > > but will get slightly changed line numbers when '1.41.99' (as we cannot > append '-rc' in our version sorting) pops up in due course. > > Cheers, Dirk > > -- > dirk.eddelbuettel.com | @eddelbuettel | ed...@de... > > Sponsor me for Tour de Shore 2026! Donate at > https://www.pledgereg.com/536435 > More about the ride at > > https://dirk.eddelbuettel.com/blog/2026/04/03#sponsor_tour_de_shore_2026 > > > _______________________________________________ > QuantLib-users mailing list > Qua...@li... > https://lists.sourceforge.net/lists/listinfo/quantlib-users > |
|
From: Dirk E. <ed...@de...> - 2026-04-10 13:16:29
|
On 2 April 2026 at 12:29, Luigi Ballabio wrote: | Hi all, | a release candidate for QuantLib 1.42 is available at < | https://github.com/lballabio/QuantLib/releases/tag/v1.42-rc>. Python | wheels are also available at < | https://test.pypi.org/project/QuantLib/1.42rc0/>, and a C# NuGet package is | at <https://int.nugettest.org/packages/QuantLib/1.42.0-rc>. If you have | some time, please try it out and report any issues on GitHub (or here on | the mailing list). Thanks! All good from here on the Debian side. Built (and uploaded) QuantLib yesterday, followed up with QuantLib-SWIG (for just Python) now. I did once again have to carry the 'make Python 3.10 the minimum' patch forward as I had for the last two (?) cycles. I presume you keep it 3.8 for a reason? If not a gentle nudge to adjust :) The patch basically is still the same as https://sources.debian.org/src/quantlib-swig/1.41-1/debian/patches/upgrade-minimum-python-version-3.10.patch but will get slightly changed line numbers when '1.41.99' (as we cannot append '-rc' in our version sorting) pops up in due course. Cheers, Dirk -- dirk.eddelbuettel.com | @eddelbuettel | ed...@de... Sponsor me for Tour de Shore 2026! Donate at https://www.pledgereg.com/536435 More about the ride at https://dirk.eddelbuettel.com/blog/2026/04/03#sponsor_tour_de_shore_2026 |
|
From: Luigi B. <lui...@gm...> - 2026-04-02 10:29:43
|
Hi all, a release candidate for QuantLib 1.42 is available at < https://github.com/lballabio/QuantLib/releases/tag/v1.42-rc>. Python wheels are also available at < https://test.pypi.org/project/QuantLib/1.42rc0/>, and a C# NuGet package is at <https://int.nugettest.org/packages/QuantLib/1.42.0-rc>. If you have some time, please try it out and report any issues on GitHub (or here on the mailing list). Thanks! Luigi |
|
From: Luigi B. <lui...@gm...> - 2026-03-16 09:49:55
|
Hello Chirag,
please don't use the latest 1.41. Backporting your class manually
might skip some steps. Use a checkout from the latest master from GitHub,
where your FxForward is already included correctly. Use a checkout for the
SWIG part too; you'll have to do that anyway in order to open a pull
request.
Luigi
On Mon, Mar 16, 2026 at 8:53 AM Chirag Desai <chi...@ya...>
wrote:
> Hi Luigi
>
> I am very sorry but may not have been clear enough
>
> 1) I got the latest versions of Quantlib 1.41 and Quantlib-SWIG 1.41
> 2) I manually added the FXForward class in my local version and it builds
> fine and the test for it under testsuite works fine
> 3) I added the fxforward.i and amended ql.i
> 4) When I build the python wheel it cannot find the FX Forward class
>
> (When I do the above exercise without the FX Forward class, I can build
> the wheel successfully and all works well)
>
> [image: Inline image]
>
> On Sunday, March 15, 2026 at 12:33:03 AM GMT+8, Luigi Ballabio <
> lui...@gm...> wrote:
>
>
> Your new class is not in QuantLib 1.41. You need to check out the latest
> versions of QuantLib and QuantLib-SWIG from GitHub and use those ones.
>
> Luigi
>
>
> On Sat, Mar 14, 2026 at 5:41 AM Chirag Desai <chi...@ya...>
> wrote:
>
> Thank you Mr Luigi
>
> 1) I added in ql.i and recompiled Quantlib as well
>
> 2) I get error messages now when building the wheel stating FXForward
> class cannot be found. I have attached the error messages.
>
> Build was successful
> [image: Inline image]
>
> [image: Inline image]
>
>
>
> On Friday, March 13, 2026 at 10:50:47 PM GMT+8, Luigi Ballabio <
> lui...@gm...> wrote:
>
>
> Hi Chirag, did you add your fxforward.i to the files included in ql.i ?
>
> Luigi
>
>
> On Fri, Mar 13, 2026 at 7:42 AM Chirag Desai <chi...@ya...>
> wrote:
>
> Hi All
> I built Quantlib with the new fxforward.hpp, fxforward.cpp ,
> fxforwarddiscountingengine hpp.cpp which I had recently contributed and
> successfully in my own version of Quantlib 1.41. All tests work
>
> I placed my fxforward.i file and swig runs successfully
>
> The wheel is generated successfully
>
> I am able to pip install the wheel and successfully can “import Quantlib
> as ql” and also can run certain Python files under SWIG in Examples and
> Tests (european-option.py, test_blackformula.py)
>
> For some reason my FX Forward tests all fail. (attached test_fxforward.py)
>
> How do I check if my FXForward class was successfully exposed to the
> Python API ? If any tools available ?
>
> When I do ql. there is no dropdown either in IDE like VSCode to see what
> ql functions are being exposed in Python ?
> Thank you all
>
>
>
>
>
>
|
|
From: Chirag D. <chi...@ya...> - 2026-03-16 07:53:51
|
Hi Luigi
I am very sorry but may not have been clear enough
1) I got the latest versions of Quantlib 1.41 and Quantlib-SWIG 1.412) I manually added the FXForward class in my local version and it builds fine and the test for it under testsuite works fine3) I added the fxforward.i and amended ql.i4) When I build the python wheel it cannot find the FX Forward class
(When I do the above exercise without the FX Forward class, I can build the wheel successfully and all works well)
On Sunday, March 15, 2026 at 12:33:03 AM GMT+8, Luigi Ballabio <lui...@gm...> wrote:
Your new class is not in QuantLib 1.41. You need to check out the latest versions of QuantLib and QuantLib-SWIG from GitHub and use those ones.
Luigi
On Sat, Mar 14, 2026 at 5:41 AM Chirag Desai <chi...@ya...> wrote:
Thank you Mr Luigi
1) I added in ql.i and recompiled Quantlib as well
2) I get error messages now when building the wheel stating FXForward class cannot be found. I have attached the error messages.
Build was successful
On Friday, March 13, 2026 at 10:50:47 PM GMT+8, Luigi Ballabio <lui...@gm...> wrote:
Hi Chirag, did you add your fxforward.i to the files included in ql.i ?
Luigi
On Fri, Mar 13, 2026 at 7:42 AM Chirag Desai <chi...@ya...> wrote:
Hi AllI built Quantlib with the newfxforward.hpp, fxforward.cpp , fxforwarddiscountingengine hpp.cpp which I had recently contributed and successfullyin my own version of Quantlib 1.41. All tests work
I placed my fxforward.i file andswig runs successfully
The wheel is generated successfully
I am able to pip install the wheeland successfully can “import Quantlib as ql” and also can run certain Pythonfiles under SWIG in Examples and Tests (european-option.py,test_blackformula.py)
For some reason my FX Forward testsall fail. (attached test_fxforward.py)
How do I check if my FXForward classwas successfully exposed to the Python API ? If any tools available ?
When I do ql. there is no dropdown either in IDE like VSCodeto see what ql functions are being exposed in Python ?
Thank you all
|
|
From: Luigi B. <lui...@gm...> - 2026-03-14 16:33:14
|
Your new class is not in QuantLib 1.41. You need to check out the latest versions of QuantLib and QuantLib-SWIG from GitHub and use those ones. Luigi On Sat, Mar 14, 2026 at 5:41 AM Chirag Desai <chi...@ya...> wrote: > Thank you Mr Luigi > > 1) I added in ql.i and recompiled Quantlib as well > > 2) I get error messages now when building the wheel stating FXForward > class cannot be found. I have attached the error messages. > > Build was successful > [image: Inline image] > > [image: Inline image] > > > > On Friday, March 13, 2026 at 10:50:47 PM GMT+8, Luigi Ballabio < > lui...@gm...> wrote: > > > Hi Chirag, did you add your fxforward.i to the files included in ql.i ? > > Luigi > > > On Fri, Mar 13, 2026 at 7:42 AM Chirag Desai <chi...@ya...> > wrote: > > Hi All > I built Quantlib with the new fxforward.hpp, fxforward.cpp , > fxforwarddiscountingengine hpp.cpp which I had recently contributed and > successfully in my own version of Quantlib 1.41. All tests work > > I placed my fxforward.i file and swig runs successfully > > The wheel is generated successfully > > I am able to pip install the wheel and successfully can “import Quantlib > as ql” and also can run certain Python files under SWIG in Examples and > Tests (european-option.py, test_blackformula.py) > > For some reason my FX Forward tests all fail. (attached test_fxforward.py) > > How do I check if my FXForward class was successfully exposed to the > Python API ? If any tools available ? > > When I do ql. there is no dropdown either in IDE like VSCode to see what > ql functions are being exposed in Python ? > Thank you all > > > > > > |
|
From: Chirag D. <chi...@ya...> - 2026-03-14 04:41:29
|
Thank you Mr Luigi
1) I added in ql.i and recompiled Quantlib as well
2) I get error messages now when building the wheel stating FXForward class cannot be found. I have attached the error messages.
Build was successful
On Friday, March 13, 2026 at 10:50:47 PM GMT+8, Luigi Ballabio <lui...@gm...> wrote:
Hi Chirag, did you add your fxforward.i to the files included in ql.i ?
Luigi
On Fri, Mar 13, 2026 at 7:42 AM Chirag Desai <chi...@ya...> wrote:
Hi AllI built Quantlib with the newfxforward.hpp, fxforward.cpp , fxforwarddiscountingengine hpp.cpp which I had recently contributed and successfullyin my own version of Quantlib 1.41. All tests work
I placed my fxforward.i file andswig runs successfully
The wheel is generated successfully
I am able to pip install the wheeland successfully can “import Quantlib as ql” and also can run certain Pythonfiles under SWIG in Examples and Tests (european-option.py,test_blackformula.py)
For some reason my FX Forward testsall fail. (attached test_fxforward.py)
How do I check if my FXForward classwas successfully exposed to the Python API ? If any tools available ?
When I do ql. there is no dropdown either in IDE like VSCodeto see what ql functions are being exposed in Python ?
Thank you all
|
|
From: Luigi B. <lui...@gm...> - 2026-03-13 14:50:52
|
Hi Chirag, did you add your fxforward.i to the files included in ql.i ? Luigi On Fri, Mar 13, 2026 at 7:42 AM Chirag Desai <chi...@ya...> wrote: > Hi All > I built Quantlib with the new fxforward.hpp, fxforward.cpp , > fxforwarddiscountingengine hpp.cpp which I had recently contributed and > successfully in my own version of Quantlib 1.41. All tests work > > I placed my fxforward.i file and swig runs successfully > > The wheel is generated successfully > > I am able to pip install the wheel and successfully can “import Quantlib > as ql” and also can run certain Python files under SWIG in Examples and > Tests (european-option.py, test_blackformula.py) > > For some reason my FX Forward tests all fail. (attached test_fxforward.py) > > How do I check if my FXForward class was successfully exposed to the > Python API ? If any tools available ? > > When I do ql. there is no dropdown either in IDE like VSCode to see what > ql functions are being exposed in Python ? > > Thank you all > > > > > |
|
From: Chirag D. <chi...@ya...> - 2026-03-13 06:42:43
|
Hi AllI built Quantlib with the newfxforward.hpp, fxforward.cpp , fxforwarddiscountingengine hpp.cpp which I had recently contributed and successfullyin my own version of Quantlib 1.41. All tests work I placed my fxforward.i file andswig runs successfully The wheel is generated successfully I am able to pip install the wheeland successfully can “import Quantlib as ql” and also can run certain Pythonfiles under SWIG in Examples and Tests (european-option.py,test_blackformula.py) For some reason my FX Forward testsall fail. (attached test_fxforward.py) How do I check if my FXForward classwas successfully exposed to the Python API ? If any tools available ? When I do ql. there is no dropdown either in IDE like VSCodeto see what ql functions are being exposed in Python ? Thank you all |