Skip to content

Commit e12787e

Browse files
committed
Add deribit client
1 parent 7e8b35d commit e12787e

File tree

5 files changed

+114
-57
lines changed

5 files changed

+114
-57
lines changed

quantflow/cli/commands/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from .base import QuantContext, quant_group
2+
from .crypto import crypto
23
from .fred import fred
34
from .stocks import stocks
45
from .vault import vault
@@ -12,5 +13,6 @@ def quantflow() -> None:
1213

1314

1415
quantflow.add_command(vault)
16+
quantflow.add_command(crypto)
1517
quantflow.add_command(stocks)
1618
quantflow.add_command(fred)

quantflow/cli/commands/base.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,23 @@ def quant_group() -> Any:
7777
invoke_without_command=True,
7878
add_help_option=False,
7979
)
80+
81+
82+
class options:
83+
length = click.option(
84+
"-l",
85+
"--length",
86+
type=int,
87+
default=100,
88+
show_default=True,
89+
help="Number of data points",
90+
)
91+
height = click.option(
92+
"-h",
93+
"--height",
94+
type=int,
95+
default=20,
96+
show_default=True,
97+
help="Chart height",
98+
)
99+
chart = click.option("-c", "--chart", is_flag=True, help="Display chart")

quantflow/cli/commands/crypto.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
from __future__ import annotations
2+
3+
import asyncio
4+
5+
import click
6+
import pandas as pd
7+
from asciichartpy import plot
8+
from ccy.cli.console import df_to_rich
9+
10+
from quantflow.data.deribit import Deribit
11+
12+
from .base import QuantContext, options, quant_group
13+
14+
15+
@quant_group()
16+
def crypto() -> None:
17+
"""Crypto currencies commands"""
18+
ctx = QuantContext.current()
19+
if ctx.invoked_subcommand is None:
20+
ctx.set_as_section()
21+
22+
23+
@crypto.command()
24+
@click.argument("currency")
25+
@options.length
26+
@options.height
27+
@options.chart
28+
def volatility(currency: str, length: int, height: int, chart: bool) -> None:
29+
"""Provides information about historical volatility for given cryptocurrency"""
30+
ctx = QuantContext.current()
31+
df = asyncio.run(get_volatility(ctx, currency))
32+
if chart:
33+
data = df["volatility"].tolist()[:length]
34+
ctx.qf.print(plot(data, {"height": height}))
35+
else:
36+
ctx.qf.print(df_to_rich(df))
37+
38+
39+
async def get_volatility(ctx: QuantContext, currency: str) -> pd.DataFrame:
40+
async with Deribit() as client:
41+
return await client.get_volatility(params=dict(currency=currency))

quantflow/cli/commands/fred.py

Lines changed: 27 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from __future__ import annotations
22

33
import asyncio
4-
from typing import TYPE_CHECKING
54

65
import click
76
import pandas as pd
@@ -12,13 +11,10 @@
1211

1312
from quantflow.data.fred import Fred
1413

15-
from .base import QuantContext, quant_group
14+
from .base import QuantContext, options, quant_group
1615

1716
FREQUENCIES = tuple(Fred.freq)
1817

19-
if TYPE_CHECKING:
20-
pass
21-
2218

2319
@quant_group()
2420
def fred() -> None:
@@ -44,29 +40,37 @@ def subcategories(category_id: str | None = None) -> None:
4440

4541
@fred.command()
4642
@click.argument("category-id")
47-
def series(category_id: str) -> None:
43+
@click.option("-j", "--json", is_flag=True, help="Output as JSON")
44+
def series(category_id: str, json: bool = False) -> None:
4845
"""List series for a Fred category"""
4946
ctx = QuantContext.current()
5047
try:
5148
data = asyncio.run(get_series(ctx, category_id))
5249
except HttpResponseError as e:
5350
ctx.qf.error(e)
5451
else:
55-
ctx.qf.print(data)
56-
# df = pd.DataFrame(data["categories"], columns=["id", "name"])
57-
# app.print(df_to_rich(df))
52+
if json:
53+
ctx.qf.print(data)
54+
else:
55+
df = pd.DataFrame(
56+
data["seriess"],
57+
columns=[
58+
"id",
59+
"popularity",
60+
"title",
61+
"frequency",
62+
"observation_start",
63+
"observation_end",
64+
],
65+
).sort_values("popularity", ascending=False)
66+
ctx.qf.print(df_to_rich(df))
5867

