ZhuLinsen/daily_stock_analysis · warning · DataFetchError
[Finnhub] {stock_code} is not a US stock
Error message
[Finnhub] {stock_code} is not a US stock What it means
FinnhubFetcher serves US equities only; _fetch_raw_data rejects any code failing is_us_stock_code. It is a deliberate capability guard so the failover chain skips Finnhub for A-share/HK/other codes instead of sending a doomed API request.
Source
Thrown at data_provider/finnhub_fetcher.py:45
class FinnhubFetcher(BaseFetcher):
name = "FinnhubFetcher"
priority = 2
def __init__(self):
from src.config import get_config
config = get_config()
self._api_key = getattr(config, 'finnhub_api_key', None) or os.getenv('FINNHUB_API_KEY')
if not self._api_key:
logger.debug("[Finnhub] API key not configured, fetcher disabled")
def _is_us_stock(self, stock_code: str) -> bool:
return is_us_stock_code(stock_code)
def _fetch_raw_data(self, stock_code: str, start_date: str, end_date: str) -> pd.DataFrame:
if not self._api_key:
raise DataFetchError("[Finnhub] API key not configured")
if not self._is_us_stock(stock_code):
raise DataFetchError(f"[Finnhub] {stock_code} is not a US stock")
symbol = stock_code.strip().upper()
start_ts = int(datetime.strptime(start_date, '%Y-%m-%d').timestamp())
end_ts = int(datetime.strptime(end_date, '%Y-%m-%d').timestamp())
url = f"{_FINNHUB_BASE_URL}/stock/candle"
params = {
'symbol': symbol,
'resolution': 'D',
'from': start_ts,
'to': end_ts,
'token': self._api_key,
}
try:
self.random_sleep(0.3, 0.8)
resp = requests.get(url, params=params, timeout=15)
resp.raise_for_status()View on GitHub (pinned to 5159bd72e8)
Solutions
- Route non-US codes through DataFetcherManager so market-appropriate sources handle them.
- Pre-check with the same predicate (is_us_stock_code / fetcher._is_us_stock) before calling FinnhubFetcher.
- Catch DataFetchError and skip to the next source — do not retry the same fetcher with the same non-US code.
Example fix
# before
f = FinnhhubFetcher()
df = f.get_daily_data('600519', ...) # raises
# after
if f._is_us_stock(code):
df = f.get_daily_data(code, ...)
else:
df = manager.get_daily_data(code, ...) Defensive patterns
Strategy: type-guard
Validate before calling
from src.utils.market_detector import is_us_stock_code # same predicate the fetcher uses
if is_us_stock_code(code):
df = finnhub.get_daily_data(code, ...) Type guard
def is_us_ticker(code: str) -> bool:
return is_us_stock_code(code) Try / catch
try:
df = finnhub.get_daily_data(code, ...)
except DataFetchError as e:
if 'is not a US stock' in str(e):
continue # routing mistake — route via manager instead Prevention
- Use the shared is_us_stock_code predicate at routing time, not a local regex copy.
- Never iterate all fetchers over a mixed-market list without market filtering.
- Cover market guards with unit tests for A-share/HK/US samples.
When it happens
Trigger: Directly invoking FinnhubFetcher.get_daily_data with '600519', 'hk00700', etc. — codes is_us_stock_code rejects. Through DataFetcherManager the US routing only offers Finnhub for US codes (and US indices via Yfinance-first order), so this normally appears only in direct/test usage.
Common situations: Tests driving every fetcher with a mixed-market list; custom loops bypassing the manager's market filter.
Related errors
- Backend executable not found: ${backendPath}
- Invalid share image record ID
- Desktop backend URL is unavailable
- Desktop backend origin is invalid
- Desktop share images require the configured backend origin
AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15).
Data as JSON: /api/errors/e74371ccd3884919.
Report an issue: GitHub.