{"record":{"id":"dee50811b18e0e44","repo":"ZhuLinsen/daily_stock_analysis","slug":"futu-codes-dee508","errorCode":null,"errorMessage":"无法转换已确认的 Futu 正股代码到当前分析格式: {codes}","messagePattern":"无法转换已确认的 Futu 正股代码到当前分析格式: (.+?)","errorType":"exception","errorClass":"FutuPortfolioError","httpStatus":null,"severity":"error","filePath":"src/brokers/futu/portfolio.py","lineNumber":453,"sourceCode":"        )\n    if missing_codes:\n        raise FutuPortfolioError(\n            \"无法确认证券类型的 Futu 持仓: \" + \", \".join(missing_codes)\n        )\n\n    result: List[str] = []\n    conversion_failures: List[str] = []\n    for futu_code in position_codes:\n        if futu_code not in stock_codes:\n            continue\n        analysis_code = _to_analysis_code(futu_code)\n        if not analysis_code:\n            conversion_failures.append(futu_code)\n            continue\n        if analysis_code not in result:\n            result.append(analysis_code)\n    if conversion_failures:\n        raise FutuPortfolioError(\n            \"无法转换已确认的 Futu 正股代码到当前分析格式: \"\n            + \", \".join(conversion_failures)\n        )\n    return result\n\n\ndef load_futu_stock_codes() -> List[str]:\n    \"\"\"Return deduplicated analysis codes from all selected REAL Futu accounts.\n\n    Only explicitly ACTIVE REAL accounts and Futu ``SecurityType.STOCK`` LONG\n    positions with non-zero quantity are kept. ``FUTU_ACC_ID`` can select one\n    account; otherwise NORMAL and MASTER accounts are merged. ``MASTER`` is an\n    account role, while read-only describes this integration's query-only API\n    calls. Firm discovery uses the SDK's ``SecurityFirm.NONE`` auto-detection\n    unless ``FUTU_SECURITY_FIRM`` is explicitly set. Position data is always\n    refreshed. Symbol conversion is limited to A/HK/US stocks; holdings from\n    other Futu markets are logged with their codes and skipped.\n    \"\"\"","sourceCodeStart":435,"sourceCodeEnd":471,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/src/brokers/futu/portfolio.py#L435-L471","documentation":"Final conversion step failure: a position code was confirmed as SecurityType.STOCK by futu, but the local mapping _to_analysis_code(futu_code) returned falsy — the loader knows no analysis-format equivalent (e.g. the analysis system's market prefix convention) for that confirmed stock code. Failing loudly here guarantees the analysis universe matches the real portfolio instead of silently omitting real holdings.","triggerScenarios":"A futu market prefix/symbol combo that _to_analysis_code has no mapping rule for, e.g. a market the analysis system doesn't support (some A-share/futures/UK codes) but which futu classifies as STOCK. The failing futu codes are listed in the message.","commonSituations":"Account holding stocks on markets added by futu but not yet mapped in the analyzer (e.g. SG, JP, AU exchanges); new futu code formats after SDK upgrades.","solutions":["Check the listed codes' market prefix; unsupported markets need either an exclusion rule or a mapping added to _to_analysis_code in src/brokers/futu/portfolio.py.","If the market is genuinely out of scope for the analysis flow, exclude those positions/account rather than extending the mapping ad hoc.","Add a regression test with the failing futu code once mapped."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    result = classify_futu_positions(position_codes)\nexcept FutuPortfolioError as exc:\n    logger.error(\"unmapped futu stock codes: %s\", exc)\n    raise","preventionTips":["Extend _to_analysis_code mappings when onboarding new markets","Keep the supported-market list documented alongside the mapping function","Add regression tests for every newly mapped futu code format"],"tags":["futu","broker","code-mapping","validation"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}