aboutsummaryrefslogtreecommitdiff
path: root/services/fmp_service.py
diff options
context:
space:
mode:
Diffstat (limited to 'services/fmp_service.py')
-rw-r--r--services/fmp_service.py81
1 files changed, 59 insertions, 22 deletions
diff --git a/services/fmp_service.py b/services/fmp_service.py
index bf31788..59d8215 100644
--- a/services/fmp_service.py
+++ b/services/fmp_service.py
@@ -1,65 +1,102 @@
-"""Financial Modeling Prep API — ratios, peers, company news."""
+"""Financial Modeling Prep API — stable ratios/metrics + company news."""
import os
import requests
import streamlit as st
from dotenv import load_dotenv
+from services.data_service import get_company_info
load_dotenv()
-BASE_URL = "https://financialmodelingprep.com/api/v3"
+BASE_URL = "https://financialmodelingprep.com"
+STABLE_BASE = f"{BASE_URL}/stable"
+LEGACY_BASE = f"{BASE_URL}/api/v3"
def _api_key() -> str:
- key = os.getenv("FMP_API_KEY", "")
- return key
+ return os.getenv("FMP_API_KEY", "")
-def _get(endpoint: str, params: dict = None) -> dict | list | None:
+def _get(base_url: str, endpoint: str, params: dict | None = None) -> dict | list | None:
params = params or {}
params["apikey"] = _api_key()
try:
- resp = requests.get(f"{BASE_URL}{endpoint}", params=params, timeout=10)
+ resp = requests.get(f"{base_url}{endpoint}", params=params, timeout=10)
resp.raise_for_status()
return resp.json()
except Exception:
return None
+def _apply_yfinance_ratio_fallbacks(ticker: str, merged: dict) -> dict:
+ info = get_company_info(ticker)
+ if not info:
+ return merged
+
+ fallback_map = {
+ "peRatioTTM": info.get("trailingPE"),
+ "priceToSalesRatioTTM": info.get("priceToSalesTrailing12Months"),
+ "priceToBookRatioTTM": info.get("priceToBook"),
+ "enterpriseValueMultipleTTM": info.get("enterpriseToEbitda"),
+ "evToEBITDATTM": info.get("enterpriseToEbitda"),
+ "evToSalesTTM": info.get("enterpriseToRevenue"),
+ "grossProfitMarginTTM": info.get("grossMargins"),
+ "operatingProfitMarginTTM": info.get("operatingMargins"),
+ "netProfitMarginTTM": info.get("profitMargins"),
+ "returnOnEquityTTM": info.get("returnOnEquity"),
+ "returnOnAssetsTTM": info.get("returnOnAssets"),
+ "debtToEquityRatioTTM": info.get("debtToEquity"),
+ "currentRatioTTM": info.get("currentRatio"),
+ "quickRatioTTM": info.get("quickRatio"),
+ "dividendYieldTTM": info.get("dividendYield"),
+ "dividendPayoutRatioTTM": info.get("payoutRatio"),
+ }
+
+ for key, value in fallback_map.items():
+ if merged.get(key) is None and value is not None:
+ merged[key] = value
+
+ return merged
+
+
@st.cache_data(ttl=3600)
def get_key_ratios(ticker: str) -> dict:
- """Return latest TTM key ratios."""
- data = _get(f"/ratios-ttm/{ticker.upper()}")
- if data and isinstance(data, list) and len(data) > 0:
- return data[0]
- return {}
+ """Return merged stable TTM ratios + key metrics for a ticker, with yfinance fallbacks."""
+ ticker = ticker.upper()
+ ratios = _get(STABLE_BASE, "/ratios-ttm", params={"symbol": ticker})
+ metrics = _get(STABLE_BASE, "/key-metrics-ttm", params={"symbol": ticker})
+
+ merged = {"symbol": ticker}
+ if isinstance(ratios, list) and ratios:
+ merged.update(ratios[0])
+ if isinstance(metrics, list) and metrics:
+ merged.update(metrics[0])
+
+ merged = _apply_yfinance_ratio_fallbacks(ticker, merged)
+ return merged if len(merged) > 1 else {}
@st.cache_data(ttl=21600)
def get_peers(ticker: str) -> list[str]:
- """Return list of comparable ticker symbols."""
- data = _get(f"/stock_peers", params={"symbol": ticker.upper()})
- if data and isinstance(data, list) and len(data) > 0:
- return data[0].get("peersList", [])
+ """Direct FMP peers endpoint was deprecated; peer discovery is handled in UI fallback logic."""
return []
@st.cache_data(ttl=3600)
def get_ratios_for_tickers(tickers: list[str]) -> list[dict]:
- """Return TTM ratios for a list of tickers (for comps table)."""
+ """Return merged TTM ratios/metrics rows for a list of tickers."""
results = []
for t in tickers:
- data = _get(f"/ratios-ttm/{t}")
- if data and isinstance(data, list) and len(data) > 0:
- row = data[0]
- row["symbol"] = t
+ row = get_key_ratios(t)
+ if row:
+ row["symbol"] = t.upper()
results.append(row)
return results
@st.cache_data(ttl=600)
def get_company_news(ticker: str, limit: int = 20) -> list[dict]:
- """Return recent news articles for a ticker."""
- data = _get("/stock_news", params={"tickers": ticker.upper(), "limit": limit})
+ """Return recent news articles for a ticker via legacy endpoint fallback."""
+ data = _get(LEGACY_BASE, "/stock_news", params={"tickers": ticker.upper(), "limit": limit})
if data and isinstance(data, list):
return data
return []