Skip to content

Commit f2fd918

Browse files
authored
Add fiscal data (#28)
1 parent 7a378c7 commit f2fd918

File tree

14 files changed

+1794
-1526
lines changed

14 files changed

+1794
-1526
lines changed

notebooks/_config.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
# Learn more at https://jupyterbook.org/customize/config.html
33

44
title: Quantflow library
5-
author: Quantmind Team
6-
copyright: "2024"
5+
author: <a href="https://quantmind.com">quantmind</a>
6+
copyright: "2014-2025"
77
logo: assets/quantflow-light.svg
88

99
# Force re-execution of notebooks on each build.

notebooks/data/fiscal_data.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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: Python 3 (ipykernel)
10+
language: python
11+
name: python3
12+
---
13+
14+
# Fiscal Data
15+
16+
```{code-cell} ipython3
17+
from quantflow.data.fiscal_data import FiscalData
18+
```
19+
20+
```{code-cell} ipython3
21+
fd = FiscalData()
22+
```
23+
24+
```{code-cell} ipython3
25+
data = await fd.securities()
26+
data
27+
```
28+
29+
```{code-cell} ipython3
30+
31+
```

notebooks/data/fmp.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,25 @@ The library provides a python client for the [Financial Modelling Prep API](http
1818

1919
```{code-cell} ipython3
2020
from quantflow.data.fmp import FMP
21+
import pandas as pd
2122
cli = FMP()
2223
cli.url
2324
```
2425

26+
## Get path
27+
28+
```{code-cell} ipython3
29+
d = await cli.get_path("stock-list")
30+
pd.DataFrame(d)
31+
```
32+
33+
## Search
34+
35+
```{code-cell} ipython3
36+
d = await cli.search("electric")
37+
pd.DataFrame(d)
38+
```
39+
2540
```{code-cell} ipython3
2641
stock = "KNOS.L"
2742
```
@@ -41,7 +56,7 @@ c
4156
## Executive trading
4257

4358
```{code-cell} ipython3
44-
stock = "KNOS.L"
59+
stock = "AAPL"
4560
```
4661

4762
```{code-cell} ipython3
@@ -58,3 +73,8 @@ await cli.insider_trading(stock)
5873
c = await cli.news(stock)
5974
c
6075
```
76+
77+
```{code-cell} ipython3
78+
p = await cli.market_risk_premium()
79+
pd.DataFrame(p)
80+
```

poetry.lock

Lines changed: 1532 additions & 1457 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "quantflow"
3-
version = "0.3.2"
3+
version = "0.3.3"
44
description = "quantitative analysis"
55
authors = ["Luca <[email protected]>"]
66
license = "BSD-3-Clause"
@@ -17,21 +17,21 @@ scipy = "^1.14.1"
1717
pydantic = "^2.0.2"
1818
ccy = { version = "^1.7.1" }
1919
python-dotenv = "^1.0.1"
20-
polars = {version = "^1.11.0", extras=["pandas", "pyarrow"]}
20+
polars = { version = "^1.11.0", extras = ["pandas", "pyarrow"] }
2121
asciichartpy = { version = "^1.5.25", optional = true }
2222
prompt-toolkit = { version = "^3.0.43", optional = true }
23-
aio-fluid = {version = "^1.2.1", extras=["http"], optional = true}
24-
rich = {version = "^13.9.4", optional = true}
25-
click = {version = "^8.1.7", optional = true}
26-
holidays = {version = "^0.63", optional = true}
27-
async-cache = {version = "^1.1.1", optional = true}
23+
aio-fluid = { version = "^1.2.1", extras = ["http"], optional = true }
24+
rich = { version = "^13.9.4", optional = true }
25+
click = { version = "^8.1.7", optional = true }
26+
holidays = { version = "^0.63", optional = true }
27+
async-cache = { version = "^1.1.1", optional = true }
2828

2929
[tool.poetry.group.dev.dependencies]
3030
black = "^25.1.0"
3131
pytest-cov = "^6.0.0"
3232
mypy = "^1.14.1"
3333
ghp-import = "^2.0.2"
34-
ruff = "^0.11.2"
34+
ruff = "^0.11.12"
3535
pytest-asyncio = "^0.26.0"
3636
isort = "^6.0.1"
3737

@@ -44,7 +44,7 @@ cli = [
4444
"prompt-toolkit",
4545
"rich",
4646
"click",
47-
"holidays"
47+
"holidays",
4848
]
4949

5050
[tool.poetry.group.book]
@@ -74,9 +74,7 @@ formats = "ipynb,myst"
7474

7575
[tool.pytest.ini_options]
7676
asyncio_mode = "auto"
77-
testpaths = [
78-
"quantflow_tests"
79-
]
77+
testpaths = ["quantflow_tests"]
8078

8179
[tool.isort]
8280
profile = "black"
@@ -102,7 +100,7 @@ module = [
102100
"IPython.*",
103101
"pandas.*",
104102
"plotly.*",
105-
"scipy.*"
103+
"scipy.*",
106104
]
107105
ignore_missing_imports = true
108106
disallow_untyped_defs = false

quantflow/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""Quantitative analysis and pricing"""
22

3-
__version__ = "0.3.2"
3+
__version__ = "0.3.3"

quantflow/cli/commands/crypto.py

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from cache import AsyncTTL
99
from ccy.cli.console import df_to_rich
1010

11-
from quantflow.data.deribit import Deribit
11+
from quantflow.data.deribit import Deribit, InstrumentKind
1212
from quantflow.options.surface import VolSurface
1313
from quantflow.utils.numbers import round_to_step
1414

@@ -24,13 +24,34 @@ def crypto() -> None:
2424
ctx.set_as_section()
2525

2626

27+
@crypto.command()
28+
@click.argument("currency")
29+
@click.option(
30+
"-k",
31+
"--kind",
32+
type=click.Choice(list(InstrumentKind)),
33+
default=InstrumentKind.spot.value,
34+
)
35+
def instruments(currency: str, kind: str) -> None:
36+
"""Provides information about instruments
37+
38+
Instruments for given cryptocurrency from Deribit API"""
39+
ctx = QuantContext.current()
40+
data = asyncio.run(get_instruments(ctx, currency, kind))
41+
df = pd.DataFrame(data)
42+
ctx.qf.print(df_to_rich(df))
43+
44+
2745
@crypto.command()
2846
@click.argument("currency")
2947
@options.length
3048
@options.height
3149
@options.chart
3250
def volatility(currency: str, length: int, height: int, chart: bool) -> None:
33-
"""Provides information about historical volatility for given cryptocurrency"""
51+
"""Provides information about historical volatility
52+
53+
Historical volatility for given cryptocurrency from Deribit API
54+
"""
3455
ctx = QuantContext.current()
3556
df = asyncio.run(get_volatility(ctx, currency))
3657
df["volatility"] = df["volatility"].map(lambda p: round_to_step(p, "0.01"))
@@ -109,9 +130,16 @@ def prices(symbol: str, height: int, length: int, chart: bool, frequency: str) -
109130
)
110131

111132

133+
async def get_instruments(ctx: QuantContext, currency: str, kind: str) -> list[dict]:
134+
async with Deribit() as client:
135+
return await client.get_instruments(
136+
currency=currency, kind=InstrumentKind(kind)
137+
)
138+
139+
112140
async def get_volatility(ctx: QuantContext, currency: str) -> pd.DataFrame:
113141
async with Deribit() as client:
114-
return await client.get_volatility(params=dict(currency=currency))
142+
return await client.get_volatility(currency)
115143

116144

117145
@AsyncTTL(time_to_live=10)

quantflow/cli/commands/stocks.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ def stocks() -> None:
2424
ctx.set_as_section()
2525

2626

27+
@stocks.command()
28+
def indices() -> None:
29+
"""Search companies"""
30+
ctx = QuantContext.current()
31+
data = asyncio.run(get_indices(ctx))
32+
df = pd.DataFrame(data)
33+
ctx.qf.print(df_to_rich(df))
34+
35+
2736
@stocks.command()
2837
@click.argument("symbol")
2938
def profile(symbol: str) -> None:
@@ -78,6 +87,11 @@ def sectors(period: str) -> None:
7887
ctx.qf.print(df_to_rich(df))
7988

8089

90+
async def get_indices(ctx: QuantContext) -> list[dict]:
91+
async with ctx.fmp() as cli:
92+
return await cli.indices()
93+
94+
8195
async def get_prices(ctx: QuantContext, symbol: str, frequency: str) -> pd.DataFrame:
8296
async with ctx.fmp() as cli:
8397
return await cli.prices(symbol, frequency)

quantflow/data/deribit.py

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
import enum
2+
from dataclasses import dataclass
13
from datetime import datetime, timezone
24
from decimal import Decimal
35
from typing import Any, cast
46

57
import pandas as pd
68
from dateutil.parser import parse
9+
from fluid.utils.data import compact_dict
710
from fluid.utils.http_client import AioHttpClient, HttpResponse, HttpResponseError
811

912
from quantflow.options.surface import VolSecurityType, VolSurfaceLoader
@@ -14,47 +17,79 @@ def parse_maturity(v: str) -> datetime:
1417
return parse(v).replace(tzinfo=timezone.utc, hour=8)
1518

1619

20+
class InstrumentKind(enum.StrEnum):
21+
"""Instrument kind for Deribit API."""
22+
23+
future = enum.auto()
24+
option = enum.auto()
25+
spot = enum.auto()
26+
future_combo = enum.auto()
27+
option_combo = enum.auto()
28+
29+
30+
@dataclass
1731
class Deribit(AioHttpClient):
1832
"""Deribit API client
1933
20-
Fetch market and static data from `Deribit`_.
34+
Fetch market and static data from `Deribit`_ API.
2135
2236
.. _Deribit: https://docs.deribit.com/
2337
"""
2438

25-
url = "https://www.deribit.com/api/v2"
39+
url: str = "https://www.deribit.com/api/v2"
2640

27-
async def get_book_summary_by_instrument(self, **kw: Any) -> list[dict]:
28-
kw.update(callback=self.to_result)
41+
async def get_book_summary_by_instrument(
42+
self,
43+
instrument_name: str,
44+
**kw: Any,
45+
) -> list[dict]:
46+
"""Get the book summary for a given instrument."""
47+
kw.update(params=dict(instrument_name=instrument_name), callback=self.to_result)
2948
return cast(
3049
list[dict],
3150
await self.get_path("public/get_book_summary_by_instrument", **kw),
3251
)
3352

34-
async def get_book_summary_by_currency(self, **kw: Any) -> list[dict]:
35-
kw.update(callback=self.to_result)
53+
async def get_book_summary_by_currency(
54+
self, currency: str, kind: InstrumentKind | None = None, **kw: Any
55+
) -> list[dict]:
56+
"""Get the book summary for a given currency."""
57+
kw.update(
58+
params=compact_dict(currency=currency, kind=kind), callback=self.to_result
59+
)
3660
return cast(
3761
list[dict], await self.get_path("public/get_book_summary_by_currency", **kw)
3862
)
3963

40-
async def get_instruments(self, **kw: Any) -> list[dict]:
41-
kw.update(callback=self.to_result)
64+
async def get_instruments(
65+
self,
66+
currency: str,
67+
kind: InstrumentKind | None = None,
68+
expired: bool | None = None,
69+
**kw: Any,
70+
) -> list[dict]:
71+
"""Get the list of instruments for a given currency."""
72+
kw.update(
73+
params=compact_dict(currency=currency, kind=kind, expired=expired),
74+
callback=self.to_result,
75+
)
4276
return cast(list[dict], await self.get_path("public/get_instruments", **kw))
4377

44-
async def get_volatility(self, **kw: Any) -> pd.DataFrame:
45-
kw.update(callback=self.to_df)
78+
async def get_volatility(self, currency: str, **kw: Any) -> pd.DataFrame:
79+
"""Provides information about historical volatility for given cryptocurrency"""
80+
kw.update(params=dict(currency=currency), callback=self.to_df)
4681
return await self.get_path("public/get_historical_volatility", **kw)
4782

4883
async def volatility_surface_loader(self, currency: str) -> VolSurfaceLoader:
4984
"""Create a :class:`.VolSurfaceLoader` for a given crypto-currency"""
5085
loader = VolSurfaceLoader()
5186
futures = await self.get_book_summary_by_currency(
52-
params=dict(currency=currency, kind="future")
87+
currency=currency, kind=InstrumentKind.future
5388
)
5489
options = await self.get_book_summary_by_currency(
55-
params=dict(currency=currency, kind="option")
90+
currency=currency, kind=InstrumentKind.option
5691
)
57-
instruments = await self.get_instruments(params=dict(currency=currency))
92+
instruments = await self.get_instruments(currency=currency)
5893
instrument_map = {i["instrument_name"]: i for i in instruments}
5994
min_tick_size = Decimal("inf")
6095
for future in futures:

quantflow/data/fed.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,7 @@
66
import pandas as pd
77
from fluid.utils.http_client import AioHttpClient
88

9-
URL = (
10-
"https://www.federalreserve.gov/datadownload/Output.aspx?"
11-
"rel=H15&series=bf17364827e38702b42a58cf8eaa3f78&lastobs=&"
12-
)
13-
14-
maturities = [
9+
MATURITIES = (
1510
"month_1",
1611
"month_3",
1712
"month_6",
@@ -23,7 +18,7 @@
2318
"year_10",
2419
"year_20",
2520
"year_30",
26-
]
21+
)
2722

2823

2924
@dataclass
@@ -52,7 +47,7 @@ async def yield_curves(self, **params: Any) -> pd.DataFrame:
5247
params.update(series="bf17364827e38702b42a58cf8eaa3f78", rel="H15")
5348
data = await self._get_text(params)
5449
df = pd.read_csv(data, header=5, index_col=None, parse_dates=True)
55-
df.columns = ["date"] + maturities # type: ignore
50+
df.columns = list(("date",) + MATURITIES) # type: ignore
5651
df = df.set_index("date").replace("ND", np.nan)
5752
return df.dropna(axis=0, how="all").reset_index()
5853

0 commit comments

Comments
 (0)