quantflow 0.3.2__tar.gz → 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. {quantflow-0.3.2 → quantflow-0.4.0}/LICENSE +1 -1
  2. {quantflow-0.3.2 → quantflow-0.4.0}/PKG-INFO +15 -15
  3. {quantflow-0.3.2 → quantflow-0.4.0}/pyproject.toml +30 -38
  4. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/__init__.py +1 -1
  5. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/cli/commands/crypto.py +31 -3
  6. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/cli/commands/stocks.py +14 -0
  7. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/data/deribit.py +84 -27
  8. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/data/fed.py +3 -8
  9. quantflow-0.4.0/quantflow/data/fiscal_data.py +42 -0
  10. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/data/fmp.py +37 -28
  11. quantflow-0.4.0/quantflow/options/inputs.py +72 -0
  12. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/options/surface.py +167 -90
  13. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/utils/dates.py +9 -0
  14. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/utils/numbers.py +5 -0
  15. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/utils/plot.py +5 -5
  16. quantflow-0.3.2/quantflow/options/inputs.py +0 -51
  17. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/cli/__init__.py +0 -0
  18. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/cli/app.py +0 -0
  19. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/cli/commands/__init__.py +0 -0
  20. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/cli/commands/base.py +0 -0
  21. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/cli/commands/fred.py +0 -0
  22. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/cli/commands/vault.py +0 -0
  23. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/cli/script.py +0 -0
  24. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/cli/settings.py +0 -0
  25. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/data/__init__.py +0 -0
  26. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/data/fred.py +0 -0
  27. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/data/vault.py +0 -0
  28. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/options/__init__.py +0 -0
  29. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/options/bs.py +0 -0
  30. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/options/calibration.py +0 -0
  31. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/options/pricer.py +0 -0
  32. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/py.typed +0 -0
  33. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/sp/__init__.py +0 -0
  34. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/sp/base.py +0 -0
  35. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/sp/bns.py +0 -0
  36. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/sp/cir.py +0 -0
  37. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/sp/copula.py +0 -0
  38. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/sp/dsp.py +0 -0
  39. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/sp/heston.py +0 -0
  40. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/sp/jump_diffusion.py +0 -0
  41. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/sp/ou.py +0 -0
  42. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/sp/poisson.py +0 -0
  43. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/sp/weiner.py +0 -0
  44. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/ta/__init__.py +0 -0
  45. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/ta/base.py +0 -0
  46. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/ta/ohlc.py +0 -0
  47. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/ta/paths.py +0 -0
  48. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/utils/__init__.py +0 -0
  49. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/utils/bins.py +0 -0
  50. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/utils/distributions.py +0 -0
  51. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/utils/functions.py +0 -0
  52. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/utils/interest_rates.py +0 -0
  53. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/utils/marginal.py +0 -0
  54. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/utils/transforms.py +0 -0
  55. {quantflow-0.3.2 → quantflow-0.4.0}/quantflow/utils/types.py +0 -0
  56. {quantflow-0.3.2 → quantflow-0.4.0}/readme.md +0 -0
@@ -1,4 +1,4 @@
1
- Copyright (c) 2024 Quantmind
1
+ Copyright (c) 2023-2025 Quantmind
2
2
 
3
3
  Redistribution and use in source and binary forms, with or without modification,
4
4
  are permitted provided that the following conditions are met:
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: quantflow
3
- Version: 0.3.2
3
+ Version: 0.4.0
4
4
  Summary: quantitative analysis
5
5
  License: BSD-3-Clause
6
- Author: Luca
6
+ Author: Luca Sbardella
7
7
  Author-email: luca@quantmind.com
8
- Requires-Python: >=3.11
8
+ Requires-Python: >=3.11,<4.0
9
9
  Classifier: License :: OSI Approved :: BSD License
10
10
  Classifier: Programming Language :: Python :: 3
11
11
  Classifier: Programming Language :: Python :: 3.11
@@ -13,18 +13,18 @@ Classifier: Programming Language :: Python :: 3.12
13
13
  Classifier: Programming Language :: Python :: 3.13
14
14
  Provides-Extra: cli
15
15
  Provides-Extra: data
