Skip to content

Commit 7927316

Browse files
author
lsbardel
committed
better plots
1 parent 33a3ebc commit 7927316

File tree

4 files changed

+63
-25
lines changed

4 files changed

+63
-25
lines changed

.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"python.defaultInterpreterPath": ".venv/bin/python"
3+
}

notebooks/applications/volatility_surface.md

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ jupytext:
66
format_version: 0.13
77
jupytext_version: 1.16.6
88
kernelspec:
9-
display_name: Python 3 (ipykernel)
9+
display_name: .venv
1010
language: python
1111
name: python3
1212
---
1313

1414
# Volatility Surface
1515

16-
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.
16+
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.
1717

1818
First thing, fetch the data
1919

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

2929
```{code-cell} ipython3
3030
vs = loader.surface()
31-
vs.maturities = vs.maturities[1:]
31+
vs.maturities = vs.maturities
3232
vs.term_structure()
3333
```
3434

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

6060
```{code-cell} ipython3
61-
vs.plot().update_layout(height=500, title="BTC Volatility Surface")
61+
from plotly.subplots import make_subplots
62+
63+
# consider 6 expiries
64+
vs6 = vs.trim(6)
65+
66+
titles = []
67+
for row in range(2):
68+
for col in range(3):
69+
index = row * 3 + col
70+
titles.append(f"Expiry {vs6.maturities[index].maturity}")
71+
fig = make_subplots(rows=2, cols=3, subplot_titles=titles).update_layout(height=600, title="ETH Volatility Surface")
72+
for row in range(2):
73+
for col in range(3):
74+
index = row * 3 + col
75+
vs6.plot(index=index, fig=fig, showlegend=False, fig_params=dict(row=row+1, col=col+1))
76+
fig
6277
```
6378

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

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

7691
## Model Calibration
7792

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

8095
```{code-cell} ipython3
81-
from quantflow.options.calibration import HestonCalibration, OptionPricer
82-
from quantflow.sp.heston import Heston
96+
from quantflow.options.calibration import HestonJCalibration, OptionPricer
97+
from quantflow.utils.distributions import DoubleExponential
98+
from quantflow.sp.heston import HestonJ
8399
84-
pricer = OptionPricer(Heston.create(vol=0.5))
85-
cal = HestonCalibration(pricer=pricer, vol_surface=vs, moneyness_weight=-0)
100+
model = HestonJ.create(DoubleExponential, vol=0.8, sigma=1.5, kappa=0.5, rho=0.1, jump_intensity=50, jump_fraction=0.3)
101+
pricer = OptionPricer(model=model)
102+
cal = HestonJCalibration(pricer=pricer, vol_surface=vs6, moneyness_weight=-0)
86103
len(cal.options)
87104
```
88105

89106
```{code-cell} ipython3
90-
cal.model
107+
cal.model.model_dump()
91108
```
92109

93110
```{code-cell} ipython3
@@ -99,7 +116,7 @@ pricer.model
99116
```
100117

101118
```{code-cell} ipython3
102-
cal.plot(index=6, max_moneyness_ttm=1)
119+
cal.plot(index=5, max_moneyness_ttm=1)
103120
```
104121

105122
##

quantflow/options/surface.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
import enum
44
import warnings
5-
from dataclasses import dataclass, field
5+
from dataclasses import dataclass, field, replace
66
from datetime import datetime, timedelta
77
from decimal import Decimal
8-
from typing import Any, Generic, Iterator, NamedTuple, Protocol, TypeVar
8+
from typing import Any, Generic, Iterator, NamedTuple, Protocol, Self, TypeVar
99

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

433+
def trim(self, num_maturities: int) -> Self:
434+
"""Create a new volatility surface with the last `num_maturities` maturities"""
435+
return replace(self, maturities=self.maturities[-num_maturities:])
436+
433437
def option_prices(
434438
self,
435439
*,

quantflow/utils/plot.py

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -95,34 +95,48 @@ def plot_characteristic(m: Marginal1D, n: int | None = None, **kwargs: Any) -> A
9595

9696

9797
def plot_vol_surface(
98-
data: Any,
98+
data: pd.DataFrame,
9999
*,
100100
model: pd.DataFrame | None = None,
101-
template: str = PLOTLY_THEME,
102101
marker_size: int = 10,
102+
x_series: str = "moneyness_ttm",
103103
series: str = "implied_vol",
104+
color_series: str = "side",
105+
fig: Any | None = None,
106+
fig_params: dict | None = None,
104107
**kwargs: Any
105108
) -> Any:
106109
check_plotly()
110+
# Define a color map for the categorical values
111+
color_map = {"bid": "blue", "ask": "red"}
112+
colors = data[color_series].map(color_map)
113+
fig_params = fig_params or {}
114+
fig_: go.Figure = fig or go.Figure()
107115
params = dict(
108-
x="moneyness_ttm",
109-
y=series,
110-
color="side",
111-
template=template,
116+
mode="markers",
117+
marker=dict(color=colors),
118+
**kwargs,
119+
)
120+
fig_.add_trace(
121+
go.Scatter(
122+
x=data[x_series],
123+
y=data[series],
124+
**params,
125+
),
126+
**fig_params,
112127
)
113-
params.update(kwargs)
114-
fig = px.scatter(data, **params)
115128
if model is not None:
116-
fig.add_trace(
129+
fig_.add_trace(
117130
go.Scatter(
118131
x=model["moneyness_ttm"],
119132
y=model[series],
120133
name="model",
121134
mode="lines",
122-
)
135+
),
136+
**fig_params,
123137
)
124-
fig.update_traces(marker_size=marker_size)
125-
return fig
138+
fig_.update_traces(marker_size=marker_size)
139+
return fig_
126140

127141

128142
def plot_vol_surface_3d(

0 commit comments

Comments
 (0)