Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ applyTo: '/**'

## Run Tests

* To run all tests use `make test` — runs all tests in the `tests/` directory using pytest
* To run all tests use `make tests` — runs all tests in the `tests/` directory using pytest
* To run a specific test file, use `uv run pytest tests/path/to/test_file.py`

## Docker
Expand Down Expand Up @@ -56,7 +56,7 @@ applyTo: '/**'
* Do not repeat concept definitions inline in tutorials or docstrings, link to the glossary instead using a relative markdown link (e.g. `[moneyness](../glossary.md#moneyness)`).
* Use relative links for all mkdocs page links (e.g. `[Option Pricing](../theory/option_pricing.md)`) — prefer relative over absolute URLs to keep links shorter and portable.
* Prefer mkdocstrings relative cross-references whenever the target is visible from the current scope: write `[label][.member]` (same class) or `[label][..Sibling]` (same module) instead of repeating the fully-qualified path. Use the full path only when the target lives in a different module than the current docstring.
* To rebuild doc examples run `uv run ./dev/build-examples` — runs all scripts in `docs/examples/` and writes their output to `docs/examples_output/`
* To rebuild doc examples run `uv run ./dev/build-examples` — runs all scripts in `docs/examples/` and writes their output to `docs/examples/output/`

## Pydantic models

Expand Down
4 changes: 4 additions & 0 deletions .github/instructions/makefile.instructions.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
---
applyTo: 'Makefile'
---

# Makefile Conventions

- Keep all targets sorted alphabetically.
Expand Down
4 changes: 4 additions & 0 deletions .github/instructions/release.instructions.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
---
applyTo: 'pyproject.toml,docs/release-notes.md,.github/workflows/release.yml,Makefile'
---

# Release Instructions

Releases are driven by `v*` git tags. Pushing a tag triggers
Expand Down
9 changes: 7 additions & 2 deletions app/api/cointegration.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import warnings
from functools import partial

import numpy as np
Expand Down Expand Up @@ -62,8 +63,12 @@ async def _cointegration(fmp: FMP, frequency: FMP.freq) -> CointegrationResponse
log_prices_3 = np.log(prices_3)
std = log_prices_3.std()
scaled = log_prices_3 / std
johansen_result = coint_johansen(scaled, det_order=0, k_ar_diff=1)
deltas = johansen_result.evec[:, 0] / std.values
with warnings.catch_warnings():
# statsmodels assigns complex eigenvalue statistics into real arrays,
# 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(johansen_result.evec[:, 0]) / std.values
deltas = deltas / np.linalg.norm(deltas)

residuals = log_prices_3.dot(deltas)
Expand Down
5 changes: 4 additions & 1 deletion app/api/volatility.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from quantflow.data.deribit import Deribit
from quantflow.data.yahoo import Yahoo
from quantflow.options.inputs import VolSurfaceInputs
from quantflow.options.ssvi import SSVI
from quantflow.options.surface import OptionInfo, VolSurfaceLoader
from quantflow.rates.cir import CIRCurve
from quantflow.rates.nelson_siegel import NelsonSiegelCurve
Expand Down Expand Up @@ -37,6 +38,7 @@ class ForwardCurveResponse(BaseModel):


