ZhuLinsen/daily_stock_analysis · error · FutuPortfolioError

查询 Futu 真实账户失败: {data}

Error message

查询 Futu 真实账户失败: {data}

What it means

FutuPortfolioError raised in _discover_real_accounts (src/brokers/futu/portfolio.py:182) when OpenSecTradeContext.get_acc_list() returns ret != RET_OK. In the Futu SDK convention (ret, data), a non-zero ret means the request failed and 'data' carries the error string; that string is embedded in this exception.

Source

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

def _discover_real_accounts(api: _FutuApi, host: str, port: int) -> List[_FutuAccount]:
    """Discover explicitly ACTIVE NORMAL or MASTER REAL accounts."""

    accounts: List[_FutuAccount] = []
    seen_ids = set()
    requested_acc_id = _configured_account_id()
    security_firm = _configured_security_firm(api)
    context = None
    try:
        context = api.OpenSecTradeContext(
            host=host,
            port=port,
            filter_trdmarket=api.TrdMarket.NONE,
            security_firm=security_firm,
        )
        ret, data = context.get_acc_list()
        if ret != api.RET_OK:
            raise FutuPortfolioError(f"查询 Futu 真实账户失败: {data}")
        for row in _iter_rows(data, "Futu 账户查询"):
            if _enum_text(row.get("trd_env")) != "REAL":
                continue
            if _enum_text(row.get("acc_status")) != "ACTIVE":
                continue
            if _enum_text(row.get("acc_role")) not in _SUPPORTED_ACCOUNT_ROLES:
                continue
            raw_acc_id = row.get("acc_id")
            try:
                acc_id = int(raw_acc_id)
                exact_integer = isinstance(raw_acc_id, str) or bool(
                    raw_acc_id == acc_id
                )
            except (TypeError, ValueError, OverflowError) as exc:
                raise FutuPortfolioError(
                    "Futu 账户查询返回了无效账户 ID"
                ) from exc
            if isinstance(raw_acc_id, bool) or not exact_integer or acc_id <= 0:

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Confirm OpenD is running and reachable: nc -vz <host> 11111 (or telnet), and check the OpenD UI/log for login state.
  2. Read the embedded '{data}' text — the SDK's error string usually names the exact cause (connect failure, not logged in, no permission).
  3. Verify OpenD is logged into the Futu account with trading enabled, and that its version is compatible with futu-api==10.8.6808.
  4. Match FUTU_SECURITY_FIRM to the actual account or leave it at NONE for auto-detection.

Example fix

# before: assume success
ret, data = ctx.get_acc_list()

# after: fail fast with the SDK's reason surfaced
ret, data = ctx.get_acc_list()
if ret != RET_OK:
    raise RuntimeError(f"get_acc_list failed: {data}")
Defensive patterns

Strategy: retry

Validate before calling

import socket

def opend_reachable(host: str, port: int, timeout: float = 2.0) -> bool:
    try:
        with socket.create_connection((host, port), timeout=timeout):
            return True
    except OSError:
        return False

Try / catch

from src.brokers.futu.portfolio import FutuPortfolioError
try:
    accounts = discover(api, host, port)
except FutuPortfolioError as exc:
    if '查询 Futu 真实账户失败' in str(exc):
        log.error('OpenD get_acc_list failed: %s', exc)
        # inspect message: login/permission vs connectivity before retrying
    raise

Prevention

When it happens

Trigger: OpenD gateway not running or unreachable at FUTU_OPEND_HOST:PORT; OpenD logged out of the Futu account; the connected OpenD has no trading (SecTrade) capability enabled; invalid security_firm for the connection; OpenD version too old for the pinned SDK.

Common situations: OpenD not started on the deployment machine; firewall blocking the port in Docker/remote setups; OpenD session expired and needs re-login; FUTU_SECURITY_FIRM mismatched with the actual logged-in broker.

Related errors


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