Fix forwards - #90
Conversation
There was a problem hiding this comment.
Pull request overview
This PR reworks forward and discount-curve calibration for option surfaces so that option pricing uses per-maturity forwards calibrated from put-call parity, while discount curves are derived from those parity forwards using selectable curve models. It also exposes curve-model selection through the API and frontend, adds a full tutorial + example, and updates fixtures/tests accordingly.
Changes:
- Add parity-based forward calibration (
calibrate_forwards) and make surfaces price off parity forwards viapricing_forward. - Rework curve calibration to fit quote/asset discount curves from parity-implied discount factors (with a short-maturity exclusion) and support “fixed” curves (e.g. no-discount).
- Add API/frontend curve selection, new Deribit offline loader builder, and extensive documentation/examples/tests.
Reviewed changes
Copilot reviewed 21 out of 22 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| quantflow/rates/no_discount.py | Removes the no-op calibration wrapper and documents that NoDiscountCurve.calibrator() is None. |
| quantflow/rates/interpolated.py | Seeds interpolated curves from an observation grid and averages duplicate TTMs into single nodes. |
| quantflow/rates/calibration.py | Adds YieldCurveCalibration.prepare() and calls it before joint optimisation. |
| quantflow/options/surface.py | Introduces parity_forward / pricing_forward, parity-forward calibration, and parity-forward-based curve calibration. |
| quantflow/options/ssvi.py | Adds fallback behavior in anchored eSSVI slice fitting when no feasible rho is found (uses previous slice when possible). |
| quantflow/options/parity.py | Adds weighted parity regression utilities to calibrate forwards and quote discount factors with a forward held fixed. |
| quantflow/data/deribit.py | Refactors Deribit loader creation into a reusable loader_from_book() for offline rebuilding. |
| quantflow_tests/test_vasicek_curve.py | Updates tests for the new “no calibrator” behavior on NoDiscountCurve. |
| quantflow_tests/test_surface_methods.py | Adds tests validating pricing_forward propagation into pricing + term-structure outputs. |
| quantflow_tests/test_surface_calibration.py | Adds coverage for new curve calibration modes and parity-forward calibration behavior. |
| quantflow_tests/test_app.py | Updates API tests to build a loader via mocked Yahoo data and verifies per-curve-selection caching. |
| mkdocs.yml | Adds the new curve calibration tutorial to mkdocs navigation. |
| frontend/src/volatility-surface.md | Adds UI controls for curve-model selection and updates curve labeling/display. |
| docs/tutorials/index.md | Links the new “Discount Curves from Option Prices” tutorial. |
| docs/tutorials/curve_calibration.md | New tutorial explaining parity-forward calibration and discount-curve derivation workflow. |
| docs/glossary.md | Expands put-call parity glossary entry to include spot/discount-factor form. |
| docs/examples/curve_calibration.py | New reproducible example generating figures and structured outputs for the tutorial. |
| docs/examples/_utils.py | Adds helper to rebuild BTC Deribit loader from recorded fixture (offline). |
| dev/tools/record_fixtures.py | Adds Deribit fixture recording to support the new tutorial/example workflow. |
| app/api/volatility.py | Adds query params for curve selection, updates caching key, and returns parity-forward term structure. |
| app/api/docs/volatility_surface.md | Documents parity-forward pricing and curve-model selection behavior in the API. |
| forward = parities.calibrate_forward(band=band, min_pairs=min_pairs) | ||
| section.parity_forward = to_decimal(round(forward, 8)) if forward else None | ||
| if section.parity_forward is not None: |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #90 +/- ##
==========================================
+ Coverage 88.75% 89.03% +0.27%
==========================================
Files 84 85 +1
Lines 5142 5483 +341
==========================================
+ Hits 4564 4882 +318
- Misses 578 601 +23 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 23 out of 25 changed files in this pull request and generated no new comments.
Suppressed comments (1)
quantflow/options/surface.py:1580
- In
calibrate_curves, the per-maturityquote_discount()regression is run unconditionally and must succeed to include a maturity (dq is None->continue). Whenquote_curveisNone(treated as known per docstring), this can still drop all maturities and raiseValueError("No parity forwards available..."), even though discounting can be taken directly from the existingself.quote_curveand only the asset curve needs fitting. Consider bypassing the regression whenquote_curveis known and sourcingdqfromself.quote_curve.discount_factor(ttm)instead.
parities = section.put_call_parities(
spot, ref_date=ref_date, max_pairs=max_pairs
)
forward = float(section.parity_forward / spot)
if (dq := parities.quote_discount(forward)) is None:
No description provided.