feat: add fundamental data providers (Alpha Vantage, FMP, Yahoo Finance) with rotation

This commit is contained in:
Viktor Barzin 2026-02-23 21:41:16 +00:00
parent 2398e8faf6
commit aa47e896dd
No known key found for this signature in database
GPG key ID: 0EB088298288D958
7 changed files with 704 additions and 0 deletions

View file

@ -0,0 +1,50 @@
"""Rotating (fallback) wrapper over multiple fundamental data providers."""
from __future__ import annotations
import logging
from shared.fundamentals.base import FundamentalsProvider, FundamentalsSnapshot
logger = logging.getLogger(__name__)
class RotatingProvider(FundamentalsProvider):
"""Tries each provider in order and returns the first successful result.
If a provider raises an exception it is caught, logged, and the next
provider is attempted. Returns ``None`` only when every provider either
returned ``None`` or raised.
"""
def __init__(self, providers: list[FundamentalsProvider]) -> None:
if not providers:
raise ValueError("RotatingProvider requires at least one provider")
self._providers = providers
async def fetch(self, ticker: str) -> FundamentalsSnapshot | None:
"""Try each provider in sequence; return the first non-None result."""
for provider in self._providers:
provider_name = type(provider).__name__
try:
result = await provider.fetch(ticker)
if result is not None:
logger.info(
"Provider %s succeeded for %s",
provider_name,
ticker,
)
return result
logger.debug(
"Provider %s returned None for %s, trying next",
provider_name,
ticker,
)
except Exception:
logger.exception(
"Provider %s raised for %s, trying next",
provider_name,
ticker,
)
logger.warning("All providers exhausted for %s", ticker)
return None