Skip to content

Commit 734935e

Browse files
committed
Updated fmp_client.py file
1 parent b0b52c5 commit 734935e

13 files changed

Lines changed: 204 additions & 88 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Python-generated files
22
__pycache__/
3+
.mypy_cache
34
*.py[cod]
45
*$py.class
56
*.so

pyproject.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ authors = [
88
]
99
requires-python = ">=3.12"
1010
dependencies = [
11+
"asyncio>=3.4.3",
1112
"dash>=2.18.2",
1213
"duckdb>=1.1.3",
1314
"fastapi>=0.115.4",
@@ -33,3 +34,6 @@ dev = [
3334
"pytest>=8.3.3",
3435
"ruff>=0.7.3",
3536
]
37+
38+
[tool.setuptools]
39+
package-dir = {"" = "src"}
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
api:
22
base_url: "https://financialmodelingprep.com/api/v3"
3-
annual_ratios: "ratios/"
4-
annual_financial_growth: "financial-growth/"
5-
rating: "rating/"
3+
annual_ratios: "ratios"
4+
annual_financial_growth: "financial-growth"
5+
rating: "rating"
66

77
valuation:
88
default_pe_ratio: 15

src/main.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import asyncio
2+
from stock_valuation_app.data import fmp_client
3+
4+
async def main() -> None:
5+
"""Main function to fetch data from the Financial Modeling Prep API."""
6+
api_client = fmp_client.FMPClient()
7+
rating_endpoint = fmp_client.get_endpoint("annual_ratios")
8+
data = await api_client.fetch_data(rating_endpoint, "AAPL")
9+
print(data)
10+
11+
12+
if __name__ == "__main__":
13+
asyncio.run(main())
Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +0,0 @@
1-
from fastapi import FastAPI
2-
from .api.routes import router
3-
from .ui.dashboard import app as dash_app
4-
5-
app = FastAPI()
6-
app.include_router(router)
7-
8-
# Mount Dash app
9-
app.mount("/dashboard", dash_app.server)

src/stock_valuation_app/api/__init__.py

Whitespace-only changes.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from fastapi import APIRouter, Depends
2+
from stock_valuation_app.data.fmp_client import FMPClient
Lines changed: 52 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,63 @@
1+
import os
2+
import asyncio
13
from dataclasses import dataclass, field
2-
from urllib.parse import urljoin
4+
from typing import Any
35
import httpx
4-
from typing import Any, Optional
5-
from pydantic import BaseModel
6+
import utils
7+
from stock_valuation_app.models.stock import CombinedModel
8+
9+
10+
def get_endpoint(url: str) -> str:
11+
"""Get endpoints from the configuration."""
12+
api_config = utils.get_section_config("api")
13+
return api_config.get(f"{url}")
14+
15+
16+
def get_base_url() -> str:
17+
"""Get the base URL from the configuration."""
18+
return get_endpoint("base_url")
19+
20+
21+
def get_api_key() -> str:
22+
"""Get the API key from the .env file."""
23+
return os.getenv("FMP_API_KEY", "")
624

725

826
@dataclass
927
class FMPClient:
1028
"""A client for interacting with the Financial Modeling Prep API."""
11-
base_url: str = field(default_factory=utils.get_base_url)
12-
api_key: str = field(default_factory=utils.get_api_key)
29+
base_url: str = field(default_factory=get_base_url)
30+
api_key: str = field(default_factory=get_api_key)
31+
metric_types: list[str] = field(default_factory=lambda: ["profile", "rating", "ratios", "key-metrics", "financial-growth",])
1332

33+
async def get_data(self, client: httpx.Client, url: str) -> dict[str, Any]:
34+
"""Call API endpoint asynchronously"""
35+
response = await client.get(url)
36+
data = response.json()
37+
return data
1438

15-
async def fetch_data(self, endpoint: str, symbol: str, period: Optional[str] = "annual"): # params: dict[str, Any]
16-
"""Fetch data from the Financial Modeling Prep API."""
17-
base_endpoint = urljoin(self.base_url, endpoint)
18-
if period is None:
19-
url = f"{base_endpoint}/{symbol}?apikey={self.api_key}"
20-
else:
21-
url = f"{base_endpoint}/{symbol}?period={period}&apikey={self.api_key}"
39+
async def fetch_data(self, ticker: str) -> dict[str, list[dict[str, Any]]]:
40+
urls = []
41+
for metric in self.metric_types:
42+
if metric in ["profile", "rating"]:
43+
endpoint = f"{self.base_url}/{metric}/{ticker}?apikey={self.api_key}"
44+
else:
45+
endpoint = f"{self.base_url}/{metric}/{ticker}?period=annual&apikey={self.api_key}"
46+
urls.append(endpoint)
2247

2348
async with httpx.AsyncClient() as client:
24-
response = await client.get(url)
25-
response.raise_for_status()
26-
data = response.json()
27-
return data
49+
tasks = []
50+
for url in urls:
51+
tasks.append(asyncio.create_task(self.get_data(client, url)))
52+
53+
results = await asyncio.gather(*tasks)
54+
55+
# Rename some metric types to match with fields defined in the CombinedModel
56+
replace_metric_types = {"key-metrics": "key_metrics", "financial-growth": "growth",}
57+
new_metric_types = [replace_metric_types.get(item, item) for item in self.metric_types]
58+
59+
# Create combined records dict for validation
60+
records = dict(zip(new_metric_types, results))
61+
62+
# Validate the combined records
63+
return CombinedModel(**records).model_dump()

src/stock_valuation_app/main.py

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

src/stock_valuation_app/models/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)