16
- Requires-Dist: aio-fluid[http] (>=1.2.1,<2.0.0) ; extra == "data"
17
- Requires-Dist: asciichartpy (>=1.5.25,<2.0.0) ; extra == "cli"
18
- Requires-Dist: async-cache (>=1.1.1,<2.0.0) ; extra == "cli"
19
- Requires-Dist: ccy (>=1.7.1,<2.0.0)
20
- Requires-Dist: click (>=8.1.7,<9.0.0) ; extra == "cli"
21
- Requires-Dist: holidays (>=0.63,<0.64) ; extra == "cli"
22
- Requires-Dist: polars[pandas,pyarrow] (>=1.11.0,<2.0.0)
23
- Requires-Dist: prompt-toolkit (>=3.0.43,<4.0.0) ; extra == "cli"
24
- Requires-Dist: pydantic (>=2.0.2,<3.0.0)
25
- Requires-Dist: python-dotenv (>=1.0.1,<2.0.0)
26
- Requires-Dist: rich (>=13.9.4,<14.0.0) ; extra == "cli"
27
- Requires-Dist: scipy (>=1.14.1,<2.0.0)
16
+ Requires-Dist: aio-fluid[http] (>=1.2.1) ; extra == "data"
17
+ Requires-Dist: asciichartpy (>=1.5.25) ; extra == "cli"
18
+ Requires-Dist: async-cache (>=1.1.1) ; extra == "cli"
19
+ Requires-Dist: ccy (>=1.7.1)
20
+ Requires-Dist: click (>=8.1.7) ; extra == "cli"
21
+ Requires-Dist: holidays (>=0.63) ; extra == "cli"
22
+ Requires-Dist: polars[pandas,pyarrow] (>=1.11.0)
23
+ Requires-Dist: prompt-toolkit (>=3.0.43) ; extra == "cli"
24
+ Requires-Dist: pydantic (>=2.0.2)
25
+ Requires-Dist: python-dotenv (>=1.0.1)
26
+ Requires-Dist: rich (>=13.9.4) ; extra == "cli"
27
+ Requires-Dist: scipy (>=1.14.1)
28
28
  Project-URL: Documentation, https://quantmind.github.io/quantflow/
29
29
  Project-URL: Homepage, https://github.com/quantmind/quantflow
30
30
  Project-URL: Repository, https://github.com/quantmind/quantflow
@@ -1,52 +1,48 @@
1
- [tool.poetry]
1
+ [project]
2
2
  name = "quantflow"
3
- version = "0.3.2"
3
+ version = "0.4.0"
4
4
  description = "quantitative analysis"
5
- authors = ["Luca <luca@quantmind.com>"]
5
+ authors = [{ name = "Luca Sbardella", email = "luca@quantmind.com" }]
6
6
  license = "BSD-3-Clause"
7
7
  readme = "readme.md"
8
+ requires-python = ">=3.11,<4.0"
9
+ dependencies = [
10
+ "scipy>=1.14.1",
11
+ "pydantic>=2.0.2",
12
+ "ccy>=1.7.1",
13
+ "python-dotenv>=1.0.1",
14
+ "polars[pandas,pyarrow]>=1.11.0",
15
+ ]
8
16
 
9
- [tool.poetry.urls]
17
+ [project.urls]
10
18
  Homepage = "https://github.com/quantmind/quantflow"
11
19
  Repository = "https://github.com/quantmind/quantflow"
12
20
  Documentation = "https://quantmind.github.io/quantflow/"
13
21
 
14
- [tool.poetry.dependencies]
15
- python = ">=3.11"
16
- scipy = "^1.14.1"
17
- pydantic = "^2.0.2"
18
- ccy = { version = "^1.7.1" }
19
- python-dotenv = "^1.0.1"
20
- polars = {version = "^1.11.0", extras=["pandas", "pyarrow"]}
21
- asciichartpy = { version = "^1.5.25", optional = true }
22
- 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}
22
+ [project.optional-dependencies]
23
+ data = ["aio-fluid[http]>=1.2.1"]
24
+ cli = [
25
+ "asciichartpy>=1.5.25",
26
+ "async-cache>=1.1.1",
27
+ "prompt-toolkit>=3.0.43",
28
+ "rich>=13.9.4",
29
+ "click>=8.1.7",
30
+ "holidays>=0.63",
31
+ ]
32
+
33
+ [project.scripts]
34
+ qf = "quantflow.cli.script:main"
35
+
28
36
 
29
37
  [tool.poetry.group.dev.dependencies]
30
38
  black = "^25.1.0"
31
39
  pytest-cov = "^6.0.0"
32
40
  mypy = "^1.14.1"
33
41
  ghp-import = "^2.0.2"
34
- ruff = "^0.11.2"
35
- pytest-asyncio = "^0.26.0"
42
+ ruff = "^0.12.2"
43
+ pytest-asyncio = "^1.0.0"
36
44
  isort = "^6.0.1"
37
45
 
