Skip to content

Commit

Permalink
Add surface plot
Browse files Browse the repository at this point in the history
  • Loading branch information
lsbardel committed Jul 15, 2023
1 parent 487932c commit 7cf547e
Show file tree
Hide file tree
Showing 15 changed files with 135 additions and 8 deletions.
4 changes: 2 additions & 2 deletions notebooks/_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
title: Quantflow library
author: Quantmind Team
copyright: "2023"
logo: quantflow-light.svg
logo: assets/quantflow-light.svg

# Force re-execution of notebooks on each build.
# See https://jupyterbook.org/content/execute.html
Expand All @@ -29,7 +29,7 @@ repository:
# Add GitHub buttons to your book
# See https://jupyterbook.org/customize/config.html#add-a-link-to-your-repository
html:
favicon: quantflow-logo.png
favicon: assets/quantflow-logo.png
home_page_in_navbar: false
use_edit_page_button: true
use_issues_button: true
Expand Down
Binary file added notebooks/_static/heston.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
1 change: 1 addition & 0 deletions notebooks/_toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ parts:
- file: examples/overview
sections:
- file: examples/poisson_sampling
- file: example/heston_vol_surface

- file: api/overview.rst
sections:
Expand Down
41 changes: 41 additions & 0 deletions notebooks/examples/heston_vol_surface.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
jupytext:
text_representation:
extension: .md
format_name: myst
format_version: 0.13
jupytext_version: 1.14.7
kernelspec:
display_name: Python 3 (ipykernel)
language: python
name: python3
---

# Heston Volatility Surface

```{code-cell} ipython3
from quantflow.sp.heston import HestonJ
from quantflow.options.pricer import OptionPricer
pricer = OptionPricer(model=HestonJ.create(
vol=0.5,
kappa=2,
rho=-0.3,
sigma=0.8,
theta=0.36,
jump_fraction=0.3,
jump_asymmetry=1.2
))
pricer
```

```{code-cell} ipython3
pricer.plot3d(max_moneyness_ttm=1.5, support=31).update_layout(
height=800,
title="Heston volatility surface",
)
```

```{code-cell} ipython3
```
5 changes: 4 additions & 1 deletion notebooks/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ kernelspec:

A library for quantitative analysis and pricing.

This documentation is organized into a few major sections.
<img src="./_static_/heston.gif" alt="Heston volatility surface" width="400">


This documentation is organized into a few major sections.
* **Theory** cover some important concept used throughout the library, for the curious reader
* **Stochastic Processes** cover all the stochastic models supported and their use
* **Applications** show case the real-world use cases
Expand Down
40 changes: 40 additions & 0 deletions quantflow/options/pricer.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,43 @@ def maturity(self, ttm: float, **kwargs: Any) -> MaturityPricer:
def call_price(self, ttm: float, moneyness: float) -> float:
"""Price a single call option"""
return self.maturity(ttm).call_price(moneyness)

def plot3d(
self,
max_moneyness_ttm: float = 1.0,
support: int = 51,
ttm: FloatArray | None = None,
**kwargs: Any,
) -> Any:
"""Plot the implied vols surface
It requires plotly to be installed
"""
if ttm is None:
ttm = np.arange(0.05, 1.0, 0.05)
moneyness_ttm = np.linspace(-max_moneyness_ttm, max_moneyness_ttm, support)
implied = np.zeros((len(ttm), len(moneyness_ttm)))
for i, t in enumerate(ttm):
maturity = self.maturity(t)
implied[i, :] = maturity.interp(moneyness_ttm * np.sqrt(t)).implied_vols
properties: dict = dict(
xaxis_title="moneyness_ttm",
yaxis_title="TTM",
colorscale="viridis",
scene=dict(
xaxis=dict(title="moneyness_ttm"),
yaxis=dict(title="TTM"),
zaxis=dict(title="implied_vol"),
),
scene_camera=dict(eye=dict(x=1.2, y=-1.8, z=0.3)),
contours=dict(
x=dict(show=True, color="white"), y=dict(show=True, color="white")
),
)
properties.update(kwargs)
return plot.plot3d(
x=moneyness_ttm,
y=ttm,
z=implied,
**properties,
)
17 changes: 12 additions & 5 deletions quantflow/sp/heston.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,18 @@ class Heston(StochasticProcess1D):

@classmethod
def create(
cls, vol: float = 0.5, kappa: float = 1, sigma: float = 0.8, rho: float = 0
cls,
vol: float = 0.5,
kappa: float = 1,
sigma: float = 0.8,
rho: float = 0,
theta: float | None = None,
) -> Heston:
rate = vol * vol
if theta is None:
theta = rate
return cls(
variance_process=CIR(
rate=vol * vol, kappa=kappa, sigma=sigma, theta=vol * vol
),
variance_process=CIR(rate=rate, kappa=kappa, sigma=sigma, theta=theta),
rho=rho,
)

Expand Down Expand Up @@ -86,6 +92,7 @@ def create(
kappa: float = 1,
sigma: float = 0.8,
rho: float = 0,
theta: float | None = None,
jump_intensity: float = 100, # number of jumps per year
jump_fraction: float = 0.1, # percentage of variance due to jumps
jump_asymmetry: float = 1,
Expand All @@ -104,7 +111,7 @@ def create(
rate=diffusion_variance,
kappa=kappa,
sigma=sigma,
theta=diffusion_variance,
theta=theta if theta is not None else diffusion_variance,
),
rho=rho,
jumps_up=CompoundPoissonProcess[Exponential](
Expand Down
18 changes: 18 additions & 0 deletions quantflow/utils/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from scipy.stats import norm

from .marginal import Marginal1D
from .types import FloatArray

PLOTLY_THEME = os.environ.get("PLOTLY_THEME", "plotly_dark")

Expand Down Expand Up @@ -162,3 +163,20 @@ def plot_vol_cross(
)
)
return fig.update_layout(xaxis_title="moneyness_ttm", yaxis_title=series)


def plot3d(
x: FloatArray,
y: FloatArray,
z: FloatArray,
contours: Any | None,
colorscale: str = "viridis",
**kwargs: Any
) -> Any:
check_plotly()
fig = go.Figure(
data=[go.Surface(x=x, y=y, z=z, contours=contours, colorscale=colorscale)]
)
if kwargs:
fig.update_layout(**kwargs)
return fig
17 changes: 17 additions & 0 deletions tests/test_options_pricer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import pytest

from quantflow.options.pricer import OptionPricer
from quantflow.sp.heston import HestonJ


@pytest.fixture
def pricer() -> OptionPricer[HestonJ]:
return OptionPricer(HestonJ.create(vol=0.5, kappa=1, sigma=0.8, rho=0))


def test_plot_surface(pricer: OptionPricer):
fig = pricer.plot3d()
surface = fig.data[0]
assert surface.x is not None
assert surface.y is not None
assert surface.z is not None

0 comments on commit 7cf547e

Please sign in to comment.