Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds an SSVI (Surface SVI) volatility parametrisation to the options module, along with documentation and site enhancements to support bibliographic cross-references and richer MkDocs output.
Changes:
- Add
SSVImodel with calibration helpers (fit,fit_surface) and comprehensive unit tests. - Improve docs bibliography linking and styling (BibTeX entries + MkDocs config updates), and add an API docs page for SSVI.
- Miscellaneous robustness/typing tweaks across app APIs and options pricing.
Reviewed changes
Copilot reviewed 19 out of 20 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| quantflow/ta/paths.py | Adds typing annotation for tau list in Hurst exponent calculation. |
| quantflow/options/svi.py | Updates SVI docstring to link to bibliography entries. |
| quantflow/options/surface.py | Forces Black price sum to float before sigfig/Decimal conversion. |
| quantflow/options/ssvi.py | Introduces new SSVI model, analytics, and calibration routines. |
| quantflow_tests/test_ssvi.py | Adds tests covering SSVI shape, variance/IV, arbitrage checks, and calibration. |
| pyproject.toml | Bumps pandas + dev tooling minimum versions. |
| mkdocs.yml | Adds site description, nav entry for SSVI, enables md_in_html, and includes extra CSS. |
| docs/stylesheets/bibliography.css | Adds bibliography entry highlighting/styling. |
| docs/references.bib | Adds Gatheral SVI and Gatheral-Jacquier references. |
| docs/contributing.md | Tightens wording around responsibility for AI-assisted contributions. |
| docs/bibliography.md | Wrapes bibliography entries for styling and fragment targeting. |
| docs/bib2md.py | Changes bibliography generator output to emit wrapped entries. |
| docs/api/options/ssvi.md | Adds mkdocstrings page for quantflow.options.ssvi.SSVI. |
| app/utils/paths.py | Injects social OpenGraph/Twitter meta tags into MkDocs pages. |
| app/api/volatility.py | Ensures ttm_grid is JSON-friendly (float list). |
| app/api/cointegration.py | Suppresses ComplexWarning and coerces eigenvector to real values. |
| .github/instructions/release.instructions.md | Adds applyTo front matter. |
| .github/instructions/makefile.instructions.md | Adds applyTo front matter. |
| .github/copilot-instructions.md | Fixes documented test command and doc examples output path. |
Comments suppressed due to low confidence (2)
quantflow/options/ssvi.py:160
fit()usesnp.interp(0.0, k_arr, w_obs)and then runs least-squares without checking that inputs are non-empty, same-shape, and sorted byk.np.interprequires an increasing x-grid, and empty/mismatched inputs will currently producenaninitial guesses or broadcast errors.
k_arr = np.asarray(k, dtype=float)
iv_arr = np.asarray(iv, dtype=float)
w_obs = iv_arr**2 * ttm
atm_var = float(np.interp(0.0, k_arr, w_obs)) if k_arr.size else w_obs.mean()
x0 = [0.0, 1.0, max(atm_var, 1e-4)]
quantflow/options/ssvi.py:213
fit_surface()collects slices without validating/sorting each(k, iv)pair. This can break the ATM interpolation (np.interpexpects sortedk) and can yieldnaninitial thetas if a slice is empty.
data = []
thetas0 = []
for k, iv, ttm in slices:
k_arr = np.asarray(k, dtype=float)
iv_arr = np.asarray(iv, dtype=float)
w_obs = iv_arr**2 * ttm
data.append((k_arr, w_obs))
atm = float(np.interp(0.0, k_arr, w_obs)) if k_arr.size else w_obs.mean()
thetas0.append(max(atm, 1e-4))
| def iv( | ||
| self, | ||
| k: Annotated[ArrayLike, Doc("Log-moneyness log(K/F), scalar or array")], | ||
| ttm: Annotated[float, Doc("Time to maturity in years")], | ||
| ) -> np.ndarray: | ||
| r"""Implied volatility $\sigma(k) = \sqrt{w(k) / \tau}$. | ||
|
|
||
| Returns an array of the same shape as $k$. The SSVI total variance is | ||
| strictly positive for $|\rho| < 1$, so no clipping is required. | ||
| """ | ||
| return np.sqrt(self.total_variance(k) / ttm) |
| if suffix: | ||
| body = f"{body}, {suffix}" | ||
|
|
||
| return f"#### {key}\n\n{body}\n" | ||
| return f'<div class="bib-entry" markdown>\n\n#### {key}\n\n{body}\n\n</div>\n' | ||
|
|
|
|
||
| --- | ||
|
|
||
| <div class="bib-entry" markdown> |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #86 +/- ##
==========================================
+ Coverage 88.75% 88.86% +0.10%
==========================================
Files 84 85 +1
Lines 5142 5300 +158
==========================================
+ Hits 4564 4710 +146
- Misses 578 590 +12 ☔ 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 19 out of 20 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
quantflow/options/ssvi.py:328
fit_surface()doesn’t validate that each slice has non-emptyk/ivarrays of matching shape. Empty slices will yieldnaninitial thetas and mismatched shapes can broadcast, producing incorrect residual vectors.
for k, iv, ttm in slices:
k_arr = np.asarray(k, dtype=float)
iv_arr = np.asarray(iv, dtype=float)
w_obs = iv_arr**2 * ttm
data.append((k_arr, w_obs, float(ttm)))
| # discarding noise-level imaginary parts | ||
| warnings.simplefilter("ignore", np.exceptions.ComplexWarning) | ||
| johansen_result = coint_johansen(scaled, det_order=0, k_ar_diff=1) | ||
| deltas = np.real_if_close(johansen_result.evec[:, 0]).real / std.values |
| k_arr = np.asarray(k, dtype=float) | ||
| iv_arr = np.asarray(iv, dtype=float) | ||
| w_obs = iv_arr**2 * ttm |
No description provided.