Skip to content

Commit 79e45ee

Browse files
authored
Lewis and Cos pricing and log-strike moneyness fixes (#47)
1 parent 2524b4a commit 79e45ee

29 files changed

Lines changed: 1923 additions & 495 deletions

‎Makefile‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ docs: ## build documentation
1111
@uv run ./dev/build-examples
1212
@uv run mkdocs build
1313

14+
.PHONY: docs-bib
15+
docs-bib: ## Regenerate docs bibliography
16+
@uv run ./docs/bib2md.py
17+
1418
.PHONY: docs-examples
1519
docs-examples: ## Regenerate docs examples
1620
@uv run ./dev/build-examples

‎docs/api/options/pricer.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ different stochastic volatility models.
55

66
::: quantflow.options.pricer.OptionPricerBase
77

8+
::: quantflow.options.pricer.OptionPricingMethod
9+
810
::: quantflow.options.pricer.OptionPricer
911

1012
::: quantflow.options.pricer.MaturityPricer

‎docs/api/sp/bns.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Barndorff-Nielson & Shephard process
2+
3+
::: quantflow.sp.bns.BNS

‎docs/api/sp/index.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,5 @@ This page gives an overview of all stochastic processes available in the library
4848
::: quantflow.sp.base.StochasticProcess1D
4949

5050
::: quantflow.sp.base.IntensityProcess
51+
52+
::: quantflow.sp.base.StochasticProcess1DMarginal

‎docs/api/sp/ou.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,6 @@ These are the classes that implement gaussian and non-gaussian
1010

1111
## Non-Gaussian OU process
1212

13+
::: quantflow.sp.ou.NGOU
14+
1315
::: quantflow.sp.ou.GammaOU

‎docs/api/utils/index.md‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
11
# Utilities
22

3-
This section contains utility functions and classes that are used throughout the library. They are not meant to be used directly by the user, but they can be useful for advanced users who want to extend the library or understand its inner workings.
3+
This section contains utility functions and classes used throughout the library.
4+
They are not meant to be used directly by the user, but can be useful for advanced
5+
users who want to extend the library or understand its inner workings.
6+
7+
## Classes
8+
9+
| Page | Description |
10+
|---|---|
11+
| [Marginal 1D](marginal1d.md) | Abstract base class for 1D marginal distributions with Fourier-based option pricing (Carr-Madan and Lewis formulas) |
12+
| [Distributions](distributions.md) | Parametric 1D distributions (e.g. Exponential) |
13+
| [Bins](bins.md) | Histogram and event-density utilities |
14+
| [Numbers](numbers.md) | Decimal number helpers |
15+
| [Types](types.md) | Shared type aliases (FloatArray, etc.) |

‎docs/bib2md.py‎

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
#!/usr/bin/env python3
2+
"""Convert docs/references.bib to docs/bibliography.md.
3+
4+
Usage:
5+
uv run python docs/bib2md.py [--bib PATH] [--out PATH]
6+
7+
The BibTeX file is the source of truth; run this script whenever it changes.
8+
"""
9+
from __future__ import annotations
10+
11+
import argparse
12+
import re
13+
import sys
14+
from pathlib import Path
15+
16+
# ---------------------------------------------------------------------------
17+
# Parser
18+
# ---------------------------------------------------------------------------
19+
20+
_ENTRY_RE = re.compile(
21+
r"@(\w+)\s*\{\s*([\w][\w:/-]*)\s*,\s*(.*?)\n\}",
22+
re.DOTALL,
23+
)
24+
_FIELD_RE = re.compile(
25+
r"""(\w+)\s*=\s*(?:\{((?:[^{}]|\{[^{}]*\})*)\}|"([^"]*)")""",
26+
re.DOTALL,
27+
)
28+
29+
30+
def _strip_braces(text: str) -> str:
31+
"""Remove single-level LaTeX capitalisation braces, e.g. {Lévy} -> Lévy."""
32+
return re.sub(r"\{([^{}]*)\}", r"\1", text)
33+
34+
35+
def _clean(value: str) -> str:
36+
text = _strip_braces(" ".join(value.split()))
37+
# Convert LaTeX en-dash (--) to a plain hyphen
38+
return text.replace("--", "-")
39+
40+
41+
def parse_bib(text: str) -> list[dict[str, str]]:
42+
entries = []
43+
for m in _ENTRY_RE.finditer(text):
44+
entry: dict[str, str] = {
45+
"type": m.group(1).lower(),
46+
"key": m.group(2),
47+
}
48+
for fm in _FIELD_RE.finditer(m.group(3)):
49+
raw = fm.group(2) if fm.group(2) is not None else fm.group(3)
50+
entry[fm.group(1).lower()] = _clean(raw)
51+
entries.append(entry)
52+
return entries
53+
54+
55+
# ---------------------------------------------------------------------------
56+
# Author formatting
57+
# ---------------------------------------------------------------------------
58+
59+
def _format_single_author(author: str) -> str:
60+
"""Convert 'Lastname, Firstname' to 'Firstname Lastname'; leave others as-is."""
61+
author = author.strip()
62+
# Only reformat if a comma is clearly separating last from first name
63+
# (avoid splitting on commas inside name suffixes or freeform strings)
64+
parts = author.split(",", 1)
65+
if len(parts) == 2:
66+
last, first = parts[0].strip(), parts[1].strip()
67+
# Sanity: last name should be a single word (no spaces)
68+
if first and " " not in last:
69+
return f"{first} {last}"
70+
return author
71+
72+
73+
def format_authors(author_field: str) -> str:
74+
# Normalise separators: "and" and "&" both split authors
75+
normalised = re.sub(r"\s+and\s+", " & ", author_field, flags=re.IGNORECASE)
76+
authors = [a.strip() for a in normalised.split("&") if a.strip()]
77+
return ", ".join(_format_single_author(a) for a in authors)
78+
79+
80+
# ---------------------------------------------------------------------------
81+
# Entry formatter
82+
# ---------------------------------------------------------------------------
83+
84+
def _title_link(title: str, url: str) -> str:
85+
if not url:
86+
return title
87+
return f'[{title}]({url}){{target="_blank" rel="noopener"}}'
88+
89+
90+
def _journal_detail(entry: dict[str, str]) -> str:
91+
"""Build 'Journal, Vol(Num):Pages' string."""
92+
journal = entry.get("journal", "")
93+
volume = entry.get("volume", "")
94+
number = entry.get("number", "")
95+
pages = entry.get("pages", "")
96+
if not journal:
97+
return ""
98+
detail = journal
99+
if volume:
100+
vol_str = volume
101+
if number:
102+
vol_str += f"({number})"
103+
if pages:
104+
vol_str += f":{pages}"
105+
detail += f", {vol_str}"
106+
elif pages:
107+
detail += f", {pages}"
108+
return detail
109+
110+
111+
def format_entry(entry: dict[str, str]) -> str:
112+
key = entry["key"]
113+
title = entry.get("title", "Unknown Title")
114+
year = entry.get("year", "")
115+
url = entry.get("url", "")
116+
author = entry.get("author", "")
117+
118+
# Prefer doi as url when no url field present
119+
if not url and "doi" in entry:
120+
doi = entry["doi"].lstrip("doi:")
121+
url = f"https://doi.org/{doi}"
122+
123+
author_str = format_authors(author) if author else ""
124+
title_md = _title_link(title, url)
125+
year_str = f"({year})" if year else ""
126+
127+
# Prefix: "Author(s). (Year)"
128+
prefix_parts = []
129+
if author_str:
130+
prefix_parts.append(author_str.rstrip(".") + ".")
131+
if year_str:
132+
prefix_parts.append(year_str)
133+
prefix = " ".join(prefix_parts)
134+
135+
# Suffix depends on entry type
136+
entry_type = entry.get("type", "article")
137+
if entry_type == "article":
138+
suffix = _journal_detail(entry)
139+
elif entry_type == "book":
140+
suffix = entry.get("publisher", "")
141+
elif entry_type in ("mastersthesis", "phdthesis"):
142+
suffix = entry.get("school", "")
143+
else:
144+
suffix = entry.get("publisher", entry.get("school", ""))
145+
146+
body = f"{prefix} {title_md}" if prefix else title_md
147+
if suffix:
148+
body = f"{body}, {suffix}"
149+
150+
return f"#### {key}\n\n{body}\n"
151+
152+
153+
# ---------------------------------------------------------------------------
154+
# Main
155+
# ---------------------------------------------------------------------------
156+
157+
def convert(bib_path: Path, out_path: Path) -> None:
158+
text = bib_path.read_text(encoding="utf-8")
159+
entries = parse_bib(text)
160+
if not entries:
161+
print(f"No entries found in {bib_path}", file=sys.stderr)
162+
sys.exit(1)
163+
164+
entries.sort(key=lambda e: e["key"].lower())
165+
166+
lines = ["# Bibliography\n", "\n---\n"]
167+
for entry in entries:
168+
lines.append("\n")
169+
lines.append(format_entry(entry))
170+
171+
out_path.write_text("".join(lines), encoding="utf-8")
172+
print(f"Wrote {len(entries)} entries to {out_path}")
173+
174+
175+
def main() -> None:
176+
docs = Path(__file__).parent
177+
parser = argparse.ArgumentParser(description=__doc__)
178+
parser.add_argument(
179+
"--bib",
180+
type=Path,
181+
default=docs / "references.bib",
182+
help="Input BibTeX file (default: docs/references.bib)",
183+
)
184+
parser.add_argument(
185+
"--out",
186+
type=Path,
187+
default=docs / "bibliography.md",
188+
help="Output Markdown file (default: docs/bibliography.md)",
189+
)
190+
args = parser.parse_args()
191+
convert(args.bib, args.out)
192+
193+
194+
if __name__ == "__main__":
195+
main()

‎docs/bibliography.md‎

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,73 @@
11
# Bibliography
22

3+
The raw BibTeX source for all entries is available in [references.bib](/references.bib).
4+
35
---
46

7+
#### bertoin
8+
9+
J. Bertoin. (1996) Lévy Processes, Cambridge University Press
10+
11+
#### bns
12+
13+
O.E. Barndorff-Nielsen, N. Shephard. (2001) [Non-Gaussian OU based models and some of their uses in financial economics](https://drive.google.com/file/d/11TnGl2ER_-QzEu_h7ZtGGass2rF8_u1Y/view){target="_blank" rel="noopener"}, Journal of the Royal Statistical Society, 63(2)
14+
515
#### carr_madan
616

7-
Peter Carr, Dilip Madan, [Option Valuation Using the Fast Fourier Transform](https://doi.org/10.21314/JCF.1999.043), Journal of Computational Finance, 2(4):61-73, 1999
17+
P. Carr, D. Madan. (1999) [Option valuation using the fast Fourier transform](http://faculty.baruch.cuny.edu/lwu/890/CarrMadan99.pdf){target="_blank" rel="noopener"}, Journal of Computational Finance, 3:463-520
818

919
#### carr_wu
1020

11-
Peter Carr, Liuren Wu, [Time-Changed Lévy Processes and Option Pricing](https://doi.org/10.1016/S0304-405X(03)00171-5), Journal of Financial Economics, 71(1):113-141, 2004
21+
P. Carr, L. Wu. (2002) [Time-changed Lévy processes and option pricing](https://engineering.nyu.edu/sites/default/files/2019-03/Carr-time-changed-levy-processes-option-pricing.pdf){target="_blank" rel="noopener"}, Journal of Financial Economics, 7:113-141
22+
23+
#### cgmy
24+
25+
Carr P., Geman H., Madan D.B., Yor M. (2003) [Stochastic Volatility for Lévy processes](https://engineering.nyu.edu/sites/default/files/2019-03/Carr-stochastic-volatility-levy-processes.pdf){target="_blank" rel="noopener"}, Mathematical Finance, 13(3)
1226

1327
#### chourdakis
1428

15-
Kyriakos Chourdakis, [Option Pricing Using the Fractional FFT](https://doi.org/10.21314/JCF.2005.137),Journal of Computational Finance, 8(2):1-18, 2005
29+
K. Chourdakis. (2004) [Option Pricing Using the Fractional FFT](https://citeseerx.ist.psu.edu/document?repid=rep1&type=pdf&doi=6bdf4696312d37427eda2740137650c09deacda7){target="_blank" rel="noopener"}, Journal of Computational Finance, 8:1-18
30+
31+
#### cos
32+
33+
F. Fang, K. Oosterlee. (2008) [A Novel Pricing Method for European Options Based on Fourier-Cosine Series Expansions](https://mpra.ub.uni-muenchen.de/8914/4/MPRA_paper_8914.pdf){target="_blank" rel="noopener"}
34+
35+
#### dsp
36+
37+
Unknown. (2017) [Doubly Stochastic Poisson Processes with Affine Intensities](https://editorialexpress.com/cgi-bin/conference/download.cgi?db_name=sbe35&paper_id=179){target="_blank" rel="noopener"}, internet
38+
39+
#### ekf
40+
41+
Wang X., He X., Zhao Y., Zuo Z. (2017) [Parameter Estimations of Heston Model Based on Consistent Extended Kalman Filter](https://www.sciencedirect.com/science/article/pii/S2405896317324758){target="_blank" rel="noopener"}, internet
42+
43+
#### gamma-ou
44+
45+
P. Sabino, C. Petroni. (2021) [Gamma Related Ornstein-Uhlenbeck Processes and their Simulation](https://doi.org/10.1080/00949655.2020.1842408){target="_blank" rel="noopener"}, Journal of Statistical Computation and Simulation, 91(6)
46+
47+
#### heston-calibration
48+
49+
Milan Mrázek, Jan Pospíšil. (2017) [Calibration and simulation of Heston model](https://doi.org/10.1515/math-2017-0058){target="_blank" rel="noopener"}, Open Mathematics, 15(1):679-704
50+
51+
#### heston-simulation
52+
53+
Leif B.G. Andersen. (2008) [Efficient Simulation of the Heston Stochastic Volatility Model](https://papers.ssrn.com/sol3/papers.cfm?abstract_id=946405){target="_blank" rel="noopener"}, Journal of Computational Finance, 11(3)
54+
55+
#### lee_option
56+
57+
R. W. Lee. (2004) [Option Pricing by Transform Methods: Extensions, Unification, and Error Control](https://www.math.uchicago.edu/~rl/dft.pdf){target="_blank" rel="noopener"}
58+
59+
#### lewis
60+
61+
A. L. Lewis. (2001) [A Simple Option Formula for General Jump-Diffusion and other Exponential Lévy Processes](https://drive.google.com/file/d/1JiYfyOU7lUKHrMqTRPbBCdasctwYDc4Z/view){target="_blank" rel="noopener"}
1662

1763
#### molnar
1864

19-
Peter Molnar, [Volatility modeling and forecasting: utilization of realized volatility, implied volatility and the highest and lowest price of the day](https://drive.google.com/file/d/1zCU1OZyrKQLpxaypPv9U5UPbReBDXcMf/view), Master's thesis, University of Economics in Prague, 2020
65+
Peter Molnar. (2020) [Volatility modeling and forecasting: utilization of realized volatility, implied volatility and the highest and lowest price of the day](https://drive.google.com/file/d/1zCU1OZyrKQLpxaypPv9U5UPbReBDXcMf/view){target="_blank" rel="noopener"}, University of Economics in Prague
66+
67+
#### saez
68+
69+
G. K. G. Saez. (2014) [Fourier Transform Methods for Option Pricing: An Application to extended Heston-type Models](https://www.uv.es/bfc/TFM2014/008-014.pdf){target="_blank" rel="noopener"}, Universidad del Pais Vasco
2070

21-
#### Gauthier
71+
#### ukf
2272

23-
Gauthier Godin & Legros, [Deep Implied Volatility Factor Models for Stock Options](https://drive.google.com/file/d/1Rjypn1IqnhpiZz08s0hxl5ISQDC8KeWk/view?usp=sharing), 2025
73+
Merwe. (2014) [The Unscented Kalman Filter for Nonlinear Estimation](https://groups.seas.harvard.edu/courses/cs281/papers/unscented.pdf){target="_blank" rel="noopener"}, internet

0 commit comments

Comments
 (0)