Skip to content

Commit

Permalink
Fix docs (#27)
Browse files Browse the repository at this point in the history
  • Loading branch information
lsbardel authored Jan 14, 2025
1 parent fd81b94 commit 4b3c8ab
Show file tree
Hide file tree
Showing 12 changed files with 163 additions and 52 deletions.
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"python.defaultInterpreterPath": ".venv/bin/python"
}
7 changes: 7 additions & 0 deletions notebooks/api/utils/distributions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ Distributions

.. module:: quantflow.utils.distributions

.. autoclass:: Distribution1D
:members:
:member-order: groupwise
:autosummary:
:autosummary-nosignatures:


.. autoclass:: Exponential
:members:
:member-order: groupwise
Expand Down
39 changes: 28 additions & 11 deletions notebooks/applications/volatility_surface.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ jupytext:
format_version: 0.13
jupytext_version: 1.16.6
kernelspec:
display_name: Python 3 (ipykernel)
display_name: .venv
language: python
name: python3
---

# Volatility Surface

In this notebook we illustrate the use of the Volatility Surface tool in the library. We use [deribit](https://docs.deribit.com/) options on BTCUSD as example.
In this notebook we illustrate the use of the Volatility Surface tool in the library. We use [deribit](https://docs.deribit.com/) options on ETHUSD as example.

First thing, fetch the data

Expand All @@ -28,7 +28,7 @@ Once we have loaded the data, we create the surface and display the term-structu

```{code-cell} ipython3
vs = loader.surface()
vs.maturities = vs.maturities[1:]
vs.maturities = vs.maturities
vs.term_structure()
```

Expand Down Expand Up @@ -58,7 +58,22 @@ df
The plot function is enabled only if [plotly](https://plotly.com/python/) is installed

```{code-cell} ipython3
vs.plot().update_layout(height=500, title="BTC Volatility Surface")
from plotly.subplots import make_subplots
# consider 6 expiries
vs6 = vs.trim(6)
titles = []
for row in range(2):
for col in range(3):
index = row * 3 + col
titles.append(f"Expiry {vs6.maturities[index].maturity}")
fig = make_subplots(rows=2, cols=3, subplot_titles=titles).update_layout(height=600, title="ETH Volatility Surface")
for row in range(2):
for col in range(3):
index = row * 3 + col
vs6.plot(index=index, fig=fig, showlegend=False, fig_params=dict(row=row+1, col=col+1))
fig
```

The `moneyness_ttm` is defined as
Expand All @@ -70,24 +85,26 @@ The `moneyness_ttm` is defined as
where $T$ is the time-to-maturity.

```{code-cell} ipython3
vs.plot3d().update_layout(height=800, title="BTC Volatility Surface", scene_camera=dict(eye=dict(x=1, y=-2, z=1)))
vs6.plot3d().update_layout(height=800, title="ETH Volatility Surface", scene_camera=dict(eye=dict(x=1, y=-2, z=1)))
```

## Model Calibration

We can now use the Vol Surface to calibrate the Heston stochastic volatility model.

```{code-cell} ipython3
from quantflow.options.calibration import HestonCalibration, OptionPricer
from quantflow.sp.heston import Heston
from quantflow.options.calibration import HestonJCalibration, OptionPricer
from quantflow.utils.distributions import DoubleExponential
from quantflow.sp.heston import HestonJ
pricer = OptionPricer(Heston.create(vol=0.5))
cal = HestonCalibration(pricer=pricer, vol_surface=vs, moneyness_weight=-0)
model = HestonJ.create(DoubleExponential, vol=0.8, sigma=1.5, kappa=0.5, rho=0.1, jump_intensity=50, jump_fraction=0.3)
pricer = OptionPricer(model=model)
cal = HestonJCalibration(pricer=pricer, vol_surface=vs6, moneyness_weight=-0)
len(cal.options)
```

```{code-cell} ipython3
cal.model
cal.model.model_dump()
```

```{code-cell} ipython3
Expand All @@ -99,7 +116,7 @@ pricer.model
```

```{code-cell} ipython3
cal.plot(index=6, max_moneyness_ttm=1)
cal.plot(index=5, max_moneyness_ttm=1)
```

##
Expand Down
8 changes: 4 additions & 4 deletions notebooks/models/poisson.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,16 @@ The most common process is the Poisson process.

## Poisson Process

The Poisson Process $N_t$ with intensity parameter $\lambda > 0$ is a Lévy process with values in $N$ such that each $N_t$ has a [Poisson distribution](https://en.wikipedia.org/wiki/Poisson_distribution) with parameter $\lambda t$, that is
The Poisson Process $n_t$ with intensity parameter $\lambda > 0$ is a Lévy process with values in ${\mathbb N}$ such that each $n_t$ has a [Poisson distribution](https://en.wikipedia.org/wiki/Poisson_distribution) with parameter $\lambda t$, that is

\begin{equation}
P\left(N_t=n\right) = \frac{\left(\lambda t\right)^n}{n!}e^{-\lambda t}
{\mathbb P}_{n_t}\left(n_t=n\right) = \frac{\left(\lambda t\right)^n}{n!}e^{-\lambda t}
\end{equation}

The characteristic exponent is given by
The [](characteristic-exponent) is given by

\begin{equation}
\phi_{N_t, u} = t \lambda \left(1 - e^{iu}\right)
\phi_{n_t, u} = t \lambda \left(1 - e^{iu}\right)
\end{equation}

```{code-cell} ipython3
Expand Down
2 changes: 1 addition & 1 deletion notebooks/models/weiner.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ In this document, we use the term Weiner process $w_t$ to indicate a Brownian mo
1. $w_t$ is Lévy process
2. $d w_t = w_{t+dt}-w_t \sim N\left(0, \sigma dt\right)$ where $N$ is the normal distribution

The characteristic exponent of $w$ is
The [](characteristic-exponent) of $w$ is
\begin{equation}
\phi_{w, u} = \frac{\sigma^2 u^2}{2}
\end{equation}
Expand Down
22 changes: 16 additions & 6 deletions notebooks/reference/glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,30 @@ kernelspec:

## Characteristic Function

The characteristic function of a random variable $X$ is the Fourier transform of $f_X$, where $f_X$ is the probability density function
of $X$
The [characteristic function](../theory/characteristic.md) of a random variable $x$ is the Fourier transform of ${\mathbb P}_x$,
where ${\mathbb P}_x$ is the distrubution measure of $x$.

\begin{equation}
\Phi_{X,u} = {\mathbb E}\left[e^{i u X_t}\right] = \int e^{i u x} f_X\left(x\right) dx
\Phi_{x,u} = {\mathbb E}\left[e^{i u x}\right] = \int e^{i u s} {\mathbb P}_x\left(d s\right)
\end{equation}

If $x$ is a continuous random variable, than the characteristic function is the Fourier transform of the PDF $f_x$.

\begin{equation}
\Phi_{x,u} = {\mathbb E}\left[e^{i u x}\right] = \int e^{i u s} f_x\left(s\right) ds
\end{equation}


## Cumulative Distribution Function (CDF)

The cumulative distribution function (CDF), or just distribution function,
of a real-valued random variable $X$ is the function given by
of a real-valued random variable $x$ is the function given by
\begin{equation}
F_X(x) = P(X \leq x)
F_x(s) = {\mathbb P}_x(x \leq s)
\end{equation}

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

## Hurst Exponent

The Hurst exponent is a measure of the long-term memory of time series. The Hurst exponent is a measure of the relative tendency of a time series either to regress strongly to the mean or to cluster in a direction.
Expand Down Expand Up @@ -66,5 +76,5 @@ The [probability density function](https://en.wikipedia.org/wiki/Probability_den
(PDF), or density, of a continuous random variable, is a function that describes the relative likelihood for this random variable to take on a given value. It is related to the CDF by the formula

\begin{equation}
F_X(x) = \int_{-\infty}^x f_X(s) ds
F_x(x) = \int_{-\infty}^x f_x(s) ds
\end{equation}
26 changes: 20 additions & 6 deletions notebooks/theory/characteristic.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@ kernelspec:

# Characteristic Function

The library makes heavy use of characteristic function concept and therefore, it is useful to familiarize with it.
The library makes heavy use of [characteristic function](https://en.wikipedia.org/wiki/Characteristic_function_(probability_theory))
concept and therefore, it is useful to familiarize with it.

## Definition

The characteristic function of a random variable $x$ is the Fourier (inverse) transform of $P^x$, where $P^x$ is the distrubution measure of $x$
The characteristic function of a random variable $x$ is the Fourier (inverse) transform of ${\mathbb P}_x$, where ${\mathbb P}_x$ is the distrubution measure of $x$
\begin{equation}
\Phi_{x,u} = {\mathbb E}\left[e^{i u x_t}\right] = \int e^{i u x} P^x\left(dx\right)
\Phi_{x,u} = {\mathbb E}\left[e^{i u x}\right] = \int e^{i u s} {\mathbb P}_x\left(ds\right)
\end{equation}

## Properties
Expand All @@ -30,6 +31,7 @@ The characteristic function of a random variable $x$ is the Fourier (inverse) tr
* it is continuous
* characteristic function of a symmetric random variable is real-valued and even
* moments of $x$ are given by

\begin{equation}
{\mathbb E}\left[x^n\right] = i^{-n} \left.\frac{\Phi_{x, u}}{d u}\right|_{u=0}
\end{equation}
Expand All @@ -45,10 +47,13 @@ The characteristic function is a great tool for working with linear combination
\end{equation}

* which means, if $x$ and $y$ are independent, the characteristic function of $x+y$ is the product

\begin{equation}
\Phi_{x+x,u} = \Phi_{x,u}\Phi_{y,u}
\end{equation}

* The characteristic function of $ax+b$ is

\begin{equation}
\Phi_{ax+b,u} = e^{iub}\Phi_{x,au}
\end{equation}
Expand All @@ -62,18 +67,27 @@ There is a one-to-one correspondence between cumulative distribution functions a
The inversion formula for these distributions is given by

\begin{equation}
{\mathbb P}\left(x\right) = \frac{1}{2\pi}\int_{-\infty}^\infty e^{-iuk}\Phi_{x, u} du
{\mathbb P}_x\left(x=s\right) = \frac{1}{2\pi}\int_{-\infty}^\infty e^{-ius}\Phi_{s, u} du
\end{equation}

### Discrete distributions

In these distributions, the random variable $x$ takes integer values. For example, the Poisson distribution is discrete.
In these distributions, the random variable $x$ takes integer values $k$. For example, the Poisson distribution is discrete.
The inversion formula for these distributions is given by

\begin{equation}
{\mathbb P}\left(x=k\right) = \frac{1}{2\pi}\int_{-\pi}^\pi e^{-iuk}\Phi_{x, u} du
{\mathbb P}_x\left(x=k\right) = \frac{1}{2\pi}\int_{-\pi}^\pi e^{-iuk}\Phi_{k, u} du
\end{equation}

```{code-cell}
```

(characteristic-exponent)=
## Characteristic Exponent

The characteristic exponent $\phi_{x,u}$ is defined as

\begin{equation}
\Phi_{x,u} = e^{-\phi_{x,u}}
\end{equation}
5 changes: 3 additions & 2 deletions notebooks/theory/levy.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@ The independence and stationarity of the increments of the Lévy process imply t
\Phi_{x_t, u} = {\mathbb E}\left[e^{i u x_t}\right] = e^{-\phi_{x_t, u}} = e^{-t \phi_{x_1,u}}
\end{equation}

where the **characteristic exponent** $\phi_{x_1,u}$ is given by the [Lévy–Khintchine formula](https://en.wikipedia.org/wiki/L%C3%A9vy_process).
where the [](characteristic-exponent) $\phi_{x_1,u}$ is given by the [Lévy–Khintchine formula](https://en.wikipedia.org/wiki/L%C3%A9vy_process).

There are several Lévy processes in the literature, including, importantly, the [Poisson process](../models/poisson.md), the compound Poisson process, and the Brownian motion.
There are several Lévy processes in the literature, including, the [Poisson process](../models/poisson.md), the compound Poisson process
and the [Brownian motion](../models/weiner.md).

+++

Expand Down
29 changes: 25 additions & 4 deletions quantflow/options/calibration.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,10 +253,31 @@ def get_bounds(self) -> Sequence[Bounds] | None:
vol_range = self.implied_vol_range()
vol_lb = 0.5 * vol_range.lb[0]
vol_ub = 1.5 * vol_range.ub[0]
return Bounds(
[vol_lb * vol_lb, vol_lb * vol_lb, 0.0, 0.0, -0.9],
[vol_ub * vol_ub, vol_ub * vol_ub, np.inf, np.inf, 0.0],
)
lower = [
(0.5 * vol_lb) ** 2, # rate
(0.5 * vol_lb) ** 2, # theta
0.001, # kappa - mean reversion speed
0.001, # sigma - vol of vol
-0.9, # correlation
1.0, # jump intensity
(0.01 * vol_lb) ** 2, # jump variance
]
upper = [
(1.5 * vol_ub) ** 2, # rate
(1.5 * vol_ub) ** 2, # theta
np.inf, # kappa
np.inf, # sigma
0.0, # correlation
np.inf, # jump intensity
(0.5 * vol_ub) ** 2, # jump variance
]
try:
self.model.jumps.jumps.asymmetry()
lower.append(-2.0) # jump asymmetry
upper.append(2.0)
except NotImplementedError:
pass
return Bounds(lower, upper)

def get_params(self) -> np.ndarray:
params = [
Expand Down
8 changes: 6 additions & 2 deletions quantflow/options/surface.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

import enum
import warnings
from dataclasses import dataclass, field
from dataclasses import dataclass, field, replace
from datetime import datetime, timedelta
from decimal import Decimal
from typing import Any, Generic, Iterator, NamedTuple, Protocol, TypeVar
from typing import Any, Generic, Iterator, NamedTuple, Protocol, Self, TypeVar

import numpy as np
import pandas as pd
Expand Down Expand Up @@ -430,6 +430,10 @@ def term_structure(self, frequency: float = 0) -> pd.DataFrame:
cross.info_dict(self.ref_date, self.spot) for cross in self.maturities
)

def trim(self, num_maturities: int) -> Self:
"""Create a new volatility surface with the last `num_maturities` maturities"""
return replace(self, maturities=self.maturities[-num_maturities:])

def option_prices(
self,
*,
Expand Down
28 changes: 24 additions & 4 deletions quantflow/utils/distributions.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,26 @@ def from_variance_and_asymmetry(cls, variance: float, asymmetry: float) -> Self:
raise NotImplementedError

def asymmetry(self) -> float:
"""Asymmetry of the distribution"""
"""Asymmetry of the distribution, 0 for symmetric
Implemented by distributions that have asymmetry
"""
raise NotImplementedError

def set_variance(self, variance: float) -> None:
"""Set the variance of the distribution"""
raise NotImplementedError

def set_asymmetry(self, asymmetry: float) -> None:
"""Set the asymmetry of the distribution"""
"""Set the asymmetry of the distribution
Implemented by distributions that have asymmetry
"""
raise NotImplementedError


class Exponential(Distribution1D):
r"""A :class:`.Marginal1D` for the `Exponential distribution`_
r"""A :class:`.Distribution1D` for the `Exponential distribution`_
The exponential distribution is a continuous probability distribution with PDF
given by
Expand Down Expand Up @@ -90,8 +96,21 @@ def cdf(self, x: FloatArrayLike) -> FloatArrayLike:


class Normal(Distribution1D):
r"""A :class:`.Distribution1D` for the `Normal distribution`_
The normal distribution is a continuous probability distribution with PDF
given by
.. math::
f(x) = \frac{e^{-\frac{\left(x - \mu\right)^2}{2\sigma^2}}}{\sqrt{2\pi\sigma^2}}
.. _Normal distribution: https://en.wikipedia.org/wiki/Normal_distribution
"""

mu: float = Field(default=0, description="mean")
r"""The mean :math:`\mu` of the normal distribution"""
sigma: float = Field(default=1, gt=0, description="standard deviation")
r"""The standard deviation :math:`\sigma` of the normal distribution"""

@classmethod
def from_variance_and_asymmetry(cls, variance: float, asymmetry: float) -> Self:
Expand Down Expand Up @@ -188,7 +207,8 @@ def characteristic(self, u: Vector) -> Vector:
r"""Characteristic function of the double exponential distribution
.. math::
\phi(u) = \frac{e^{i u \mu}}{1 - \sigma^2 u^2}
\phi(u) = \frac{e^{i u m}}{\left(1 + \frac{i u \kappa}{\lambda}\right)
\left(1 - \frac{i u}{\lambda \kappa}\right)}
"""
den = (1.0 + 1j * u * self.kappa / self.decay) * (
1.0 - 1j * u / (self.kappa * self.decay)
Expand Down
Loading

0 comments on commit 4b3c8ab

Please sign in to comment.