38
-
39
- [tool.poetry.extras]
40
- data = ["aio-fluid"]
41
- cli = [
42
- "asciichartpy",
43
- "async-cache",
44
- "prompt-toolkit",
45
- "rich",
46
- "click",
47
- "holidays"
48
- ]
49
-
50
46
  [tool.poetry.group.book]
51
47
  optional = true
52
48
 
@@ -62,8 +58,6 @@ sphinx-autosummary-accessors = "^2023.4.0"
62
58
  sphinx-copybutton = "^0.5.2"
63
59
  autodocsumm = "^0.2.14"
64
60
 
65
- [tool.poetry.scripts]
66
- qf = "quantflow.cli.script:main"
67
61
 
68
62
  [build-system]
69
63
  requires = ["poetry-core>=1.0.0"]
@@ -74,9 +68,7 @@ formats = "ipynb,myst"
74
68
 
75
69
  [tool.pytest.ini_options]
76
70
  asyncio_mode = "auto"
77
- testpaths = [
78
- "quantflow_tests"
79
- ]
71
+ testpaths = ["quantflow_tests"]
80
72
 
81
73
  [tool.isort]
82
74
  profile = "black"
@@ -102,7 +94,7 @@ module = [
102
94
  "IPython.*",
103
95
  "pandas.*",
104
96
  "plotly.*",
105
- "scipy.*"
97
+ "scipy.*",
106
98
  ]
107
99
  ignore_missing_imports = true
108
100
  disallow_untyped_defs = false
@@ -1,3 +1,3 @@
1
1
  """Quantitative analysis and pricing"""
2
2
 
3
- __version__ = "0.3.2"
3
+ __version__ = "0.4.0"
@@ -8,7 +8,7 @@ from asciichartpy import plot
8
8
  from cache import AsyncTTL
9
9
  from ccy.cli.console import df_to_rich
10
10
 
11
- from quantflow.data.deribit import Deribit
11
+ from quantflow.data.deribit import Deribit, InstrumentKind
12
12
  from quantflow.options.surface import VolSurface
13
13
  from quantflow.utils.numbers import round_to_step
14
14
 
@@ -24,13 +24,34 @@ def crypto() -> None:
24
24
  ctx.set_as_section()
25
25
 
26
26
 
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
+
27
45
  @crypto.command()
28
46
  @click.argument("currency")
29
47
  @options.length
30
48
  @options.height
31
49
  @options.chart
32
50
  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
+ """
34
55
  ctx = QuantContext.current()
35
56
  df = asyncio.run(get_volatility(ctx, currency))
36
57
  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) -
109
130
  )
110
131
 
111
132
 
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
+
112
140
  async def get_volatility(ctx: QuantContext, currency: str) -> pd.DataFrame:
113
141
  async with Deribit() as client:
114
- return await client.get_volatility(params=dict(currency=currency))
142
+ return await client.get_volatility(currency)
115
143
 
116
144
 
117
145
  @AsyncTTL(time_to_live=10)
@@ -24,6 +24,15 @@ def stocks() -> None:
24
24
  ctx.set_as_section()
25
25
 
26
26
 
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
+
27
36
  @stocks.command()
28
37
  @click.argument("symbol")
29
38
  def profile(symbol: str) -> None:
@@ -78,6 +87,11 @@ def sectors(period: str) -> None:
78
87
  ctx.qf.print(df_to_rich(df))
79
88
 
80
89
 
90
+ async def get_indices(ctx: QuantContext) -> list[dict]:
91
+ async with ctx.fmp() as cli:
92
+ return await cli.indices()
93
+
94
+
81
95
  async def get_prices(ctx: QuantContext, symbol: str, frequency: str) -> pd.DataFrame:
82
96
  async with ctx.fmp() as cli:
83
97
  return await cli.prices(symbol, frequency)
@@ -1,65 +1,116 @@
1
+ import enum
2
+ from dataclasses import dataclass
1
3
  from datetime import datetime, timezone
2
4
  from decimal import Decimal
3
5
  from typing import Any, cast
4
6
 
5
7
  import pandas as pd
6
8
  from dateutil.parser import parse
9
+ from fluid.utils.data import compact_dict
7
10
  from fluid.utils.http_client import AioHttpClient, HttpResponse, HttpResponseError
8
11
 
12
+ from quantflow.options.inputs import OptionType
9
13
  from quantflow.options.surface import VolSecurityType, VolSurfaceLoader
10
- 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
+ )
11
20
 
12
21
 
13
22
  def parse_maturity(v: str) -> datetime:
14
23
  return parse(v).replace(tzinfo=timezone.utc, hour=8)
15
24
 
16
25
 
26
+ class InstrumentKind(enum.StrEnum):
27
+ """Instrument kind for Deribit API."""
28
+
29
+ future = enum.auto()
30
+ option = enum.auto()
31
+ spot = enum.auto()
32
+ future_combo = enum.auto()
33
+ option_combo = enum.auto()
34
+
35
+
36
+ @dataclass
17
37
  class Deribit(AioHttpClient):
18
38
  """Deribit API client
