Skip to content

Commit f620669

Browse files
authored
Use aio-fluid (#6)
1 parent 3b6fbb2 commit f620669

File tree

8 files changed

+883
-724
lines changed

8 files changed

+883
-724
lines changed

.github/workflows/build.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
FMP_API_KEY: ${{ secrets.FMP_API_KEY }}
1717
strategy:
1818
matrix:
19-
python-version: ["3.11", "3.12"]
19+
python-version: ["3.11", "3.12", "3.13"]
2020

2121
steps:
2222
- uses: actions/checkout@v4
@@ -27,7 +27,7 @@ jobs:
2727
- name: Install poetry
2828
run: pip install -U pip poetry
2929
- name: Install dependencies no book
30-
run: poetry install
30+
run: poetry install --all-extras
3131
- name: run tests no book
3232
run: make tests
3333
- name: Install dependencies

poetry.lock

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

pyproject.toml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "quantflow"
3-
version = "0.2.7"
3+
version = "0.2.8"
44
description = "quantitative analysis"
55
authors = ["Luca <luca@quantmind.com>"]
66
license = "BSD-3-Clause"
@@ -14,26 +14,26 @@ Documentation = "https://quantmind.github.io/quantflow/"
1414
[tool.poetry.dependencies]
1515
python = ">=3.11"
1616
scipy = "^1.14.1"
17-
aiohttp = {version = "^3.8.1", optional = true}
1817
pydantic = "^2.0.2"
1918
ccy = {version="1.6.0", extras=["cli"]}
2019
asciichart = "^0.1"
2120
python-dotenv = "^1.0.1"
2221
asciichartpy = "^1.5.25"
2322
prompt-toolkit = "^3.0.43"
24-
polars = {version = "1.11.0", extras=["pandas", "pyarrow"]}
23+
polars = {version = "^1.11.0", extras=["pandas", "pyarrow"]}
24+
aio-fluid = {version = "^1.2.1", extras=["http"], optional = true}
2525

2626
[tool.poetry.group.dev.dependencies]
2727
black = "^24.1.1"
28-
pytest-cov = "^5.0.0"
28+
pytest-cov = "^6.0.0"
2929
mypy = "^1.13.0"
3030
ghp-import = "^2.0.2"
31-
ruff = "^0.7.1"
31+
ruff = "^0.8.1"
3232
pytest-asyncio = "^0.24.0"
3333

3434

3535
[tool.poetry.extras]
36-
data = ["aiohttp"]
36+
data = ["aio-fluid"]
3737

3838
[tool.poetry.group.book]
3939
optional = true

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.2.7"
3+
__version__ = "0.2.8"

quantflow/data/client.py

Lines changed: 0 additions & 96 deletions
This file was deleted.

quantflow/data/fmp.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,30 @@
22
from dataclasses import dataclass, field
33
from datetime import date, timedelta
44
from typing import Any, cast
5-
5+
from fluid.utils.http_client import AioHttpClient
6+
from fluid.utils.data import compact_dict
67
import pandas as pd
8+
from enum import StrEnum
79

8-
from ..utils.dates import isoformat
9-
from .client import HttpClient, compact
10+
from quantflow.utils.dates import isoformat
1011

1112

1213
@dataclass
13-
class FMP(HttpClient):
14+
class FMP(AioHttpClient):
1415
url: str = "https://financialmodelingprep.com/api"
1516
key: str = field(default_factory=lambda: os.environ.get("FMP_API_KEY", ""))
1617

18+
class freq(StrEnum):
19+
"""FMP historical frequencies"""
20+
21+
one_min = "1min"
22+
five_min = "5min"
23+
fifteen_min = "15min"
24+
thirty_min = "30min"
25+
one_hour = "1hour"
26+
four_hour = "4hour"
27+
daily = ""
28+
1729
async def stocks(self, **kw: Any) -> list[dict]:
1830
return await self.get_path("v3/stock/list", **kw)
1931

@@ -80,7 +92,7 @@ async def ratios(
8092
path = "ratios" if period else "ratios-ttm"
8193
return await self.get_path(
8294
f"v3/{path}/{ticker}",
83-
**self.params(compact(period=period, limit=limit), **kw),
95+
**self.params(compact_dict(period=period, limit=limit), **kw),
8496
)
8597

8698
async def peers(self, *tickers: str, **kw: Any) -> list[dict]:
@@ -108,12 +120,15 @@ async def search(
108120
path = "v3/search-ticker" if ticker else "v3/search"
109121
return await self.get_path(
110122
path,
111-
**self.params(compact(query=query, exchange=exchange, limit=limit), **kw),
123+
**self.params(
124+
compact_dict(query=query, exchange=exchange, limit=limit), **kw
125+
),
112126
)
113127

114128
async def prices(
115129
self, ticker: str, frequency: str = "", to_date: bool = False, **kw: Any
116130
) -> pd.DataFrame:
131+
"""Historical prices, daily if frequency is not provided"""
117132
base = (
118133
"historical-price-full/"
119134
if not frequency
@@ -146,6 +161,10 @@ def historical_frequencies_annulaized(self) -> dict:
146161
one_year = 525600
147162
return {k: v / one_year for k, v in self.historical_frequencies().items()}
148163

164+
# Crypto
165+
async def crypto_list(self) -> list[dict]:
166+
return await self.get_path("v3/symbol/available-cryptocurrencies")
167+
149168
# Internals
150169
async def get_path(self, path: str, **kw: Any) -> list[dict]:
151170
result = await self.get(f"{self.url}/{path}", **self.params(**kw))

quantflow_tests/conftest.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import dotenv
2+
3+
dotenv.load_dotenv()

quantflow_tests/test_data.py

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,16 @@
1-
from typing import AsyncIterator, cast
1+
from typing import AsyncIterator
22

33
import pytest
44

5-
try:
6-
from quantflow.data.fmp import FMP
7-
except ImportError:
8-
FMP = None # type: ignore
5+
from quantflow.data.fmp import FMP
96

10-
pytestmark = pytest.mark.skipif(
11-
FMP is None or not FMP().key, reason="No FMP API key found"
12-
)
7+
pytestmark = pytest.mark.skipif(not FMP().key, reason="No FMP API key found")
138

149

1510
@pytest.fixture
1611
async def fmp() -> AsyncIterator[FMP]:
1712
async with FMP() as fmp:
18-
yield cast(FMP, fmp)
13+
yield fmp
1914

2015

2116
def test_client(fmp: FMP) -> None:
@@ -24,7 +19,7 @@ def test_client(fmp: FMP) -> None:
2419

2520

2621
async def test_historical(fmp: FMP) -> None:
27-
df = await fmp.prices("BTCUSD")
22+
df = await fmp.prices("BTCUSD", fmp.freq.one_hour)
2823
assert df["close"] is not None
2924

3025

0 commit comments

Comments
 (0)