ZhuLinsen/daily_stock_analysis · error · FutuPortfolioError

未找到状态为 ACTIVE 的 Futu REAL 普通或 MASTER 证券账户

Error message

未找到状态为 ACTIVE 的 Futu REAL 普通或 MASTER 证券账户

What it means

FutuPortfolioError raised in _discover_real_accounts (src/brokers/futu/portfolio.py:227) when no FUTU_ACC_ID is configured and the discovered account list (filtered to trd_env=REAL, acc_status=ACTIVE, acc_role in supported roles) is empty. OpenD answered the query but reports zero usable real trading accounts.

Source

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

            )
            seen_ids.add(acc_id)
            accounts.append(_FutuAccount(acc_id=acc_id, security_firm=returned_firm))
    except FutuPortfolioError:
        raise
    except Exception as exc:  # noqa: BLE001 - translate SDK/network failures
        raise FutuPortfolioError(f"查询 Futu 真实账户失败: {exc}") from exc
    finally:
        _safe_close(context)

    if requested_acc_id is not None:
        accounts = [account for account in accounts if account.acc_id == requested_acc_id]
        if not accounts:
            raise FutuPortfolioError(
                "FUTU_ACC_ID 未匹配到可用的真实证券账户;请检查账户 ID、券商和 OpenD 登录状态。"
            )

    if not accounts:
        raise FutuPortfolioError(
            "未找到状态为 ACTIVE 的 Futu REAL 普通或 MASTER 证券账户"
        )
    return accounts


def _load_position_codes(
    api: _FutuApi,
    host: str,
    port: int,
    accounts: Iterable[_FutuAccount],
) -> List[str]:
    """Load deduplicated non-zero LONG position codes from selected accounts."""

    codes: List[str] = []
    seen_codes = set()
    skipped_short_count = 0
    skipped_unknown_side_count = 0

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Open the OpenD console and verify a REAL securities account exists with ACTIVE status and trading enabled for the target market.
  2. If only a simulate account exists, switch OpenD login to the real account (this source intentionally excludes SIMULATE).
  3. Re-login OpenD and re-run; status can lag after activation until OpenD refreshes.
  4. If the account role is unusual, check _SUPPORTED_ACCOUNT_ROLES in the module and whether your role (e.g. joint/custody) is supported.

Example fix

# standalone diagnosis — see what OpenD actually reports
from futu import OpenSecTradeContext, TrdMarket, SecurityFirm
ctx = OpenSecTradeContext(host='127.0.0.1', port=11111,
                          filter_trdmarket=TrdMarket.NONE,
                          security_firm=SecurityFirm.NONE)
ret, data = ctx.get_acc_list()
print(data[['acc_id', 'trd_env', 'acc_status', 'acc_role']])
ctx.close()
Defensive patterns

Strategy: validation

Validate before calling

def has_active_real_account(rows) -> bool:
    for row in rows:
        if str(getattr(row.get('trd_env'), 'name', row.get('trd_env'))).upper() == 'REAL' \
           and str(getattr(row.get('acc_status'), 'name', row.get('acc_status'))).upper() == 'ACTIVE':
            return True
    return False

Prevention

When it happens

Trigger: OpenD logged in with trading disabled or no securities account; only a SIMULATE account present; accounts exist but acc_status is not ACTIVE (frozen, pending activation); account role not NORMAL/MASTER; OpenD connected to a market without trading permission.

Common situations: Fresh OpenD setup where the Futu account has no opened securities account; accounts frozen by the broker; OpenD logged into a paper-trading profile; missing market/trade permissions on the account.

Related errors


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