From bee4ccfbe4f9167d5e91c47e78b5d62f0af1939a Mon Sep 17 00:00:00 2001 From: Luca Date: Sat, 8 Aug 2026 14:47:29 +0100 Subject: [PATCH] Improve docs --- docs/api/rates/index.md | 2 ++ docs/api/rates/yield_curve.md | 3 +++ docs/examples/spx_vol_surface.py | 1 + docs/tutorials/spx_vol_surface.md | 6 +++++- docs/tutorials/volatility_surface.md | 15 +++++++++++++++ quantflow/data/yahoo.py | 1 + quantflow/rates/__init__.py | 10 ++++++++++ 7 files changed, 37 insertions(+), 1 deletion(-) diff --git a/docs/api/rates/index.md b/docs/api/rates/index.md index 066d920a..0ed512be 100644 --- a/docs/api/rates/index.md +++ b/docs/api/rates/index.md @@ -14,6 +14,8 @@ The central concept is the [discount factor](../../glossary.md#discount-factor) The trivial [NoDiscountCurve][quantflow.rates.no_discount.NoDiscountCurve] implementation has zero rates, so its discount factor is always one. +[AnyYieldCurve][quantflow.rates.AnyYieldCurve] is a discriminated union of all concrete curve models: use it for Pydantic fields that must accept any curve and round trip through JSON. + **[Interpolated Curves](interpolated.md)** build the term structure directly from observed zero rates at a set of anchor dates. [InterpolatedLinearCurve][quantflow.rates.interpolated.InterpolatedLinearCurve] interpolates the zero rate piecewise linearly, while [InterpolatedMonotonicCubicCurve][quantflow.rates.interpolated.InterpolatedMonotonicCubicCurve] uses a shape-preserving cubic spline. **[CIRCurve](cir.md)** is a short-rate term-structure model derived from the Cox-Ingersoll-Ross process, with positive-rate dynamics and closed-form discount factors. diff --git a/docs/api/rates/yield_curve.md b/docs/api/rates/yield_curve.md index be15ea3a..3f439a5c 100644 --- a/docs/api/rates/yield_curve.md +++ b/docs/api/rates/yield_curve.md @@ -5,3 +5,6 @@ ::: quantflow.rates.no_discount.NoDiscountCurve + + +::: quantflow.rates.AnyYieldCurve diff --git a/docs/examples/spx_vol_surface.py b/docs/examples/spx_vol_surface.py index da170d72..f248443f 100644 --- a/docs/examples/spx_vol_surface.py +++ b/docs/examples/spx_vol_surface.py @@ -6,6 +6,7 @@ chain = json.loads(gzip.decompress((FIXTURES / "yahoo_spx.json.gz").read_bytes())) loader = Yahoo.loader_from_chain(chain, exclude_volume=1) +loader.calibrate_curves() surface = loader.surface() surface.bs() surface.disable_outliers() diff --git a/docs/tutorials/spx_vol_surface.md b/docs/tutorials/spx_vol_surface.md index 8d79a32d..469058c3 100644 --- a/docs/tutorials/spx_vol_surface.md +++ b/docs/tutorials/spx_vol_surface.md @@ -16,7 +16,11 @@ the raw chain dictionary into a are non-inverse (quoted in USD) and Yahoo does not provide forwards, so each maturity's forward is recovered from put-call parity inside the loader. -Once the loader has the data, [surface()][quantflow.options.surface.GenericVolSurfaceLoader.surface] +Once the loader has the data, +[calibrate_curves()][quantflow.options.surface.GenericVolSurfaceLoader.calibrate_curves] +calibrates the parity forwards and fits the discount curves (see the +[volatility surface tutorial](volatility_surface.md) for details). Then +[surface()][quantflow.options.surface.GenericVolSurfaceLoader.surface] builds the [VolSurface][quantflow.options.surface.VolSurface], [bs()][quantflow.options.surface.VolSurface.bs] inverts each bid and ask through Black-Scholes, and diff --git a/docs/tutorials/volatility_surface.md b/docs/tutorials/volatility_surface.md index fa3447e9..92c21f2b 100644 --- a/docs/tutorials/volatility_surface.md +++ b/docs/tutorials/volatility_surface.md @@ -34,6 +34,21 @@ Key parameters of `volatility_surface_loader`: ## Building the Surface +Before creating the surface, calibrate the forwards and discount curves with +[calibrate_curves][quantflow.options.surface.GenericVolSurfaceLoader.calibrate_curves]: + +```python +loader.calibrate_curves() +``` + +This step infers the forward price of each maturity from put-call parity and fits +the asset discount curve to the resulting discount factors. The surface prices +options off these calibrated forwards: skipping the call leaves the surface with +forwards taken from the raw futures quotes, which are often illiquid or stale. + +See [Extracting Forwards and Discount Factors](#extracting-forwards-and-discount-factors) +below for how the calibration works. + The loader holds the raw market data. Call [surface()][quantflow.options.surface.GenericVolSurfaceLoader.surface] to construct a [VolSurface][quantflow.options.surface.VolSurface]: diff --git a/quantflow/data/yahoo.py b/quantflow/data/yahoo.py index 8e91bd39..5f4968c8 100644 --- a/quantflow/data/yahoo.py +++ b/quantflow/data/yahoo.py @@ -40,6 +40,7 @@ class Yahoo(HttpxClient): ```python async with Yahoo() as yahoo: loader = await yahoo.volatility_surface_loader("AAPL") + loader.calibrate_curves() surface = loader.surface() ``` """ diff --git a/quantflow/rates/__init__.py b/quantflow/rates/__init__.py index 85f8855e..3e6e92c9 100644 --- a/quantflow/rates/__init__.py +++ b/quantflow/rates/__init__.py @@ -40,6 +40,16 @@ ], Field(discriminator="curve_type"), ] +"""Discriminated union of all concrete +[YieldCurve][quantflow.rates.yield_curve.YieldCurve] implementations. + +Use this type for Pydantic fields that can hold any curve model, such as the +quote and asset curves of a +[VolSurface][quantflow.options.surface.VolSurface]. + +The `curve_type` discriminator selects the concrete class during validation, +so curves serialise to and from JSON without losing their type. +""" YieldCurve.register_curve_types( NoDiscountCurve,