确认PokieTicker新闻库数据源

This commit is contained in:
2026-03-16 02:19:25 +08:00
parent 78f133617f
commit 564c92c0c8
182 changed files with 6436 additions and 1050 deletions

View File

@@ -30,6 +30,25 @@ logger = logging.getLogger(__name__)
_DATA_DIR = Path(__file__).parent / "ret_data"
def _format_provider_error(exc: Exception) -> str:
"""Condense common provider failures into short, readable messages."""
message = str(exc).strip().replace("\n", " ")
if "429" in message:
return "rate limit reached"
if "402" in message:
return "insufficient credits"
if "422" in message or "Missing parameters" in message:
return "invalid request parameters"
if "Quote not found" in message:
return "quote not found"
return message
def _has_valid_ticker(ticker: str) -> bool:
"""Return whether the normalized ticker is non-empty."""
return bool((ticker or "").strip())
class DataProviderRouter:
"""Route data requests across configured providers with fallbacks."""
@@ -56,6 +75,8 @@ class DataProviderRouter:
end_date: str,
) -> tuple[list[Price], DataSource]:
"""Fetch prices using preferred providers with fallback."""
if not _has_valid_ticker(ticker):
return [], "local_csv"
last_error: Optional[Exception] = None
for source in self.price_sources():
@@ -78,7 +99,12 @@ class DataProviderRouter:
return prices, source
except Exception as exc:
last_error = exc
logger.warning("Price source %s failed for %s: %s", source, ticker, exc)
logger.warning(
"Price source %s failed for %s: %s",
source,
ticker,
_format_provider_error(exc),
)
if last_error:
raise last_error
@@ -92,6 +118,8 @@ class DataProviderRouter:
limit: int = 10,
) -> tuple[list[FinancialMetrics], DataSource]:
"""Fetch financial metrics with API provider fallback."""
if not _has_valid_ticker(ticker):
return [], "local_csv"
last_error: Optional[Exception] = None
for source in self.api_sources():
@@ -126,7 +154,7 @@ class DataProviderRouter:
"Financial metrics source %s failed for %s: %s",
source,
ticker,
exc,
_format_provider_error(exc),
)
if last_error:
@@ -142,6 +170,8 @@ class DataProviderRouter:
limit: int = 10,
) -> list[LineItem]:
"""Line items are only supported via Financial Datasets."""
if not _has_valid_ticker(ticker):
return []
if "financial_datasets" not in self.api_sources():
return []
try:
@@ -155,7 +185,11 @@ class DataProviderRouter:
self._record_success("line_items", "financial_datasets")
return results
except Exception as exc:
logger.warning("Line items source failed for %s: %s", ticker, exc)
logger.warning(
"Line items source failed for %s: %s",
ticker,
_format_provider_error(exc),
)
return []
def get_insider_trades(
@@ -166,6 +200,8 @@ class DataProviderRouter:
limit: int = 1000,
) -> tuple[list[InsiderTrade], DataSource]:
"""Fetch insider trades with provider fallback."""
if not _has_valid_ticker(ticker):
return [], "local_csv"
last_error: Optional[Exception] = None
for source in self.api_sources():
@@ -193,7 +229,7 @@ class DataProviderRouter:
"Insider trades source %s failed for %s: %s",
source,
ticker,
exc,
_format_provider_error(exc),
)
if last_error:
@@ -208,6 +244,8 @@ class DataProviderRouter:
limit: int = 1000,
) -> tuple[list[CompanyNews], DataSource]:
"""Fetch company news with provider fallback."""
if not _has_valid_ticker(ticker):
return [], "local_csv"
last_error: Optional[Exception] = None
for source in self.api_sources():
@@ -244,7 +282,7 @@ class DataProviderRouter:
"Company news source %s failed for %s: %s",
source,
ticker,
exc,
_format_provider_error(exc),
)
if last_error:
@@ -258,6 +296,8 @@ class DataProviderRouter:
metrics_lookup,
) -> tuple[Optional[float], DataSource]:
"""Fetch market cap using facts API or financial metrics fallback."""
if not _has_valid_ticker(ticker):
return None, "local_csv"
today = datetime.datetime.now().strftime("%Y-%m-%d")
if end_date == today and "financial_datasets" in self.api_sources():
try:
@@ -267,7 +307,7 @@ class DataProviderRouter:
logger.warning(
"Market cap facts source failed for %s: %s",
ticker,
exc,
_format_provider_error(exc),
)
metrics, source = metrics_lookup(ticker, end_date)