trading/shared/fundamentals/rotating.py

50 lines
1.8 KiB
Python

"""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