Skip to content

Commit 5734d68

Browse files
authored
Fix SVJ model (#22)
1 parent 6a4ee9a commit 5734d68

21 files changed

+514
-158
lines changed

notebooks/_config.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ sphinx:
5656
}
5757
extra_extensions:
5858
- "sphinx.ext.autodoc"
59-
- "sphinx_autodoc_typehints"
6059
- "sphinx.ext.autosummary"
6160
- "sphinx.ext.intersphinx"
6261
- "sphinx_autosummary_accessors"

notebooks/_toc.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ parts:
3333
- file: examples/overview
3434
sections:
3535
- file: examples/gaussian_sampling
36+
- file: examples/exponential_sampling
3637
- file: examples/poisson_sampling
3738
- file: examples/heston_vol_surface
3839

notebooks/api/options/black.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ Black Pricing
66

77
.. autofunction:: black_price
88

9+
.. autofunction:: black_delta
10+
911
.. autofunction:: black_vega
1012

1113
.. autofunction:: implied_black_volatility

notebooks/api/utils/bins.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
===========
2+
Bins
3+
===========
4+
5+
.. module:: quantflow.utils.bins
6+
7+
.. autofunction:: pdf
8+
9+
.. autofunction:: event_density

notebooks/api/utils/distributions.rst

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
==============
2+
Distributions
3+
==============
4+
5+
.. module:: quantflow.utils.distributions
6+
7+
.. autoclass:: Exponential
8+
:members:
9+
:member-order: groupwise
10+
:autosummary:
11+
:autosummary-nosignatures:
12+
13+
14+
.. autoclass:: DoubleExponential
15+
:members:
16+
:member-order: groupwise
17+
:autosummary:
18+
:autosummary-nosignatures:
19+
20+
21+
.. autoclass:: Normal
22+
:members:
23+
:member-order: groupwise
24+
:autosummary:
25+
:autosummary-nosignatures:

notebooks/api/utils/index.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ Utils
88
:maxdepth: 1
99

1010
marginal1d
11+
distributions
12+
bins
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
---
2+
jupytext:
3+
text_representation:
4+
extension: .md
5+
format_name: myst
6+
format_version: 0.13
7+
jupytext_version: 1.16.6
8+
kernelspec:
9+
display_name: .venv
10+
language: python
11+
name: python3
12+
---
13+
14+
# Exponential Sampling
15+
16+
Here we sample the Asymmetric Laplace distribution. We will set the mean to 0 and the variance to 1 so that the distribution is fully determined by the asymmetric parameter $\kappa$.
17+
18+
```{code-cell} ipython3
19+
from quantflow.utils.distributions import DoubleExponential
20+
from quantflow.utils import bins
21+
import numpy as np
22+
import ipywidgets as widgets
23+
import plotly.graph_objects as go
24+
25+
def simulate():
26+
pr = DoubleExponential.from_moments(kappa=np.exp(asym.value))
27+
data = pr.sample(samples.value)
28+
pdf = bins.pdf(data, num_bins=50, symmetric=0)
29+
pdf["simulation"] = pdf["pdf"]
30+
pdf["analytical"] = pr.pdf(pdf.index)
31+
cha = pr.pdf_from_characteristic()
32+
return pdf, cha
33+
34+
def on_change(change):
35+
df, cha = simulate()
36+
fig.data[0].x = df.index
37+
fig.data[0].y = df["simulation"]
38+
fig.data[1].x = df.index
39+
fig.data[1].y = df["analytical"]
40+
fig.data[2].x = cha.x
41+
fig.data[2].y = cha.y
42+
43+
asym = widgets.FloatSlider(description="asymmetry (log of k)", min=-2, max=2)
44+
samples = widgets.IntSlider(description="paths", min=100, max=10000, step=100)
45+
asym.value = 0
46+
samples.value = 1000
47+
asym.observe(on_change)
48+
samples.observe(on_change)
49+
50+
df, cha = simulate()
51+
simulation = go.Bar(x=df.index, y=df["simulation"], name="simulation")
52+
analytical = go.Scatter(x=df.index, y=df["analytical"], name="analytical")
53+
cha = go.Scatter(x=cha.x, y=cha.y, name="from characteristic", mode="markers")
54+
fig = go.FigureWidget(data=[simulation, cha, analytical])
55+
56+
widgets.VBox([asym, samples, fig])
57+
```
58+
59+
```{code-cell} ipython3
60+
61+
```

notebooks/examples/heston_vol_surface.md

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,36 +6,46 @@ 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
# Heston Volatility Surface
1515