19
39
 
20
- Fetch market and static data from `Deribit`_.
40
+ Fetch market and static data from `Deribit`_ API.
21
41
 
22
42
  .. _Deribit: https://docs.deribit.com/
23
43
  """
24
44
 
25
- url = "https://www.deribit.com/api/v2"
45
+ url: str = "https://www.deribit.com/api/v2"
26
46
 
27
- async def get_book_summary_by_instrument(self, **kw: Any) -> list[dict]:
28
- kw.update(callback=self.to_result)
47
+ async def get_book_summary_by_instrument(
48
+ self,
49
+ instrument_name: str,
50
+ **kw: Any,
51
+ ) -> list[dict]:
52
+ """Get the book summary for a given instrument."""
53
+ kw.update(params=dict(instrument_name=instrument_name), callback=self.to_result)
29
54
  return cast(
30
55
  list[dict],
31
56
  await self.get_path("public/get_book_summary_by_instrument", **kw),
32
57
  )
33
58
 
34
- async def get_book_summary_by_currency(self, **kw: Any) -> list[dict]:
35
- kw.update(callback=self.to_result)
59
+ async def get_book_summary_by_currency(
60
+ self, currency: str, kind: InstrumentKind | None = None, **kw: Any
61
+ ) -> list[dict]:
62
+ """Get the book summary for a given currency."""
63
+ kw.update(
64
+ params=compact_dict(currency=currency, kind=kind), callback=self.to_result
65
+ )
36
66
  return cast(
37
67
  list[dict], await self.get_path("public/get_book_summary_by_currency", **kw)
38
68
  )
39
69
 
40
- async def get_instruments(self, **kw: Any) -> list[dict]:
41
- kw.update(callback=self.to_result)
70
+ async def get_instruments(
71
+ self,
72
+ currency: str,
73
+ kind: InstrumentKind | None = None,
74
+ expired: bool | None = None,
75
+ **kw: Any,
76
+ ) -> list[dict]:
77
+ """Get the list of instruments for a given currency."""
78
+ kw.update(
79
+ params=compact_dict(currency=currency, kind=kind, expired=expired),
80
+ callback=self.to_result,
81
+ )
42
82
  return cast(list[dict], await self.get_path("public/get_instruments", **kw))
43
83
 
44
- async def get_volatility(self, **kw: Any) -> pd.DataFrame:
45
- kw.update(callback=self.to_df)
84
+ async def get_volatility(self, currency: str, **kw: Any) -> pd.DataFrame:
85
+ """Provides information about historical volatility for given cryptocurrency"""
86
+ kw.update(params=dict(currency=currency), callback=self.to_df)
46
87
  return await self.get_path("public/get_historical_volatility", **kw)
47
88
 
48
- 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:
49
96
  """Create a :class:`.VolSurfaceLoader` for a given crypto-currency"""
50
- 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
+ )
51
102
  futures = await self.get_book_summary_by_currency(
52
- params=dict(currency=currency, kind="future")
103
+ currency=currency, kind=InstrumentKind.future
53
104
  )
54
105
  options = await self.get_book_summary_by_currency(
55
- params=dict(currency=currency, kind="option")
106
+ currency=currency, kind=InstrumentKind.option
56
107
  )
57
- instruments = await self.get_instruments(params=dict(currency=currency))
108
+ instruments = await self.get_instruments(currency=currency)
58
109
  instrument_map = {i["instrument_name"]: i for i in instruments}
59
110
  min_tick_size = Decimal("inf")
60
- for future in futures:
61
- if (bid_ := future["bid_price"]) and (ask_ := future["ask_price"]):
62
- 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"]
63
114
  meta = instrument_map[name]
64
115
  tick_size = to_decimal(meta["tick_size"])
65
116
  min_tick_size = min(min_tick_size, tick_size)
@@ -70,8 +121,8 @@ class Deribit(AioHttpClient):
70
121
  VolSecurityType.spot,
71
122
  bid=bid,
72
123
  ask=ask,
73
- open_interest=int(future["open_interest"]),
74
- volume=int(future["volume_usd"]),
124
+ open_interest=to_decimal(entry["open_interest"]),
125
+ volume=to_decimal(entry["volume_usd"]),
75
126
  )
76
127
  else:
77
128
  maturity = pd.to_datetime(
@@ -84,15 +135,15 @@ class Deribit(AioHttpClient):
84
135
  maturity=maturity,
85
136
  bid=bid,
86
137
  ask=ask,
87
- open_interest=int(future["open_interest"]),
88
- volume=int(future["volume_usd"]),
138
+ open_interest=to_decimal(entry["open_interest"]),
139
+ volume=to_decimal(entry["volume_usd"]),
89
140
  )
90
141
  loader.tick_size_forwards = min_tick_size
91
142
 
92
143
  min_tick_size = Decimal("inf")
93
- for option in options:
94
- if (bid_ := option["bid_price"]) and (ask_ := option["ask_price"]):
95
- 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"]
96
147
  meta = instrument_map[name]
97
148
  tick_size = to_decimal(meta["tick_size"])
98
149
  min_tick_size = min(min_tick_size, tick_size)
@@ -104,9 +155,15 @@ class Deribit(AioHttpClient):
104
155
  unit="ms",
105
156
  utc=True,
106
157
  ).to_pydatetime(),
107
- call=meta["option_type"] == "call",
158
+ option_type=(
159
+ OptionType.call
160
+ if meta["option_type"] == "call"
161
+ else OptionType.put
162
+ ),
108
163
  bid=round_to_step(bid_, tick_size),
109
164
  ask=round_to_step(ask_, tick_size),
165
+ open_interest=to_decimal(entry["open_interest"]),
166
+ volume=to_decimal(entry["volume_usd"]),
110
167
  )
111
168
  loader.tick_size_options = min_tick_size
112
169
  return loader
@@ -6,12 +6,7 @@ import numpy as np
6
6
  import pandas as pd
7
7
  from fluid.utils.http_client import AioHttpClient
8
8
 
9
- URL = (
10
- "https://www.federalreserve.gov/datadownload/Output.aspx?"
11
- "rel=H15&series=bf17364827e38702b42a58cf8eaa3f78&lastobs=&"
12
- )
13
-
14
- maturities = [
9
+ MATURITIES = (
15
10
  "month_1",
16
11
  "month_3",
17
12
  "month_6",
@@ -23,7 +18,7 @@ maturities = [
23
18
  "year_10",
24
19
  "year_20",
25
20
  "year_30",
26
- ]
21
+ )
27
22
 
28
23
 
29
24
  @dataclass
@@ -52,7 +47,7 @@ class FederalReserve(AioHttpClient):
52
47
  params.update(series="bf17364827e38702b42a58cf8eaa3f78", rel="H15")
53
48
  data = await self._get_text(params)
54
49
  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
56
51
  df = df.set_index("date").replace("ND", np.nan)
57
52
  return df.dropna(axis=0, how="all").reset_index()
58
53
 
@@ -0,0 +1,42 @@
1
+ from dataclasses import dataclass
2
+ from datetime import date, timedelta
3
+
4
+ import pandas as pd
5
+ from fluid.utils.http_client import AioHttpClient
6
+
7
+ from quantflow.utils.dates import as_date
8
+
9
+
10
+ @dataclass
11
+ class FiscalData(AioHttpClient):
12
+ """Fiscal Data API client.
13
+
14
+ THis class is used to fetch data from the
15
+ [fiscal data api](https://fiscaldata.treasury.gov/api-documentation/)
16
+ """
17
+
18
+ url: str = "https://api.fiscaldata.treasury.gov/services/api/fiscal_service"
19
+
20
+ async def securities(self, record_date: date | None = None) -> pd.DataFrame:
21
+ """Get treasury constant maturities rates"""
22
+ rd = as_date(record_date)
23
+ pm = rd.replace(day=1) - timedelta(days=1)
24
+ params = {"filter": f"record_date:eq:{pm.isoformat()}"}
25
+ data = await self.get_all("/v1/debt/mspd/mspd_table_3_market", params)
26
+ return pd.DataFrame(data)
27
+
28
+ async def get_all(self, path: str, params: dict[str, str]) -> list:
29
+ """Get all data from the API"""
30
+ next_url: str | None = f"{self.url}{path}"
31
+ full_data = []
32
+ while next_url:
33
+ payload = await self.get(next_url, params=params)
34
+ full_data.extend(payload["data"])
35
+ if links := payload.get("links"):
36
+ if next_path := links.get("next"):
37
+ next_url = f"{self.url}{next_path}"
38
+ else:
39
+ next_url = None
40
+ else:
41
+ next_url = None
42
+ return full_data