I'm using quantlib via the quantlib-python or open-source-risk-engine both on pypi. The question relates whether it's possible to extend QuantLib term structure base classes in python rather than C++. My guess is you can't, but wanted to double check.


I've hit a limitation whereby the available BlackVarianceSurface is unable to take different strikes for different tenors when building a surface - my FX market instruments are quotes in moneyness, not absolutely strikes.

I've found ORE's extension - FxBlackVannaVolgaVolatilitySurface, which is very close to what I want, but only handles market quotes at a single delta (eg only 25-delta by default).

So, in order to add more market deltas, I was hoping to extend the parent class - BlackVolatilityTermStructure. However, the python bindings only seem to expose its parent BlackVolTermStructure - the idea being to write a new FX Surface implementation, following the C++ for FxBlackVannaVolgaVolatilitySurface as a rough guide, but to do it in python.

I'm not sure extending BlackVolTermStructure in python is the right thing to do? My test class extending BlackVolTermStructure instantiates OK, but when I try to create a BlackVolTermStructureHandle from the instance it fails (see below). It looks to me like QuantLib is expecting a shared pointer to the class which I'm assuming I can't provide to a python-implemented child class (error below)?

My questions are:

  • Is it possible to create your own extended volatility term structures using only python, or am I should I write it in C++ and build python bindings to my C++ extension?
  • Given it's a small extension is there a way to bundle this as a separate library I build in C++ rather than forking and changing QuantLib itself? Any guides on this?
  • On the off-chance if anyone has managed to use FxBlackVannaVolgaVolatilitySurface from ORE in python with (for example) 10 and 25 market deltas on the same surface that would remove the need for me to extend!
  • Perhaps there's a better approach to avoid extending that I'm missing, given what I'm trying to build is quite typical for FX Option pricing - any advice gladly received. I'm not against implementing my extension in C++, but would prefer to avoid this complication if possible.
self = <ORE.ORE.BlackVolTermStructureHandle;  >, args = (<lib.opcalc.volatility.vol_surface.CustomVolatilitySurface;  >,)

    def __init__(self, *args):
>       _ORE.BlackVolTermStructureHandle_swiginit(self, _ORE.new_BlackVolTermStructureHandle(*args))
E       TypeError: Wrong number or type of arguments for overloaded function 'new_BlackVolTermStructureHandle'.
E         Possible C/C++ prototypes are:
E           Handle< BlackVolTermStructure >::Handle(ext::shared_ptr< BlackVolTermStructure > const &)
E           Handle< BlackVolTermStructure >::Handle()

venv-osre/lib/python3.11/site-packages/ORE/ORE.py:8657: TypeError

2 Answers 2


I think you can use a combination of the ql.DeltaVolQuote and ql.BlackDeltaCalculator classes with the .strikeFromDelta attribute to switch between strikes and delta quotes, then use ql.BlackVarianceSurface, to achieve your goal. Take a look at this answer where something similar is being done.

  • 1
    $\begingroup$ Thanks for the hint - by using a common set of strikes across all tenors I was able to construct a 1D strike array just as your example explained and this seems to work pretty well. I've marked this as the answer - @Luigi Ballabio's answer is also valid and good to know, and given that fact this answer was the right approach for me. Thanks both! $\endgroup$
    – Phil
    Commented Nov 1, 2023 at 15:30
  • $\begingroup$ One other quick question I had on this - how safe is it to interpolate on the expiry axis? For example if our surface has a 2W and 1M point, but we wanted to get the strike at the 3W point. I suspect the correct thing to interpolate the inputted market instruments (ATM Straddle/Risk Reversal/Butterfly) quotes using the near and far instruments and use this to construct my vols and strikes at the 3W point. Perhaps relying on the generated BlackVarianceSurface to interpolate expiries off-tenor is not as safe as interpolating the market instruments that are used to create the surface inputs? $\endgroup$
    – Phil
    Commented Nov 7, 2023 at 14:44
  • $\begingroup$ That's a good question - haven't tested this myself but i'm not quite sure how u would deal with off-tenor interpolation with market inputted instruments - will u keep amending your code to add in the new date into the moneyness to strike conversion, then rebuilding the blackvariancesurface? I would check the difference between the two interpolation options, but would go with the surface interpolation as that's the whole point of building it. I wouldn't expect the difference to be large. $\endgroup$
    – user35980
    Commented Nov 7, 2023 at 14:52
  • $\begingroup$ I will test this shortly to see what the differences are. As you point out it doesn't fit well with QuantLib's BlackVarianceSurface because you'd have to build a new surface every time a new off-tenor expiry was requested, so really you just need to build a slice of surface for the tenor. I guess it becomes a matter of how often you're taking market data updates to whether persisting these slices makes sense. I note Open Source Risk engine has an FX specific surface generator taking market quotes directly, but still seams to return an 2D QuantLib surface: FxBlackVannaVolgaVolatilitySurface. $\endgroup$
    – Phil
    Commented Nov 8, 2023 at 8:40
  • $\begingroup$ Reg ORE, I would think the underlying analytics are probably doing something similar to what you're doing using blackvariancesurface i.e. converting market quotes to strike vols... etc but then using something more sophisticated than linear interpolation (namely vannaloga). Would be interesting to know if off-mkt tenors return similar results for your constructed surface and ORE's. If I'm right then these would be identical if u set the vannavolga parameters such that they reproduce linear interpolation in ORE. $\endgroup$
    – user35980
    Commented Nov 8, 2023 at 9:51

Just to confirm: subclassing the Python wrappers doesn't work.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.