{"record":{"id":"f4abcfe21adba926","repo":"ZhuLinsen/daily_stock_analysis","slug":"futu-prefix-data","errorCode":null,"errorMessage":"查询 Futu 持仓证券类型失败（{prefix}）: {data}","messagePattern":"查询 Futu 持仓证券类型失败（(.+?)）: (.+?)","errorType":"exception","errorClass":"FutuPortfolioError","httpStatus":null,"severity":"error","filePath":"src/brokers/futu/portfolio.py","lineNumber":404,"sourceCode":"    stock_codes = set()\n    classified_codes = set()\n    context = None\n    try:\n        context = api.OpenQuoteContext(host=host, port=port)\n        for prefix, codes in grouped.items():\n            market = getattr(api.Market, prefix, None)\n            if market is None:\n                unsupported_codes.extend(codes)\n                continue\n            for start in range(0, len(codes), _STATIC_INFO_BATCH_SIZE):\n                batch = codes[start : start + _STATIC_INFO_BATCH_SIZE]\n                ret, data = context.get_stock_basicinfo(\n                    market,\n                    stock_type=api.SecurityType.STOCK,\n                    code_list=batch,\n                )\n                if ret != api.RET_OK:\n                    raise FutuPortfolioError(\n                        f\"查询 Futu 持仓证券类型失败（{prefix}）: {data}\"\n                    )\n                for row in _iter_rows(data, \"Futu 证券类型查询\"):\n                    code = str(row.get(\"code\", \"\") or \"\").strip().upper()\n                    if not code:\n                        continue\n                    stock_type = _enum_text(row.get(\"stock_type\"))\n                    if stock_type in _UNKNOWN_SECURITY_TYPES:\n                        continue\n                    classified_codes.add(code)\n                    if stock_type == \"STOCK\":\n                        stock_codes.add(code)\n    except FutuPortfolioError:\n        raise\n    except Exception as exc:  # noqa: BLE001 - translate SDK/network errors for CLI callers\n        raise FutuPortfolioError(f\"查询 Futu 持仓证券类型失败: {exc}\") from exc\n    finally:\n        _safe_close(context)","sourceCodeStart":386,"sourceCodeEnd":422,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/src/brokers/futu/portfolio.py#L386-L422","documentation":"Raised during the security-type classification step: after grouping position codes by market prefix, the loader batches codes (in _STATIC_INFO_BATCH_SIZE chunks) into context.get_stock_basicinfo(market, stock_type=STOCK, code_list=batch). If the futu API returns ret != RET_OK (the SDK's non-exception failure mode), the error is raised with the market prefix and the raw data payload futu returned (usually an error string).","triggerScenarios":"get_stock_basicinfo returning RET_ERROR for a batch, e.g. too many codes requested, an invalid code slipped into code_list, market temporarily unavailable, rate limiting, or OpenD protocol errors. The message embeds which prefix (SH/HK/US...) failed and futu's own error text in data.","commonSituations":"Large portfolios where a batch hits futu's code_list limits; one malformed code poisoning the whole batch; FutuOpenD not subscribed/logged in for that market; API quota exceeded.","solutions":["Inspect the {data} payload — it contains futu's own error string which pinpoints the cause (invalid code, frequency limit, permission).","If a specific code is rejected, exclude that position/account or handle it as unsupported instead of STOCK.","Check market permissions for the account in Futu OpenD (e.g. US market data/quote rights).","Restart/re-auth FutuOpenD and retry; if persistent, reduce portfolio size to isolate the failing batch."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    stock_codes = classify_futu_positions(position_codes)\nexcept FutuPortfolioError as exc:\n    logger.error(\"basicinfo failed: %s\", exc)  # message embeds prefix + futu error text\n    raise","preventionTips":["Verify market permissions for all markets held in the account","Keep portfolios/batches within futu code_list limits","Read the embedded {data} payload — it is futu's own error string and names the root cause"],"tags":["futu","broker","api-error","batching"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}