ZhuLinsen/daily_stock_analysis · error · FutuPortfolioError

无法确认证券类型的 Futu 持仓: {codes}

Error message

无法确认证券类型的 Futu 持仓: {codes}

What it means

After classification, every position code must have been seen in a get_stock_basicinfo response row (classified_codes). Codes that were sent for classification but never appeared in any response row — neither classified as STOCK, nor as a known non-stock type, nor skipped as unknown — cause this hard failure. The loader treats 'asked but got no answer' as unsafe: it cannot decide whether the position is a analyzable stock, so it refuses rather than guessing.

Source

Thrown at src/brokers/futu/portfolio.py:437

    except Exception as exc:  # noqa: BLE001 - translate SDK/network errors for CLI callers
        raise FutuPortfolioError(f"查询 Futu 持仓证券类型失败: {exc}") from exc
    finally:
        _safe_close(context)

    missing_codes = [
        code
        for codes in grouped.values()
        for code in codes
        if code not in classified_codes
    ]
    if unsupported_codes:
        logger.warning(
            "已跳过 %d 个当前分析流程不支持的 Futu 持仓: %s",
            len(unsupported_codes),
            ", ".join(unsupported_codes),
        )
    if missing_codes:
        raise FutuPortfolioError(
            "无法确认证券类型的 Futu 持仓: " + ", ".join(missing_codes)
        )

    result: List[str] = []
    conversion_failures: List[str] = []
    for futu_code in position_codes:
        if futu_code not in stock_codes:
            continue
        analysis_code = _to_analysis_code(futu_code)
        if not analysis_code:
            conversion_failures.append(futu_code)
            continue
        if analysis_code not in result:
            result.append(analysis_code)
    if conversion_failures:
        raise FutuPortfolioError(
            "无法转换已确认的 Futu 正股代码到当前分析格式: "
            + ", ".join(conversion_failures)

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Read the code list in the message; those symbols were silently dropped by futu's basicinfo API.
  2. Verify each symbol in Futu OpenD / moomoo — often they are halted, pre-IPO, or non-standard symbols.
  3. Close or exclude those positions (or that account via FUTU_ACC_ID) if they are not analyzable stocks.
  4. Report to maintainers if the symbols are ordinary liquid stocks — that indicates a normalization bug between request and response codes.
Defensive patterns

Strategy: try-catch

Try / catch

try:
    analysis_codes = classify_futu_positions(position_codes)
except FutuPortfolioError as exc:
    if "无法确认证券类型" in str(exc):
        unclassified = str(exc).split(":", 1)[1]
        logger.warning("futu silently dropped: %s", unclassified)
    raise

Prevention

When it happens

Trigger: get_stock_basicinfo silently omitting codes from its response (futu does this for codes it cannot resolve), so the code is not in classified_codes. Duplicates across batches, casing mismatches after .strip().upper() normalization, or codes the API drops without error also trigger it. The full offending list is embedded in the message.

Common situations: Portfolio containing symbols futu cannot classify (new IPOs, halted symbols, market-specific quirks); response row 'code' formatting differing from the request's normalized form.

Related errors


AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15). Data as JSON: /api/errors/98f6219c56a20969. Report an issue: GitHub.