quantflow 0.3.2__tar.gz → 0.3.3__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 (55) hide show
  1. {quantflow-0.3.2 → quantflow-0.3.3}/PKG-INFO +1 -1
  2. {quantflow-0.3.2 → quantflow-0.3.3}/pyproject.toml +11 -13
  3. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/__init__.py +1 -1
  4. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/cli/commands/crypto.py +31 -3
  5. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/cli/commands/stocks.py +14 -0
  6. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/data/deribit.py +48 -13
  7. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/data/fed.py +3 -8
  8. quantflow-0.3.3/quantflow/data/fiscal_data.py +42 -0
  9. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/data/fmp.py +37 -28
  10. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/utils/dates.py +9 -0
  11. {quantflow-0.3.2 → quantflow-0.3.3}/LICENSE +0 -0
  12. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/cli/__init__.py +0 -0
  13. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/cli/app.py +0 -0
  14. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/cli/commands/__init__.py +0 -0
  15. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/cli/commands/base.py +0 -0
  16. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/cli/commands/fred.py +0 -0
  17. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/cli/commands/vault.py +0 -0
  18. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/cli/script.py +0 -0
  19. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/cli/settings.py +0 -0
  20. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/data/__init__.py +0 -0
  21. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/data/fred.py +0 -0
  22. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/data/vault.py +0 -0
  23. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/options/__init__.py +0 -0
  24. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/options/bs.py +0 -0
  25. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/options/calibration.py +0 -0
  26. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/options/inputs.py +0 -0
  27. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/options/pricer.py +0 -0
  28. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/options/surface.py +0 -0
  29. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/py.typed +0 -0
  30. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/sp/__init__.py +0 -0
  31. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/sp/base.py +0 -0
  32. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/sp/bns.py +0 -0
  33. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/sp/cir.py +0 -0
  34. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/sp/copula.py +0 -0
  35. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/sp/dsp.py +0 -0
  36. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/sp/heston.py +0 -0
  37. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/sp/jump_diffusion.py +0 -0
  38. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/sp/ou.py +0 -0
  39. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/sp/poisson.py +0 -0
  40. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/sp/weiner.py +0 -0
  41. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/ta/__init__.py +0 -0
  42. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/ta/base.py +0 -0
  43. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/ta/ohlc.py +0 -0
  44. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/ta/paths.py +0 -0
  45. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/utils/__init__.py +0 -0
  46. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/utils/bins.py +0 -0
  47. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/utils/distributions.py +0 -0
  48. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/utils/functions.py +0 -0
  49. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/utils/interest_rates.py +0 -0
  50. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/utils/marginal.py +0 -0
  51. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/utils/numbers.py +0 -0
  52. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/utils/plot.py +0 -0
  53. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/utils/transforms.py +0 -0
  54. {quantflow-0.3.2 → quantflow-0.3.3}/quantflow/utils/types.py +0 -0
  55. {quantflow-0.3.2 → quantflow-0.3.3}/readme.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: quantflow
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: quantitative analysis
5
5
  License: BSD-3-Clause
6
6
  Author: Luca
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "quantflow"
3
- version = "0.3.2"
3
+ version = "0.3.3"
4
4
  description = "quantitative analysis"
5
5
  authors = ["Luca <luca@quantmind.com>"]
6
6
  license = "BSD-3-Clause"
@@ -17,21 +17,21 @@ scipy = "^1.14.1"
17
17
  pydantic = "^2.0.2"
18
18
  ccy = { version = "^1.7.1" }
19
19
  python-dotenv = "^1.0.1"
20
- polars = {version = "^1.11.0", extras=["pandas", "pyarrow"]}
20
+ polars = { version = "^1.11.0", extras = ["pandas", "pyarrow"] }
21
21
  asciichartpy = { version = "^1.5.25", optional = true }
22
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}
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 }
28
28
 
29
29
  [tool.poetry.group.dev.dependencies]
30
30
  black = "^25.1.0"
31
31
  pytest-cov = "^6.0.0"
32
32
  mypy = "^1.14.1"
33
33
  ghp-import = "^2.0.2"
34
- ruff = "^0.11.2"
34
+ ruff = "^0.11.12"
35
35
  pytest-asyncio = "^0.26.0"
36
36
  isort = "^6.0.1"
37
37
 