5968

6069
@fred.command()
6170
@click.argument("series-id")
62-
@click.option(
63-
"-l",
64-
"--length",
65-
type=int,
66-
default=100,
67-
show_default=True,
68-
help="Number of data points",
69-
)
71+
@options.length
72+
@options.height
73+
@options.chart
7074
@click.option(
7175
"-f",
7276
"--frequency",
@@ -75,53 +79,19 @@ def series(category_id: str) -> None:
7579
show_default=True,
7680
help="Frequency of data",
7781
)
78-
def data(series_id: str, length: int, frequency: str) -> None:
82+
def data(series_id: str, length: int, height: int, chart: bool, frequency: str) -> None:
7983
"""Display a series data"""
8084
ctx = QuantContext.current()
8185
try:
8286
df = asyncio.run(get_serie_data(ctx, series_id, length, frequency))
8387
except HttpResponseError as e:
8488
ctx.qf.error(e)
8589
else:
86-
ctx.qf.print(df_to_rich(df))
87-
88-
89-
@fred.command()
90-
@click.argument("series-id")
91-
@click.option(
92-
"-h",
93-
"--height",
94-
type=int,
95-
default=20,
96-
show_default=True,
97-
help="Chart height",
98-
)
99-
@click.option(
100-
"-l",
101-
"--length",
102-
type=int,
103-
default=100,
104-
show_default=True,
105-
help="Number of data points",
106-
)
107-
@click.option(
108-
"-f",
109-
"--frequency",
110-
type=click.Choice(FREQUENCIES),
111-
default="w",
112-
show_default=True,
113-
help="Frequency of data",
114-
)
115-
def chart(series_id: str, height: int, length: int, frequency: str) -> None:
116-
"""Chart a serie"""
117-
ctx = QuantContext.current()
118-
try:
119-
df = asyncio.run(get_serie_data(ctx, series_id, length, frequency))
120-
except HttpResponseError as e:
121-
ctx.qf.error(e)
122-
else:
123-
data = list(reversed(df["value"].tolist()[:length]))
124-
ctx.qf.print(plot(data, {"height": height}))
90+
if chart:
91+
data = list(reversed(df["value"].tolist()[:length]))
92+
ctx.qf.print(plot(data, {"height": height}))
93+
else:
94+
ctx.qf.print(df_to_rich(df))
12595

12696

12797
async def get_subcategories(ctx: QuantContext, category_id: str | None) -> dict:

quantflow/data/deribit.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from typing import Any
2+
3+
import pandas as pd
4+
from fluid.utils.http_client import AioHttpClient, HttpResponse
5+
6+
7+
class Deribit(AioHttpClient):
8+
url = "https://www.deribit.com/api/v2"
9+
10+
async def get_book_summary_by_instrument(self, **kw: Any) -> Any:
11+
return await self.get_path("public/get_book_summary_by_instrument", **kw)
12+
13+
async def get_volatility(self, **kw: Any) -> pd.DataFrame:
14+
kw.update(callback=self.to_df)
15+
return await self.get_path("public/get_historical_volatility", **kw)
16+
17+
async def get_path(self, path: str, **kw: Any) -> dict:
18+
return await self.get(f"{self.url}/{path}", **kw)
19+
20+
async def to_df(self, response: HttpResponse) -> pd.DataFrame:
21+
data = await response.json()
22+
df = pd.DataFrame(data["result"], columns=["timestamp", "volatility"])
23+
df["timestamp"] = pd.to_datetime(df["timestamp"], unit="ms")
24+
return df

0 commit comments

Comments
 (0)