16-
```{code-cell}
16+
Here we study the Implied volatility surface of the Heston model. The Heston model is a stochastic volatility model that is widely used in the finance industry to price options.
17+
18+
```{code-cell} ipython3
1719
from quantflow.sp.heston import HestonJ
1820
from quantflow.options.pricer import OptionPricer
1921
20-
pricer = OptionPricer(model=HestonJ.create(
22+
pricer = OptionPricer(model=HestonJ.exponential(
2123
vol=0.5,
2224
kappa=2,
23-
rho=-0.3,
24-
sigma=0.8,
25-
theta=0.36,
26-
jump_fraction=0.3,
27-
jump_asymmetry=1.2
25+
rho=0.0,
26+
sigma=0.6,
27+
jump_fraction=0.5,
28+
jump_asymmetry=1.0
2829
))
2930
pricer
3031
```
3132

32-
```{code-cell}
33+
```{code-cell} ipython3
34+
fig = None
35+
for ttm in (0.1, 0.5, 1):
36+
fig = pricer.maturity(ttm).plot(fig=fig, name=f"ttm={ttm}")
37+
fig
38+
```
39+
40+
41+
42+
```{code-cell} ipython3
3343
pricer.plot3d(max_moneyness_ttm=1.5, support=31).update_layout(
3444
height=800,
3545
title="Heston volatility surface",
3646
)
3747
```
3848

39-
```{code-cell}
49+
```{code-cell} ipython3
4050
4151
```

notebooks/models/poisson.md

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ jupytext:
77
format_version: 0.13
88
jupytext_version: 1.16.6
99
kernelspec:
10-
display_name: Python 3 (ipykernel)
10+
display_name: .venv
1111
language: python
1212
name: python3
1313
---
1414

1515
# Poisson Processes
1616

17-
In this section, we look at the family of pure jump processes which are Lévy provcesses.
17+
In this section, we look at the family of pure jump processes which are Lévy processes.
1818
The most common process is the Poisson process.
1919

2020
## Poisson Process
@@ -31,48 +31,48 @@ The characteristic exponent is given by
3131
\phi_{N_t, u} = t \lambda \left(1 - e^{iu}\right)
3232
\end{equation}
3333

34-
```{code-cell}
34+
```{code-cell} ipython3
3535
from quantflow.sp.poisson import PoissonProcess
3636
pr = PoissonProcess(intensity=1)
3737
pr
3838
```
3939

40-
```{code-cell}
40+
```{code-cell} ipython3
4141
m = pr.marginal(0.1)
4242
import numpy as np
4343
cdf = m.cdf(np.arange(5))
4444
cdf
4545
```
4646

47-
```{code-cell}
47+
```{code-cell} ipython3
4848
n = 128*8
4949
m.cdf_from_characteristic(5, frequency_n=n).y
5050
```
5151

52-
```{code-cell}
52+
```{code-cell} ipython3
5353
cdf1 = m.cdf_from_characteristic(5, frequency_n=n).y
5454
cdf2 = m.cdf_from_characteristic(5, frequency_n=n, simpson_rule=False).y
5555
10000*np.max(np.abs(cdf-cdf1)), 10000*np.max(np.abs(cdf-cdf2))
5656
```
5757

5858
### Marginal
5959

60-
```{code-cell}
60+
```{code-cell} ipython3
6161
import numpy as np
6262
from quantflow.utils import plot
6363
6464
m = pr.marginal(1)
6565
plot.plot_marginal_pdf(m, frequency_n=128*8)
6666
```
6767

68-
```{code-cell}
68+
```{code-cell} ipython3
6969
from quantflow.utils.plot import plot_characteristic
7070
plot_characteristic(m)
7171
```
7272

7373
### Sampling Poisson
7474

75-
```{code-cell}
75+
```{code-cell} ipython3
7676
p = pr.sample(10, time_horizon=10, time_steps=1000)
7777
p.plot().update_traces(line_width=1)
7878
```
@@ -101,41 +101,41 @@ The mean and variance of the compund Poisson is given by
101101
{\mathbb Var}\left[x_t^2\right] &= \lambda t \left({\mathbb Var}\left[j\right] + {\mathbb E}\left[j\right]^2\right)
102102
\end{align}
103103

104-
### Exponential Compound Poisson Process
104+
## Exponential Compound Poisson Process
105105

106-
The library includes the Exponential Poisson Process, a compound Poisson process where the jump sizes are sampled from an exponential distribution.
106+
The Exponential Poisson Process is a compound Poisson process where the jump sizes are sampled from an exponential distribution. To create an Exponential Compound Poisson process we simply pass the `Exponential` distribution as the jump distribution.
107107

108-
```{code-cell}
108+
```{code-cell} ipython3
109109
from quantflow.sp.poisson import CompoundPoissonProcess
110110
from quantflow.utils.distributions import Exponential
111111
112112
pr = CompoundPoissonProcess(intensity=1, jumps=Exponential(decay=1))
113113
pr
114114
```
115115

116-
```{code-cell}
116+
```{code-cell} ipython3
117117
from quantflow.utils.plot import plot_characteristic
118118
m = pr.marginal(1)
119119
plot_characteristic(m)
120120
```
121121

122-
```{code-cell}
122+
```{code-cell} ipython3
123123
m.mean(), m.mean_from_characteristic()
124124
```
125125

126-
```{code-cell}
126+
```{code-cell} ipython3
127127
m.variance(), m.variance_from_characteristic()
128128
```
129129

130-
```{code-cell}
130+
```{code-cell} ipython3
131131
pr.sample(10, time_horizon=10, time_steps=1000).plot().update_traces(line_width=1)
132132
```
133133

134134
### MC simulations
135135

136136
Here we test the simulated mean and standard deviation against the analytical values.
137137

138-
```{code-cell}
138+
```{code-cell} ipython3
139139
import pandas as pd
140140
from quantflow.utils import plot
141141
@@ -145,7 +145,7 @@ df = pd.DataFrame(mean, index=paths.time)
145145
plot.plot_lines(df)
146146
```
147147

148-
```{code-cell}
148+
```{code-cell} ipython3
149149
std = dict(std=pr.marginal(paths.time).std(), simulated=paths.std())
150150
df = pd.DataFrame(std, index=paths.time)
151151
plot.plot_lines(df)
@@ -155,19 +155,19 @@ plot.plot_lines(df)
155155

156156
A compound Poisson process with a normal jump distribution
157157

158-
```{code-cell}
158+
```{code-cell} ipython3
159159
from quantflow.utils.distributions import Normal
160160
from quantflow.sp.poisson import CompoundPoissonProcess
161161
pr = CompoundPoissonProcess(intensity=10, jumps=Normal(mu=0.01, sigma=0.1))
162162
pr
163163
```
164164

165-
```{code-cell}
165+
```{code-cell} ipython3
166166
m = pr.marginal(1)
167167
m.mean(), m.std()
168168
```
169169

170-
```{code-cell}
170+
```{code-cell} ipython3
171171
m.mean_from_characteristic(), m.std_from_characteristic()
172172
```
173173

@@ -222,14 +222,14 @@ The intensity function of a DSPP is given by:
222222
{\mathbb P}\left(N_T - N_t = n\right) = {\mathbb E}_t\left[e^{-\Lambda_{t,T}} \frac{\Lambda_{t, T}^n}{n!}\right] = \frac{1}{n!}
223223
\end{equation}
224224

225-
```{code-cell}
225+
```{code-cell} ipython3
226226
from quantflow.sp.dsp import DSP, PoissonProcess, CIR
227227
pr = DSP(intensity=CIR(sigma=2, kappa=1), poisson=PoissonProcess(intensity=2))
228228
pr2 = DSP(intensity=CIR(rate=2, sigma=4, kappa=2, theta=2), poisson=PoissonProcess(intensity=1))
229229
pr, pr2
230230
```
231231

232-
```{code-cell}
232+
```{code-cell} ipython3
233233
import numpy as np
234234
from quantflow.utils import plot
235235
import plotly.graph_objects as go
@@ -242,28 +242,28 @@ plot.plot_marginal_pdf(pr2.marginal(1), n, analytical=False, fig=fig, marker_col
242242
fig.add_trace(go.Scatter(x=pdf.x, y=pr.poisson.marginal(1).pdf(pdf.x), name="Poisson", mode="markers", marker_color="blue"))
243243
```
244244

245-
```{code-cell}
245+
```{code-cell} ipython3
246246
pr.marginal(1).mean(), pr.marginal(1).variance()
247247
```
248248

249-
```{code-cell}
249+
```{code-cell} ipython3
250250
pr2.marginal(1).mean(), pr2.marginal(1).variance()
251251
```
252252

253-
```{code-cell}
253+
```{code-cell} ipython3
254254
from quantflow.utils.plot import plot_characteristic
255255
m = pr.marginal(2)
256256
plot_characteristic(m)
257257
```
258258

259-
```{code-cell}
259+
```{code-cell} ipython3
260260
pr.sample(10, time_horizon=10, time_steps=1000).plot().update_traces(line_width=1)
261261
```
262262

263-
```{code-cell}
263+
```{code-cell} ipython3
264264
m.characteristic(2)
265265
```
266266

267-
```{code-cell}
267+
```{code-cell} ipython3
268268
m.characteristic(-2).conj()
269269
```

0 commit comments

Comments
 (0)