@@ -44,7 +44,7 @@ cli = [
44
44
  "prompt-toolkit",
45
45
  "rich",
46
46
  "click",
47
- "holidays"
47
+ "holidays",
48
48
  ]
49
49
 
50
50
  [tool.poetry.group.book]
@@ -74,9 +74,7 @@ formats = "ipynb,myst"
74
74
 
75
75
  [tool.pytest.ini_options]
76
76
  asyncio_mode = "auto"
77
- testpaths = [
78
- "quantflow_tests"
79
- ]
77
+ testpaths = ["quantflow_tests"]
80
78
 
81
79
  [tool.isort]
82
80
  profile = "black"
@@ -102,7 +100,7 @@ module = [
102
100
  "IPython.*",
103
101
  "pandas.*",
104
102
  "plotly.*",
105
- "scipy.*"
103
+ "scipy.*",
106
104
  ]
107
105
  ignore_missing_imports = true
108
106
  disallow_untyped_defs = false
@@ -1,3 +1,3 @@
1
1
  """Quantitative analysis and pricing"""
2
2
 
3
- __version__ = "0.3.2"
3
+ __version__ = "0.3.3"
@@ -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,9 +1,12 @@
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
 
9
12
  from quantflow.options.surface import VolSecurityType, VolSurfaceLoader
@@ -14,47 +17,79 @@ def parse_maturity(v: str) -> datetime:
14
17
  return parse(v).replace(tzinfo=timezone.utc, hour=8)
15
18
 
16
19
 
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
17
31
  class Deribit(AioHttpClient):
18
32
  """Deribit API client
19
33
 
20
- Fetch market and static data from `Deribit`_.
34
+ Fetch market and static data from `Deribit`_ API.
21
35
 
22
36
  .. _Deribit: https://docs.deribit.com/
23
37
  """
24
38
 
25
- url = "https://www.deribit.com/api/v2"
39
+ url: str = "https://www.deribit.com/api/v2"
26
40
 
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)
29
48
  return cast(
30
49
  list[dict],
31
50
  await self.get_path("public/get_book_summary_by_instrument", **kw),
32
51
  )
33
52
 
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
+ )
36
60
  return cast(
37
61
  list[dict], await self.get_path("public/get_book_summary_by_currency", **kw)
38
62
  )
39
63
 
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
+ )
42
76
  return cast(list[dict], await self.get_path("public/get_instruments", **kw))
43
77
 
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)
46
81
  return await self.get_path("public/get_historical_volatility", **kw)
47
82
 
48
83
  async def volatility_surface_loader(self, currency: str) -> VolSurfaceLoader:
49
84
  """Create a :class:`.VolSurfaceLoader` for a given crypto-currency"""
50
85
  loader = VolSurfaceLoader()
51
86
  futures = await self.get_book_summary_by_currency(
52
- params=dict(currency=currency, kind="future")
87
+ currency=currency, kind=InstrumentKind.future
53
88
  )
54
89
  options = await self.get_book_summary_by_currency(
55
- params=dict(currency=currency, kind="option")
90
+ currency=currency, kind=InstrumentKind.option
56
91
  )
57
- instruments = await self.get_instruments(params=dict(currency=currency))
92
+ instruments = await self.get_instruments(currency=currency)
58
93
  instrument_map = {i["instrument_name"]: i for i in instruments}
59
94
  min_tick_size = Decimal("inf")
60
95
  for future in futures:
@@ -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
@@ -20,10 +20,10 @@ class FMP(AioHttpClient):
20
20
 
21
21
  Fetch market and financial data from `Financial Modeling Prep`_.
22
22
 
