Skip to content
Open
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
2 changes: 1 addition & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ applyTo: '/**'
* Math in documentation and docstrings: always use `\begin{equation}...\end{equation}` for any formula or equation. Use `$...$` only for brief inline references to variables (e.g. $F$, $K$). Do not use `$$...$$`, `` `...` ``, or RST syntax (`.. math::`, `:math:`).
* Math notation convention: use $\Phi$ for the characteristic function and $\phi$ for the characteristic exponent, where $\Phi = e^{-\phi}$.
* Glossary entries in `docs/glossary.md` must be kept in alphabetical order.
* 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)`).
* 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)`).
* 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/`

Expand Down
35 changes: 22 additions & 13 deletions app/volatility_surface.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import marimo

__generated_with = "0.22.0"
__generated_with = "0.23.5"
app = marimo.App(width="medium")


Expand Down Expand Up @@ -28,6 +28,8 @@ def _(mo):
async with Deribit() as cli:
loader = await cli.volatility_surface_loader("eth", exclude_open_interest=0)

# calibrate discount curve for the quoting asset (usd)
loader.calibrate_curves(quote_curve=NelsonSiegel)
# build the volatility surface
surface = loader.surface()
# calculate black implied volatilities
Expand All @@ -54,6 +56,7 @@ def _(mo):
async def _(asset, inverse, mo):
import pandas as pd
from quantflow.data.deribit import Deribit
from quantflow.rates.nelson_siegel import NelsonSiegel

async with Deribit() as cli:
loader = await cli.volatility_surface_loader(
Expand All @@ -62,6 +65,7 @@ async def _(asset, inverse, mo):
use_perp=not inverse.value
)

loader.calibrate_curves(quote_curve=NelsonSiegel)
# build the volatility surface
surface = loader.surface()
# calculate black implied volatilities
Expand All @@ -81,21 +85,13 @@ def int_or_none(v):
label="Maturity"
)
maturity_dropdown
return int_or_none, maturity_dropdown, pd, surface
return int_or_none, maturity_dropdown, surface


@app.cell
def _(int_or_none, maturity_dropdown, surface):
index = int_or_none(maturity_dropdown.value)
surface.plot3d(index=index)
return (index,)


@app.cell
def _(index, pd, surface):
# display inputs - only options with converged implied volatility
surface_inputs = surface.inputs(converged=True, index=index)
pd.DataFrame([i.model_dump() for i in surface_inputs.inputs])
return


Expand All @@ -107,10 +103,23 @@ def _(surface):


@app.cell
def _(ts):
from quantflow.utils import plot
def _(surface, ts):
import math
ttm_max = 0.1*math.ceil(10*surface.maturities[-1].ttm(surface.ref_date))
fig = surface.quote_curve.plot(ttm_max=ttm_max)
fig.add_scatter(x=ts["ttm"], y=ts["rate"], mode="markers", name="Cross Sections", marker=dict(size=8, color="orange"))
fig
return


@app.cell
def _(surface):
surface.asset_curve.plot(ttm_max=2)
return

plot.plot_lines(ts, x="ttm", y="rate_percent")

@app.cell
def _():
return


Expand Down
2 changes: 1 addition & 1 deletion docs/api/data/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pip install quantflow[data]
| [Financial Modeling Prep](fmp.md) | Equity prices, company profiles, and sector data |
| [FRED](fred.md) | US macroeconomic time series from the St. Louis Fed |
| [Federal Reserve](fed.md) | Federal Reserve H.15 selected interest rate data |
| [Yahoo](yahoo.md) | Equity option chains from Yahoo Finance |
| [Yahoo](yahoo.md) | Equity prices and option chains from Yahoo Finance |

## Usage

Expand Down
4 changes: 2 additions & 2 deletions docs/api/options/black.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
Here we define the [log strike](../../glossary.md#log-strike) `k` as

\begin{equation}
k = \log{\frac{K}{F}}
k = \log{\frac{K}{F_\tau}}
\end{equation}

where $K$ is the strike price and $F$ is the forward price of the underlying asset.
where $K$ is the strike price and $F_\tau$ is the forward price of the underlying asset at time to maturity $\tau$.


::: quantflow.options.bs.black_price
Expand Down
2 changes: 2 additions & 0 deletions docs/api/options/calibration.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Vol Model Calibration

::: quantflow.options.calibration.base.ResidualKind

::: quantflow.options.calibration.base.OptionEntry

::: quantflow.options.calibration.base.VolModelCalibration
Expand Down
6 changes: 6 additions & 0 deletions docs/api/options/parity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Put-Call Parity


::: quantflow.options.parity.PutCallParity

::: quantflow.options.parity.PutCallParities
2 changes: 0 additions & 2 deletions docs/api/options/vol_surface.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@

::: quantflow.options.surface.FwdPrice

::: quantflow.options.surface.ImpliedFwdPrice

::: quantflow.options.surface.Strike

::: quantflow.options.surface.OptionArrays
Expand Down
14 changes: 14 additions & 0 deletions docs/api/rates/index.md
Original file line number Diff line number Diff line change
@@ -1 +1,15 @@
# Interest Rates

The `quantflow.rates` module provides primitives for interest rate modelling: flat rates, yield curves, and curve fitting.

The central concept is the [discount factor](../../glossary.md#discount-factor) $D_\tau$, the present value of one unit of currency paid at time $\tau$. Every class in this module exposes a `discount_factor` method that computes $D_\tau$ from the configured rate or curve.

**[Rate](interest_rate.md)** represents a spot or forward interest rate with a chosen compounding frequency (continuous by default) and day count convention. It supports continuous and periodic compounding and can be bootstrapped directly from a spot/forward pair.

**[YieldCurve](yield_curve.md)** is the abstract base for term-structure models. It defines the interface via `discount_factor` and `instantaneous_forward_rate`, with the two quantities linked by

\begin{equation}
f(\tau) = -\frac{\partial \ln D_\tau}{\partial \tau}
\end{equation}

**[NelsonSiegel](nelson_siegel.md)** is a concrete `YieldCurve` implementation that fits a smooth parametric curve to observed zero-coupon rates using the Nelson-Siegel functional form.
3 changes: 3 additions & 0 deletions docs/api/rates/nelson_siegel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Nelson Siegel Curve

::: quantflow.rates.nelson_siegel.NelsonSiegel
2 changes: 0 additions & 2 deletions docs/api/rates/yield_curve.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,3 @@


::: quantflow.rates.yield_curve.YieldCurve

::: quantflow.rates.nelson_siegel.NelsonSiegel
32 changes: 32 additions & 0 deletions docs/examples/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Interactive Examples

Interactive notebooks for exploring quantflow tools and models.

Each example is a [marimo](https://marimo.io) notebook served as a live application.
You can run the code, adjust parameters, and see results update in real time.

!!! warning "Work in progress"
These notebooks are not always stable and may fail to load or produce unexpected results.
We are actively working on improving their reliability.
If you have experience with marimo and would like to help, contributions are very welcome via [GitHub](https://github.com/quantmind/quantflow).

## Stochastic Processes

| Example | Description |
|---|---|
| [Gaussian Sampling](gaussian-sampling) | Sample the Gaussian Ornstein-Uhlenbeck (Vasicek) process for different mean-reversion speeds and path counts |
| [Poisson Sampling](poisson-sampling) | Compare Monte Carlo simulation of the Poisson process against the analytical PDF |
| [Double Exponential Sampling](double-exponential-sampling) | Explore the Asymmetric Laplace distribution with adjustable asymmetry parameter |

## Time Series Analysis

| Example | Description |
|---|---|
| [Hurst Exponent](hurst) | Estimate the Hurst exponent to classify a time series as trending, mean-reverting, or random |
| [Supersmoother](supersmoother) | Apply the Supersmoother and EWMA filters to financial time series |

## Volatility and Options

| Example | Description |
|---|---|
| [Volatility Surface](volatility-surface) | Build and visualise an implied volatility surface from live Deribit ETH/USD options data |
42 changes: 35 additions & 7 deletions docs/glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,19 @@ of a real-valued random variable $x$ is the function given by

where ${\mathbb P}_x$ is the distrubution measure of $x$.

## Discount Factor

The discount factor $D\left(\tau\right)$ is the present value of one unit of currency paid at [time to maturity](#time-to-maturity-ttm) $\tau$.
It is equal to the price of a zero-coupon bond maturing at $\tau$: a contract that pays exactly 1 at maturity with no intermediate cashflows.

\begin{equation}
D\left(\tau\right) = e^{-r \tau}
\end{equation}

where $r$ is the continuously compounded risk-free rate.

Under a zero interest rate assumption, $D\left(\tau\right) = 1\ \ \ \forall\ \tau$.

## Feller Condition

The Feller condition is a parameter constraint on a square-root diffusion process
Expand Down Expand Up @@ -71,6 +84,16 @@ condition holds. The
`feller_enforce` flag (default `True`) that imposes this as a hard inequality constraint
during optimisation.

## Forwards

The forward price $F$ of an asset at maturity $\tau$ is the price agreed upon today for delivery of the asset at time $\tau$. It is given by the ratio of two discount factors: one for the asset $D_a(\tau)$ and one for the quote $D_q(\tau)$.

\begin{equation}
F(\tau) = S \cdot \frac{D_a(\tau)}{D_q(\tau)}
\end{equation}

See [Forwards and Discount Factors](theory/forwards.md) for more details.

## Forward Space

Forward space is the unit-free convention in which option prices are
Expand Down Expand Up @@ -172,21 +195,26 @@ The [probability density function](https://en.wikipedia.org/wiki/Probability_den
## Put-Call Parity

Put-call parity is a no-arbitrage relationship between the prices of European call
and put options with the same strike $K$ and maturity. Denoting forward-space prices
$c = C/F$ and $p = P/F$ (see [Black Pricing](api/options/black.md)), the relationship
reads:
and put options with the same strike $K$ and time to maturity.

\begin{equation}
c - p = 1 - \frac{K}{F} = 1 - e^k
C - P = D_q \left(F - K\right)
\end{equation}

where $k$ is the [log-strike](#log-strike).
In quoting currency terms, multiplying through by $F$:
where $D_q$ is the [discount factor](#discount-factor) of the quoting asset (generally a currency)
at maturity and $F$ is the forward price
of the underlying asset at maturity.

Denoting forward-space prices
$c = C/(D_q\ F)$ and $p = P/(D_q\ F)$ (see [Black Pricing](api/options/black.md)), the relationship
reads:

\begin{equation}
C - P = F - K
c - p = 1 - \frac{K}{F} = 1 - e^k
\end{equation}

where $k$ is the [log-strike](#log-strike).

## Time To Maturity (TTM)

Time to maturity is the time remaining until an option or forward contract expires,
Expand Down
68 changes: 68 additions & 0 deletions docs/theory/forwards.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Forwards and Discount Factors

## Discount Factors

A [discount factor](../glossary.md#discount-factor) $D(\tau)$ is the present value of
one unit of currency delivered at time $\tau$.

In quantflow, discount factors are provided by a
[YieldCurve][quantflow.rates.yield_curve.YieldCurve]. Different implementations capture
different term structures: a flat zero-rate curve, a fitted
[Nelson-Siegel][quantflow.rates.nelson_siegel.NelsonSiegel] curve, or any custom term
structure.

## Forward Price

The forward price of an asset at maturity $\tau$ is defined under the assumption that
two discount factors are available: one for the asset $D_a(\tau)$ and one for the
quote $D_q(\tau)$.

\begin{equation}
F(\tau) = S \cdot \frac{D_a(\tau)}{D_q(\tau)}
\end{equation}

where $S$ is the current spot price.

For an asset that pays no dividends and has no carry costs, the discount factor
for the asset is constant and equal to one. In this case
the forward price is simply given by the discount factor for the quote:

\begin{equation}
F(\tau) = \frac{S}{D_q(\tau)}
\end{equation}

When the quote is cash, the forward price is the spot price compounded at the risk-free rate,
which means that forward prices are typically higher than the spot price.

## Put-Call Parity

The Put call parity is a fundamental relationship between the prices of European call and put options with the same strike and maturity.
It states that the difference between the call price $C$ and the put price $P$ is equal to the discounted difference between the forward price $F$ and the strike price $K$:

\begin{equation}
C - P = S \cdot D_a - D_q \cdot K
\end{equation}

Dividing by the Spot price

\begin{equation}
\frac{C - P}{S} = D_a - \frac{D_q}{S} \cdot K
\end{equation}

This is linear in $K$, with slope $-D_q / S$ and intercept $D_a$.

Fitting a linear regression across multiple strikes at the same maturity therefore
identifies both discount factors simultaneously: $D_q$ from the slope and $D_a$ from
the intercept.

### Inverse Options

For inverse options, prices are quoted in units of the underlying asset, which acts as
the quote. The relevant discount factor is therefore $D_a$. Put-call parity becomes:

\begin{equation}
c - p = D_a \left(1 - \frac{K}{F}\right) = D_a - \frac{D_q}{S} \cdot K
\end{equation}

This is again linear in $K$, with intercept $D_a$ and slope $-D_q / S$, exactly the same as for regular options.
The same regression therefore identifies both discount factors as before.
19 changes: 13 additions & 6 deletions docs/theory/option_pricing.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,31 @@
# Option Pricing

We use characteristic function inversion to price European call options on an underlying $S_t = S_0 e^{s_t}$, where $S_0$ is the spot price at time 0. We assume zero interest rates, so the forward equals the spot. The log-return $s_t = x_t - c_t$ is constructed from a driving process $x_t$ and a deterministic [convexity correction](convexity_correction.md) $c_t$ that enforces the martingale condition ${\mathbb E}[e^{s_t}] = 1$.
We use characteristic function inversion to price European call options on an underlying $S_t = F_t e^{s_t}$, where $F_t$ is the forward price at time $t$.
The log-return $s_t = x_t - c_t$ is constructed from a driving process $x_t$ and a deterministic [convexity correction](convexity_correction.md) $c_t$
that enforces the martingale condition ${\mathbb E}[e^{s_t}] = 1$.

## Call Option

The price $C$ of a call option with strike $K$ is defined as
The price $C$ of a call option with expiry $\tau$ and strike $K$ is defined as

\begin{equation}
\begin{aligned}
C &= S_0 c_k \\
k &= \ln\frac{K}{S_0} \\
C &= D_\tau F_\tau c_k \\
k &= \ln\frac{K}{F_\tau} \\
c_k &= {\mathbb E}\left[\left(e^{s_t} - e^k\right)^+\right]
= \int_{-\infty}^\infty \left(e^s - e^k\right)^+ f_{s_t}(s)\, ds
\end{aligned}
\label{call-price}
\end{equation}

$k$ is the [log-strike](../glossary.md#log-strike) and $f_{s_t}$ is the probability density function of $s_t$. The call price is the discounted expected payoff under the risk-neutral measure, which simplifies to the undiscounted expected payoff when interest rates are zero.
$k$ is the [log-strike](../glossary.md#log-strike) with respect to the forward $F_\tau$ and $f_{s_t}$ is the probability density function of $s_t$.
$D_\tau$ is the [discount factor](../glossary#discount-factor) to time $\tau$, which is 1 under a zero interest rate assumption.

All three methods share this starting point. They all express $c_k$ via the characteristic function $\Phi_{s_t}$, but differ in how the integration contour is chosen, how the payoff is handled, and the discretisation strategy.
## Fourier Inversion Methods

The key insight is that while the density $f_{s_t}$ may not have a closed form, its [characteristic function](../glossary.md#characteristic-function) $\Phi_{s_t}$ is available analytically for a wide class of stochastic processes. The integral in $\eqref{call-price}$ can therefore be evaluated by inverting $\Phi_{s_t}$ numerically.

Quantflow implements three Fourier inversion approaches: [Carr and Madan (1999)](../bibliography.md#carr_madan), [Lewis (2001)](../bibliography.md#lewis), and the [COS method](../bibliography.md#cos) (Fang and Oosterlee, 2008). All three share the same starting point and express $c_k$ via $\Phi_{s_t}$, but differ in how the integration contour is chosen, how the payoff is handled, and the discretisation strategy.

## Carr & Madan

Expand Down
Loading
Loading