{"record":{"id":"e74371ccd3884919","repo":"ZhuLinsen/daily_stock_analysis","slug":"finnhub-stock-code-is-not-a-us-stock","errorCode":null,"errorMessage":"[Finnhub] {stock_code} is not a US stock","messagePattern":"\\[Finnhub\\] (.+?) is not a US stock","errorType":"exception","errorClass":"DataFetchError","httpStatus":null,"severity":"warning","filePath":"data_provider/finnhub_fetcher.py","lineNumber":45,"sourceCode":"class FinnhubFetcher(BaseFetcher):\n    name = \"FinnhubFetcher\"\n    priority = 2\n\n    def __init__(self):\n        from src.config import get_config\n        config = get_config()\n        self._api_key = getattr(config, 'finnhub_api_key', None) or os.getenv('FINNHUB_API_KEY')\n        if not self._api_key:\n            logger.debug(\"[Finnhub] API key not configured, fetcher disabled\")\n\n    def _is_us_stock(self, stock_code: str) -> bool:\n        return is_us_stock_code(stock_code)\n\n    def _fetch_raw_data(self, stock_code: str, start_date: str, end_date: str) -> pd.DataFrame:\n        if not self._api_key:\n            raise DataFetchError(\"[Finnhub] API key not configured\")\n        if not self._is_us_stock(stock_code):\n            raise DataFetchError(f\"[Finnhub] {stock_code} is not a US stock\")\n\n        symbol = stock_code.strip().upper()\n        start_ts = int(datetime.strptime(start_date, '%Y-%m-%d').timestamp())\n        end_ts = int(datetime.strptime(end_date, '%Y-%m-%d').timestamp())\n\n        url = f\"{_FINNHUB_BASE_URL}/stock/candle\"\n        params = {\n            'symbol': symbol,\n            'resolution': 'D',\n            'from': start_ts,\n            'to': end_ts,\n            'token': self._api_key,\n        }\n\n        try:\n            self.random_sleep(0.3, 0.8)\n            resp = requests.get(url, params=params, timeout=15)\n            resp.raise_for_status()","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/data_provider/finnhub_fetcher.py#L27-L63","documentation":"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.","triggerScenarios":"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.","commonSituations":"Tests driving every fetcher with a mixed-market list; custom loops bypassing the manager's market filter.","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."],"exampleFix":"# before\nf = FinnhhubFetcher()\ndf = f.get_daily_data('600519', ...)  # raises\n# after\nif f._is_us_stock(code):\n    df = f.get_daily_data(code, ...)\nelse:\n    df = manager.get_daily_data(code, ...)","handlingStrategy":"type-guard","validationCode":"from src.utils.market_detector import is_us_stock_code  # same predicate the fetcher uses\nif is_us_stock_code(code):\n    df = finnhub.get_daily_data(code, ...)","typeGuard":"def is_us_ticker(code: str) -> bool:\n    return is_us_stock_code(code)","tryCatchPattern":"try:\n    df = finnhub.get_daily_data(code, ...)\nexcept DataFetchError as e:\n    if 'is not a US stock' in str(e):\n        continue  # routing mistake — route via manager instead","preventionTips":["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."],"tags":["finnhub","market-guard","us-stocks","validation"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}