class VolSurfaceResponse(BaseModel):
ssvi: SSVI = Field(description="SSVI model fitted to the volatility surface")
inputs: VolSurfaceInputs = Field(description="Volatility surface inputs")
options: list[OptionInfo] = Field(
description="List of option info with implied volatilities"
Expand Down Expand Up @@ -119,9 +121,10 @@ async def _volatility_surface(asset: str) -> VolSurfaceResponse:
options = [op.info() for op in surface.option_prices(converged=True)]

max_ttm = max(float(op.ttm) for op in options) if options else 1.0
ttm_grid = list(np.linspace(1 / 365, max_ttm, 50))
ttm_grid = [float(t) for t in np.linspace(1 / 365, max_ttm, 50)]

return VolSurfaceResponse(
ssvi=SSVI.fit_vol_surface(surface),
inputs=inputs,
options=options,
Comment on lines 123 to 129
quote_curve=_curve_response(surface.quote_curve, max_ttm),
Expand Down
38 changes: 37 additions & 1 deletion app/utils/paths.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from html import escape
from pathlib import Path
from typing import Any

Expand All @@ -8,7 +9,42 @@ def head_snippet(path: Path) -> str:
return (path / "assets" / "logos" / "head-snippet.html").read_text()


def social_meta(page: Any, config: Any) -> str:
site_name = escape(str(config.get("site_name", "")))
site_description = escape(str(config.get("site_description", "")))
site_url = str(config.get("site_url", "")).rstrip("/")
title = escape(f"{page.title} | {site_name}" if page.title else site_name)
page_url = escape(str(page.canonical_url))
image = escape(f"{site_url}/assets/logos/png/quantflow-github-social-1280x640.png")
image_alt = "QuantFlow logo and tagline"
return "\n".join(
(
'<meta property="og:type" content="website">',
f'<meta property="og:site_name" content="{site_name}">',
f'<meta property="og:title" content="{title}">',
f'<meta property="og:description" content="{site_description}">',
f'<meta property="og:url" content="{page_url}">',
f'<meta property="og:image" content="{image}">',
f'<meta property="og:image:secure_url" content="{image}">',
'<meta property="og:image:type" content="image/png">',
'<meta property="og:image:width" content="1280">',
'<meta property="og:image:height" content="640">',
f'<meta property="og:image:alt" content="{image_alt}">',
'<meta name="twitter:card" content="summary_large_image">',
f'<meta name="twitter:title" content="{title}">',
f'<meta name="twitter:description" content="{site_description}">',
f'<meta name="twitter:image" content="{image}">',
f'<meta name="twitter:image:alt" content="{image_alt}">',
)
)


def on_post_page(output: str, page: Any, config: Any) -> str:
"""Hook to inject custom HTML into the head of each page in mkdocs build."""
snippet = head_snippet(APP_PATH.parent / "docs")
snippet = "\n".join(
(
head_snippet(APP_PATH.parent / "docs"),
social_meta(page, config),
)
)
return output.replace("</head>", f"{snippet}</head>", 1)
5 changes: 5 additions & 0 deletions docs/api/options/ssvi.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# SSVI Volatility Surface

::: quantflow.options.ssvi.VarianceCurve

::: quantflow.options.ssvi.SSVI
2 changes: 1 addition & 1 deletion docs/bib2md.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ def format_entry(entry: dict[str, str]) -> str:
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'

Comment on lines 147 to 151

# ---------------------------------------------------------------------------
Expand Down
96 changes: 96 additions & 0 deletions docs/bibliography.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,82 +4,178 @@ The raw BibTeX source for all entries is available in [references.bib](reference

---

<div class="bib-entry" markdown>

#### bertoin

J. Bertoin. (1996) Lévy Processes, Cambridge University Press

</div>

<div class="bib-entry" markdown>

#### bns

O.E. Barndorff-Nielsen, N. Shephard. (2001) [Non-Gaussian OU based models and some of their uses in financial economics](https://drive.google.com/file/d/11TnGl2ER_-QzEu_h7ZtGGass2rF8_u1Y/view){target="_blank" rel="noopener"}, Journal of the Royal Statistical Society, 63(2)

</div>

<div class="bib-entry" markdown>

#### carr_madan

P. Carr, D. Madan. (1999) [Option valuation using the fast Fourier transform](http://faculty.baruch.cuny.edu/lwu/890/CarrMadan99.pdf){target="_blank" rel="noopener"}, Journal of Computational Finance, 3:463-520

</div>

<div class="bib-entry" markdown>

#### carr_wu

P. Carr, L. Wu. (2002) [Time-changed Lévy processes and option pricing](https://engineering.nyu.edu/sites/default/files/2019-03/Carr-time-changed-levy-processes-option-pricing.pdf){target="_blank" rel="noopener"}, Journal of Financial Economics, 7:113-141

</div>

<div class="bib-entry" markdown>

#### cgmy

Carr P., Geman H., Madan D.B., Yor M. (2003) [Stochastic Volatility for Lévy processes](https://engineering.nyu.edu/sites/default/files/2019-03/Carr-stochastic-volatility-levy-processes.pdf){target="_blank" rel="noopener"}, Mathematical Finance, 13(3)

</div>

<div class="bib-entry" markdown>

#### chourdakis

K. Chourdakis. (2004) [Option Pricing Using the Fractional FFT](https://citeseerx.ist.psu.edu/document?repid=rep1&type=pdf&doi=6bdf4696312d37427eda2740137650c09deacda7){target="_blank" rel="noopener"}, Journal of Computational Finance, 8:1-18

</div>

<div class="bib-entry" markdown>

#### cos

F. Fang, K. Oosterlee. (2008) [A Novel Pricing Method for European Options Based on Fourier-Cosine Series Expansions](https://mpra.ub.uni-muenchen.de/8914/4/MPRA_paper_8914.pdf){target="_blank" rel="noopener"}

</div>

<div class="bib-entry" markdown>

#### dsp

Unknown. (2017) [Doubly Stochastic Poisson Processes with Affine Intensities](https://editorialexpress.com/cgi-bin/conference/download.cgi?db_name=sbe35&paper_id=179){target="_blank" rel="noopener"}, internet

</div>

<div class="bib-entry" markdown>

#### durbin_koopman

J. Durbin, S. J. Koopman. (2012) Time Series Analysis by State Space Methods, Oxford University Press

</div>

<div class="bib-entry" markdown>

#### ekf

Wang X., He X., Zhao Y., Zuo Z. (2017) [Parameter Estimations of Heston Model Based on Consistent Extended Kalman Filter](https://www.sciencedirect.com/science/article/pii/S2405896317324758){target="_blank" rel="noopener"}, internet

</div>

<div class="bib-entry" markdown>

#### gamma-ou

P. Sabino, C. Petroni. (2021) [Gamma Related Ornstein-Uhlenbeck Processes and their Simulation](https://doi.org/10.1080/00949655.2020.1842408){target="_blank" rel="noopener"}, Journal of Statistical Computation and Simulation, 91(6)

</div>

<div class="bib-entry" markdown>

#### gatheral_jacquier

Jim Gatheral, Antoine Jacquier. (2014) [Arbitrage-free SVI volatility surfaces](https://doi.org/10.1080/14697688.2013.819986){target="_blank" rel="noopener"}, Quantitative Finance, 14(1):59-71

</div>

<div class="bib-entry" markdown>

#### gatheral_svi

Jim Gatheral. (2004) [A parsimonious arbitrage-free implied volatility parameterization with application to the valuation of volatility derivatives](https://faculty.baruch.cuny.edu/jgatheral/madrid2004.pdf){target="_blank" rel="noopener"}

</div>

<div class="bib-entry" markdown>

#### heston-calibration

Milan Mrázek, Jan Pospíšil. (2017) [Calibration and simulation of Heston model](https://doi.org/10.1515/math-2017-0058){target="_blank" rel="noopener"}, Open Mathematics, 15(1):679-704

</div>

<div class="bib-entry" markdown>

#### heston-simulation

Leif B.G. Andersen. (2008) [Efficient Simulation of the Heston Stochastic Volatility Model](https://papers.ssrn.com/sol3/papers.cfm?abstract_id=946405){target="_blank" rel="noopener"}, Journal of Computational Finance, 11(3)

</div>

<div class="bib-entry" markdown>

#### julier_uhlmann

S. J. Julier, J. K. Uhlmann. (1997) [New extension of the Kalman filter to nonlinear systems](https://doi.org/10.1117/12.280797){target="_blank" rel="noopener"}, Proceedings of SPIE, 3068:182-193

</div>

<div class="bib-entry" markdown>

#### kalman

R. E. Kalman. (1960) [A New Approach to Linear Filtering and Prediction Problems](https://doi.org/10.1115/1.3662552){target="_blank" rel="noopener"}, Journal of Basic Engineering, 82(1):35-45

</div>

<div class="bib-entry" markdown>

#### lee_option

R. W. Lee. (2004) [Option Pricing by Transform Methods: Extensions, Unification, and Error Control](https://www.math.uchicago.edu/~rl/dft.pdf){target="_blank" rel="noopener"}

</div>

<div class="bib-entry" markdown>

#### lewis

A. L. Lewis. (2001) [A Simple Option Formula for General Jump-Diffusion and other Exponential Lévy Processes](https://drive.google.com/file/d/1JiYfyOU7lUKHrMqTRPbBCdasctwYDc4Z/view){target="_blank" rel="noopener"}

</div>

<div class="bib-entry" markdown>

#### molnar

Peter Molnar. (2020) [Volatility modeling and forecasting: utilization of realized volatility, implied volatility and the highest and lowest price of the day](https://drive.google.com/file/d/1zCU1OZyrKQLpxaypPv9U5UPbReBDXcMf/view){target="_blank" rel="noopener"}, University of Economics in Prague

</div>

<div class="bib-entry" markdown>

#### saez

G. K. G. Saez. (2014) [Fourier Transform Methods for Option Pricing: An Application to extended Heston-type Models](https://www.uv.es/bfc/TFM2014/008-014.pdf){target="_blank" rel="noopener"}, Universidad del Pais Vasco

</div>

<div class="bib-entry" markdown>

#### ukf

Merwe. (2014) [The Unscented Kalman Filter for Nonlinear Estimation](https://groups.seas.harvard.edu/courses/cs281/papers/unscented.pdf){target="_blank" rel="noopener"}, internet

</div>
2 changes: 1 addition & 1 deletion docs/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Example scripts can be rebuilt with `make docs-examples`.

AI assisted contributions are welcome: using AI tools to draft, review, or improve documentation is perfectly fine.

However, you are the author of your PR. You must understand every change you submit, be able to explain it, and respond to review feedback yourself. Please do not submit unreviewed AI output: if you cannot vouch for the content, do not open the PR.
However, you are the author of your PR. You must understand every change you submit, be able to explain it, and respond to review feedback yourself. Please do not submit unreviewed AI output: if you cannot take responsibility for the content, do not open the PR.

## Code of Conduct

Expand Down
20 changes: 20 additions & 0 deletions docs/references.bib
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,26 @@ @article{gamma-ou
url={https://doi.org/10.1080/00949655.2020.1842408},
}

@article{gatheral_jacquier,
title={Arbitrage-free SVI volatility surfaces},
author={Jim Gatheral and Antoine Jacquier},
journal={Quantitative Finance},
year={2014},
volume={14},
number={1},
pages={59--71},
url={https://doi.org/10.1080/14697688.2013.819986},
}

@misc{gatheral_svi,
title={A parsimonious arbitrage-free implied volatility parameterization
with application to the valuation of volatility derivatives},
author={Jim Gatheral},
howpublished={Presentation at Global Derivatives \& Risk Management, Madrid},
year={2004},
url={https://faculty.baruch.cuny.edu/jgatheral/madrid2004.pdf},
}

@article{heston-calibration,
title={Calibration and simulation of Heston model},
author={Milan Mrázek and Jan Pospíšil},
Expand Down
17 changes: 17 additions & 0 deletions docs/stylesheets/bibliography.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/* Highlight the whole bibliography entry (title and body) when its
anchor is the URL fragment, e.g. /bibliography/#carr_madan */
.bib-entry {
display: flow-root;
padding: 0 0.6rem;
margin: 0 -0.6rem;
border-radius: 0.2rem;
border-bottom: 1px solid var(--md-default-fg-color--lightest);
}

.bib-entry:last-of-type {
border-bottom: none;
}

.bib-entry:has(:target) {
background-color: var(--md-accent-fg-color--transparent);
}
Loading
Loading