mirror of
https://github.com/shawnkim1997/All-in-one-Financial-Analysis.git
synced 2026-08-18 12:58:07 +00:00
67 lines
2.6 KiB
Python
67 lines
2.6 KiB
Python
"""YahooQuery provider for the ATLAS Data Gateway."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from server.core.data_gateway import Profile, Quote
|
|
from server.core.providers.base import BaseProvider, ProviderError
|
|
|
|
|
|
class YahooQueryProvider(BaseProvider):
|
|
name = "yahooquery"
|
|
|
|
def _ticker(self, symbol: str) -> Any:
|
|
try:
|
|
from yahooquery import Ticker
|
|
except Exception as exc: # pragma: no cover - dependency is expected in app runtime
|
|
raise ProviderError(f"yahooquery import failed: {exc}") from exc
|
|
return Ticker(symbol.strip().upper())
|
|
|
|
async def quote(self, symbol: str) -> Quote:
|
|
def fetch() -> Quote:
|
|
normalized = symbol.strip().upper()
|
|
data = self._ticker(normalized).price
|
|
row = data.get(normalized) if isinstance(data, dict) else None
|
|
if not isinstance(row, dict):
|
|
raise ProviderError("missing price payload")
|
|
price = row.get("regularMarketPrice") or row.get("postMarketPrice")
|
|
prev = row.get("regularMarketPreviousClose")
|
|
change = (price - prev) if isinstance(price, (int, float)) and isinstance(prev, (int, float)) else None
|
|
change_pct = (change / prev * 100) if change is not None and prev else None
|
|
return Quote(
|
|
symbol=normalized,
|
|
price=float(price) if isinstance(price, (int, float)) else None,
|
|
currency=row.get("currency"),
|
|
change=change,
|
|
change_pct=change_pct,
|
|
source=self.name,
|
|
raw=row,
|
|
)
|
|
|
|
return await self._to_thread(fetch)
|
|
|
|
async def profile(self, symbol: str) -> Profile:
|
|
def fetch() -> Profile:
|
|
normalized = symbol.strip().upper()
|
|
ticker = self._ticker(normalized)
|
|
profiles = ticker.asset_profile
|
|
row = profiles.get(normalized) if isinstance(profiles, dict) else None
|
|
if not isinstance(row, dict):
|
|
raise ProviderError("missing asset_profile payload")
|
|
return Profile(
|
|
symbol=normalized,
|
|
name=row.get("longName") or row.get("shortName"),
|
|
description=row.get("longBusinessSummary"),
|
|
sector=row.get("sector"),
|
|
industry=row.get("industry"),
|
|
country=row.get("country"),
|
|
exchange=row.get("exchange"),
|
|
website=row.get("website"),
|
|
employees=row.get("fullTimeEmployees"),
|
|
source=self.name,
|
|
raw=row,
|
|
)
|
|
|
|
return await self._to_thread(fetch)
|