23
- .. _Financial Modeling Prep: https://financialmodelingprep.com/developer/docs/
23
+ .. _Financial Modeling Prep: https://site.financialmodelingprep.com/developer/docs/stable
24
24
  """
25
25
 
26
- url: str = "https://financialmodelingprep.com/api"
26
+ url: str = "https://financialmodelingprep.com/stable"
27
27
  key: str = field(default_factory=lambda: os.environ.get("FMP_API_KEY", ""))
28
28
 
29
29
  class freq(StrEnum):
@@ -37,22 +37,28 @@ class FMP(AioHttpClient):
37
37
  four_hour = "4hour"
38
38
  daily = ""
39
39
 
40
+ async def market_risk_premium(self) -> list[dict]:
41
+ """Market risk premium"""
42
+ return await self.get_path("market-risk-premium")
43
+
40
44
  async def stocks(self, **kw: Any) -> list[dict]:
41
- return await self.get_path("v3/stock/list", **kw)
45
+ return await self.get_path("stock-list", **kw)
42
46
 
43
47
  async def etfs(self, **kw: Any) -> list[dict]:
44
- return await self.get_path("v3/etf/list", **kw)
48
+ return await self.get_path("etf-list", **kw)
45
49
 
46
50
  async def indices(self, **kw: Any) -> list[dict]:
47
- return await self.get_path("v3/quotes/index", **kw)
51
+ """Retrieve a comprehensive list of stock market indexes
52
+ across global exchanges"""
53
+ return await self.get_path("index-list", **kw)
48
54
 
49
55
  async def profile(self, *tickers: str, **kw: Any) -> list[dict]:
50
56
  """Company profile - minute"""
51
- return await self.get_path(f"v3/profile/{self.join(*tickers)}", **kw)
57
+ return await self.get_path(f"profile/{self.join(*tickers)}", **kw)
52
58
 
53
59
  async def quote(self, *tickers: str, **kw: Any) -> list[dict]:
54
60
  """Company quote - real time"""
55
- return await self.get_path(f"v3/quote/{self.join(*tickers)}", **kw)
61
+ return await self.get_path(f"quote/{self.join(*tickers)}", **kw)
56
62
 
57
63
  # calendars
58
64
 
@@ -68,28 +74,28 @@ class FMP(AioHttpClient):
68
74
  if not to_date:
69
75
  to_date = date.today() + timedelta(days=7)
70
76
  params = {"from": isoformat(from_date), "to": isoformat(to_date)}
71
- return await self.get_path("v3/stock_dividend_calendar", params=params, **kw)
77
+ return await self.get_path("stock_dividend_calendar", params=params, **kw)
72
78
 
73
79
  # Executives
74
80
 
75
81
  async def executives(self, ticker: str, **kw: Any) -> list[dict]:
76
82
  """Company quote - real time"""
77
- return await self.get_path(f"v3/key-executives/{ticker}", **kw)
83
+ return await self.get_path(f"key-executives/{ticker}", **kw)
78
84
 
79
85
  async def insider_trading(self, ticker: str, **kw: Any) -> list[dict]:
80
86
  """Company Insider Trading"""
81
87
  return await self.get_path(
82
- "v4/insider-trading", **self.params(dict(symbol=ticker), **kw)
88
+ "insider-trading", **self.params(dict(symbol=ticker), **kw)
83
89
  )
84
90
 
85
91
  # Rating
86
92
 
87
93
  async def rating(self, ticker: str, **kw: Any) -> list[dict]:
88
94
  """Company rating - real time"""
89
- return await self.get_path(f"v3/rating/{ticker}", **kw)
95
+ return await self.get_path(f"rating/{ticker}", **kw)
90
96
 
91
97
  async def etf_holders(self, ticker: str, **kw: Any) -> list[dict]:
92
- return await self.get_path(f"v3/etf-holder/{ticker}", **kw)
98
+ return await self.get_path(f"etf-holder/{ticker}", **kw)
93
99
 
94
100
  async def ratios(
95
101
  self,
@@ -102,7 +108,7 @@ class FMP(AioHttpClient):
102
108
  the trailing 12 months"""
103
109
  path = "ratios" if period else "ratios-ttm"
104
110
  return await self.get_path(
105
- f"v3/{path}/{ticker}",
111
+ f"{path}/{ticker}",
106
112
  **self.params(compact_dict(period=period, limit=limit), **kw),
107
113
  )
108
114
 
@@ -110,14 +116,14 @@ class FMP(AioHttpClient):
110
116
  """Stock peers based on sector, exchange and market cap"""
111
117
  kwargs = self.params(**kw)
112
118
  kwargs["params"]["symbol"] = self.join(*tickers)
113
- return await self.get_path("v4/stock_peers", **kwargs)
119
+ return await self.get_path("stock_peers", **kwargs)
114
120
 
115
121
  async def news(self, *tickers: str, **kw: Any) -> list[dict]:
116
122
  """Company quote - real time"""
117
123
  kwargs = self.params(**kw)
118
124
  if tickers:
