Skip to content

Commit 3b18574

Browse files
committed
Better inputs
1 parent 76725b8 commit 3b18574

File tree

7 files changed

+4107
-129
lines changed

7 files changed

+4107
-129
lines changed

notebooks/applications/volatility_surface.md

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ jupytext:
44
extension: .md
55
format_name: myst
66
format_version: 0.13
7-
jupytext_version: 1.16.6
7+
jupytext_version: 1.17.2
88
kernelspec:
9-
display_name: .venv
10-
language: python
119
name: python3
10+
display_name: Python 3 (ipykernel)
11+
language: python
1212
---
1313

1414
# Volatility Surface
@@ -21,7 +21,7 @@ First thing, fetch the data
2121
from quantflow.data.deribit import Deribit
2222
2323
async with Deribit() as cli:
24-
loader = await cli.volatility_surface_loader("eth")
24+
loader = await cli.volatility_surface_loader("eth", exclude_open_interest=0)
2525
```
2626

2727
Once we have loaded the data, we create the surface and display the term-structure of forwards
@@ -125,16 +125,20 @@ Serialization
125125
It is possible to save the vol surface into a json file so it can be recreated for testing or for serialization/deserialization.
126126

127127
```{code-cell} ipython3
128-
with open("../tests/volsurface.json", "w") as fp:
129-
fp.write(vs.inputs().model_dump_json())
128+
with open("../../quantflow_tests/volsurface.json", "w") as fp:
129+
fp.write(vs.inputs().model_dump_json(indent=2))
130130
```
131131

132132
```{code-cell} ipython3
133133
from quantflow.options.surface import VolSurfaceInputs, surface_from_inputs
134134
import json
135135
136-
with open("../tests/volsurface.json", "r") as fp:
136+
with open("../../quantflow_tests/volsurface.json", "r") as fp:
137137
inputs = VolSurfaceInputs(**json.load(fp))
138138
139139
vs2 = surface_from_inputs(inputs)
140140
```
141+
142+
```{code-cell} ipython3
143+
144+
```

quantflow/data/deribit.py

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,14 @@
99
from fluid.utils.data import compact_dict
1010
from fluid.utils.http_client import AioHttpClient, HttpResponse, HttpResponseError
1111

12+
from quantflow.options.inputs import OptionType
1213
from quantflow.options.surface import VolSecurityType, VolSurfaceLoader
13-
from quantflow.utils.numbers import round_to_step, to_decimal
14+
from quantflow.utils.numbers import (
15+
Number,
16+
round_to_step,
17+
to_decimal,
18+
to_decimal_or_none,
19+
)
1420

1521

1622
def parse_maturity(v: str) -> datetime:
@@ -80,9 +86,19 @@ async def get_volatility(self, currency: str, **kw: Any) -> pd.DataFrame:
8086
kw.update(params=dict(currency=currency), callback=self.to_df)
8187
return await self.get_path("public/get_historical_volatility", **kw)
8288

83-
async def volatility_surface_loader(self, currency: str) -> VolSurfaceLoader:
89+
async def volatility_surface_loader(
90+
self,
91+
currency: str,
92+
*,
93+
exclude_open_interest: Number | None = None,
94+
exclude_volume: Number | None = None,
95+
) -> VolSurfaceLoader:
8496
"""Create a :class:`.VolSurfaceLoader` for a given crypto-currency"""
85-
loader = VolSurfaceLoader()
97+
loader = VolSurfaceLoader(
98+
asset=currency,
99+
exclude_open_interest=to_decimal_or_none(exclude_open_interest),
100+
exclude_volume=to_decimal_or_none(exclude_volume),
101+
)
86102
futures = await self.get_book_summary_by_currency(
87103
currency=currency, kind=InstrumentKind.future
88104
)
@@ -92,9 +108,9 @@ async def volatility_surface_loader(self, currency: str) -> VolSurfaceLoader:
92108
instruments = await self.get_instruments(currency=currency)
93109
instrument_map = {i["instrument_name"]: i for i in instruments}
94110
min_tick_size = Decimal("inf")
95-
for future in futures:
96-
if (bid_ := future["bid_price"]) and (ask_ := future["ask_price"]):
97-
name = future["instrument_name"]
111+
for entry in futures:
112+
if (bid_ := entry["bid_price"]) and (ask_ := entry["ask_price"]):
113+
name = entry["instrument_name"]
98114
meta = instrument_map[name]
99115
tick_size = to_decimal(meta["tick_size"])
100116
min_tick_size = min(min_tick_size, tick_size)
@@ -105,8 +121,8 @@ async def volatility_surface_loader(self, currency: str) -> VolSurfaceLoader:
105121
VolSecurityType.spot,
106122
bid=bid,
107123
ask=ask,
108-
open_interest=int(future["open_interest"]),
109-
volume=int(future["volume_usd"]),
124+
open_interest=to_decimal(entry["open_interest"]),
125+
volume=to_decimal(entry["volume_usd"]),
110126
)
111127
else:
112128
maturity = pd.to_datetime(
@@ -119,15 +135,15 @@ async def volatility_surface_loader(self, currency: str) -> VolSurfaceLoader:
119135
maturity=maturity,
120136
bid=bid,
121137
ask=ask,
122-
open_interest=int(future["open_interest"]),
123-
volume=int(future["volume_usd"]),
138+
open_interest=to_decimal(entry["open_interest"]),
139+
volume=to_decimal(entry["volume_usd"]),
124140
)
125141
loader.tick_size_forwards = min_tick_size
126142

127143
min_tick_size = Decimal("inf")
128-
for option in options:
129-
if (bid_ := option["bid_price"]) and (ask_ := option["ask_price"]):
130-
name = option["instrument_name"]
144+
for entry in options:
145+
if (bid_ := entry["bid_price"]) and (ask_ := entry["ask_price"]):
146+
name = entry["instrument_name"]
131147
meta = instrument_map[name]
132148
tick_size = to_decimal(meta["tick_size"])
133149
min_tick_size = min(min_tick_size, tick_size)
@@ -139,9 +155,15 @@ async def volatility_surface_loader(self, currency: str) -> VolSurfaceLoader:
139155
unit="ms",
140156
utc=True,
141157
).to_pydatetime(),
142-
call=meta["option_type"] == "call",
158+
option_type=(
159+
OptionType.call
160+
if meta["option_type"] == "call"
161+
else OptionType.put
162+
),
143163
bid=round_to_step(bid_, tick_size),
144164
ask=round_to_step(ask_, tick_size),
165+
open_interest=to_decimal(entry["open_interest"]),
166+
volume=to_decimal(entry["volume_usd"]),
145167
)
146168
loader.tick_size_options = min_tick_size
147169
return loader

quantflow/options/inputs.py

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,35 @@
33
import enum
44
from datetime import datetime
55
from decimal import Decimal
6-
from typing import Generic, TypeVar
6+
from typing import TypeVar
77

88
from pydantic import BaseModel
99

10+
from quantflow.utils.numbers import ZERO
11+
1012
P = TypeVar("P")
1113

1214

15+
class Side(enum.StrEnum):
16+
"""Side of the market"""
17+
18+
bid = enum.auto()
19+
ask = enum.auto()
20+
21+
22+
class OptionType(enum.StrEnum):
23+
"""Type of option"""
24+
25+
call = enum.auto()
26+
put = enum.auto()
27+
28+
def is_call(self) -> bool:
29+
return self is OptionType.call
30+
31+
def is_put(self) -> bool:
32+
return self is OptionType.put
33+
34+
1335
class VolSecurityType(enum.StrEnum):
1436
"""Type of security for the volatility surface"""
1537

@@ -21,31 +43,30 @@ def vol_surface_type(self) -> VolSecurityType:
2143
return self
2244

2345

24-
class VolSurfaceInput(BaseModel, Generic[P]):
25-
bid: P
26-
ask: P
27-
46+
class VolSurfaceInput(BaseModel):
47+
bid: Decimal
48+
ask: Decimal
49+
open_interest: Decimal = ZERO
50+
volume: Decimal = ZERO
2851

29-
class OptionInput(BaseModel):
30-
price: Decimal
31-
strike: Decimal
32-
maturity: datetime
33-
call: bool
3452

35-
36-
class SpotInput(VolSurfaceInput[Decimal]):
53+
class SpotInput(VolSurfaceInput):
3754
security_type: VolSecurityType = VolSecurityType.spot
3855

3956

40-
class ForwardInput(VolSurfaceInput[Decimal]):
57+
class ForwardInput(VolSurfaceInput):
4158
maturity: datetime
4259
security_type: VolSecurityType = VolSecurityType.forward
4360

4461

45-
class OptionSidesInput(VolSurfaceInput[OptionInput]):
62+
class OptionInput(VolSurfaceInput):
63+
strike: Decimal
64+
maturity: datetime
65+
option_type: OptionType
4666
security_type: VolSecurityType = VolSecurityType.option
4767

4868

4969
class VolSurfaceInputs(BaseModel):
70+
asset: str
5071
ref_date: datetime
51-
inputs: list[ForwardInput | SpotInput | OptionSidesInput]
72+
inputs: list[ForwardInput | SpotInput | OptionInput]

0 commit comments

Comments
 (0)