{"record":{"id":"4340730b7cd07bb7","repo":"ZhuLinsen/daily_stock_analysis","slug":"futu-security-firm-name","errorCode":null,"errorMessage":"不支持的 FUTU_SECURITY_FIRM: {name}","messagePattern":"不支持的 FUTU_SECURITY_FIRM: (.+?)","errorType":"exception","errorClass":"FutuPortfolioError","httpStatus":null,"severity":"error","filePath":"src/brokers/futu/portfolio.py","lineNumber":161,"sourceCode":"    value = (os.getenv(\"FUTU_ACC_ID\") or \"\").strip()\n    if not value:\n        return None\n    try:\n        account_id = int(value)\n    except ValueError as exc:\n        raise FutuPortfolioError(\"FUTU_ACC_ID 必须是正整数账户 ID\") from exc\n    if account_id <= 0:\n        raise FutuPortfolioError(\"FUTU_ACC_ID 必须是正整数账户 ID\")\n    return account_id\n\n\ndef _configured_security_firm(api: _FutuApi) -> Any:\n    \"\"\"Resolve one firm, defaulting to the SDK's official auto-detection mode.\"\"\"\n\n    name = (os.getenv(\"FUTU_SECURITY_FIRM\") or \"NONE\").strip().upper()\n    firm = getattr(api.SecurityFirm, name, None)\n    if firm is None:\n        raise FutuPortfolioError(f\"不支持的 FUTU_SECURITY_FIRM: {name}\")\n    return firm\n\n\ndef _discover_real_accounts(api: _FutuApi, host: str, port: int) -> List[_FutuAccount]:\n    \"\"\"Discover explicitly ACTIVE NORMAL or MASTER REAL accounts.\"\"\"\n\n    accounts: List[_FutuAccount] = []\n    seen_ids = set()\n    requested_acc_id = _configured_account_id()\n    security_firm = _configured_security_firm(api)\n    context = None\n    try:\n        context = api.OpenSecTradeContext(\n            host=host,\n            port=port,\n            filter_trdmarket=api.TrdMarket.NONE,\n            security_firm=security_firm,\n        )","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/src/brokers/futu/portfolio.py#L143-L179","documentation":"FutuPortfolioError raised in _configured_security_firm (src/brokers/futu/portfolio.py:161) when FUTU_SECURITY_FIRM is set to a name that has no matching attribute on the SDK's SecurityFirm enum. The value is stripped and upper-cased, then looked up via getattr(api.SecurityFirm, name); unknown names (or unset, defaulting to 'NONE') yield None and raise. 'NONE' is the SDK's official auto-detection mode.","triggerScenarios":"Setting FUTU_SECURITY_FIRM to a firm name that is not an enum member: 'HK' instead of 'FUTUHK'/'HK', a Chinese firm name, a made-up value like 'AUTO', or a value from a different futu-api version whose enum members differ from 10.8.6808.","commonSituations":"Copying firm names from newer Futu docs that don't exist in the pinned SDK; guessing abbreviations; typos; leaving a template value in place.","solutions":["List the valid members for your pinned SDK: python -c \"from futu import SecurityFirm; print([m for m in dir(SecurityFirm) if not m.startswith('_')])\" and use one of those exact names (upper-case).","Prefer unsetting FUTU_SECURITY_FIRM (or NONE) to use the SDK's auto-detection unless you specifically need to pin a firm.","Re-check the name after any futu-api version change — enum members shift between releases."],"exampleFix":"# before (.env)\nFUTU_SECURITY_FIRM=HK\n\n# after (.env)\nFUTU_SECURITY_FIRM=FUTUSECURITIES\n# or simply unset for auto-detection:\n# FUTU_SECURITY_FIRM=NONE","handlingStrategy":"validation","validationCode":"def valid_security_firm(name: str | None) -> bool:\n    if not name or not name.strip():\n        return True  # defaults to NONE (auto-detect)\n    from futu import SecurityFirm\n    return hasattr(SecurityFirm, name.strip().upper())","typeGuard":"def is_security_firm_name(name: str, api) -> bool:\n    return getattr(api.SecurityFirm, name.strip().upper(), None) is not None","tryCatchPattern":null,"preventionTips":["Enumerate SecurityFirm members for the pinned SDK before setting the env var.","Default to unset/NONE (auto-detection) unless pinning a firm is required.","Re-validate firm names after any futu-api version change."],"tags":["configuration","futu","enum","validation"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}