119
125
  kwargs["params"]["tickers"] = self.join(*tickers)
120
- return await self.get_path("v3/stock_news", **kwargs)
126
+ return await self.get_path("stock_news", **kwargs)
121
127
 
122
128
  async def search(
123
129
  self,
@@ -125,10 +131,10 @@ class FMP(AioHttpClient):
125
131
  *,
126
132
  exchange: str | None = None,
127
133
  limit: int | None = None,
128
- ticker: bool = False,
134
+ symbol: bool = False,
129
135
  **kw: Any,
130
136
  ) -> list[dict]:
131
- path = "v3/search-ticker" if ticker else "v3/search"
137
+ path = "search-symbol" if symbol else "search-name"
132
138
  return await self.get_path(
133
139
  path,
134
140
  **self.params(
@@ -137,15 +143,20 @@ class FMP(AioHttpClient):
137
143
  )
138
144
 
139
145
  async def prices(
140
- self, ticker: str, frequency: str = "", to_date: bool = False, **kw: Any
146
+ self,
147
+ symbol: str,
148
+ frequency: str = "",
149
+ to_date: bool = False,
150
+ **kw: Any,
141
151
  ) -> pd.DataFrame:
142
152
  """Historical prices, daily if frequency is not provided"""
143
- base = (
144
- "historical-price-full/"
153
+ path = (
154
+ "historical-price-eod/full"
145
155
  if not frequency
146
156
  else f"historical-chart/{frequency}"
147
157
  )
148
- data = await self.get_path(f"v3/{base}/{ticker}", **kw)
158
+ kw.update(params=dict(symbol=symbol))
159
+ data = await self.get_path(path, **kw)
149
160
  if isinstance(data, dict):
150
161
  data = data.get("historical", [])
151
162
  df = pd.DataFrame(data)
@@ -164,25 +175,23 @@ class FMP(AioHttpClient):
164
175
  **kw: Any,
165
176
  ) -> dict | list[dict]:
166
177
  if not from_date:
167
- data = await self.get_path("v3/sectors-performance", params=params, **kw)
178
+ data = await self.get_path("sectors-performance", params=params, **kw)
168
179
  return {d["sector"]: Decimal(d["changesPercentage"][:-1]) for d in data}
169
180
  else:
170
181
  params = params.copy() if params is not None else {}
171
182
  params.update(compact_dict({"from": from_date, "to": to_date}))
172
183
  data = await self.get_path(
173
- "v3/historical-sectors-performance", params=params, **kw
184
+ "historical-sectors-performance", params=params, **kw
174
185
  )
175
186
  ts = [dict(nice_sector_performance(d)) for d in data]
176
187
  return summary_sector_performance(ts) if summary else ts
177
188
 
178
189
  async def sector_pe(self, **kw: Any) -> list[dict]:
179
- return cast(
180
- list[dict], await self.get_path("v4/sector_price_earning_ratio", **kw)
181
- )
190
+ return cast(list[dict], await self.get_path("sector_price_earning_ratio", **kw))
182
191
 
183
192
  # forex
184
193
  async def forex_list(self) -> list[dict]:
185
- return await self.get_path("v3/symbol/available-forex-currency-pairs")
194
+ return await self.get_path("symbol/available-forex-currency-pairs")
186
195
 
187
196
  def historical_frequencies(self) -> dict:
188
197
  return {
@@ -201,7 +210,7 @@ class FMP(AioHttpClient):
201
210
 
202
211
  # Crypto
203
212
  async def crypto_list(self) -> list[dict]:
204
- return await self.get_path("v3/symbol/available-cryptocurrencies")
213
+ return await self.get_path("symbol/available-cryptocurrencies")
205
214
 
206
215
  # Internals
207
216
  async def get_path(self, path: str, **kw: Any) -> list[dict]:
@@ -22,3 +22,12 @@ def isoformat(date: str | date) -> str:
22
22
 
23
23
  def start_of_day(dt: date | None = None) -> datetime:
24
24
  return as_utc(dt).replace(hour=0, minute=0, second=0, microsecond=0)
25
+
26
+
27
+ def as_date(dt: date | None = None) -> date:
28
+ if dt is None:
29
+ return date.today()
30
+ elif isinstance(dt, datetime):
31
+ return dt.date()
32
+ else:
33
+ return dt
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes