ZhuLinsen/daily_stock_analysis · error · FutuPortfolioError
查询 Futu 真实持仓失败: {exc}
Error message
查询 Futu 真实持仓失败: {exc} What it means
Catch-all translator inside the Futu position loader: any exception that is not already a FutuPortfolioError (SDK errors, network failures, attribute errors from unexpected response shapes) is re-raised as FutuPortfolioError with the original message and __cause__ preserved. This gives CLI callers one exception type to handle for the whole 'query real positions' step; the context is closed via _safe_close in finally.
Source
Thrown at src/brokers/futu/portfolio.py:305
if quantity == 0:
continue
if not isinstance(raw_code, str):
raise FutuPortfolioError("Futu 非零持仓返回了无效证券代码")
if not code:
raise FutuPortfolioError("Futu 非零持仓返回了空证券代码")
market, separator, symbol = code.partition(".")
if not separator or not market or not symbol:
raise FutuPortfolioError(
f"Futu 非零持仓返回了无效证券代码: {code}"
)
if code in seen_codes:
continue
seen_codes.add(code)
codes.append(code)
except FutuPortfolioError:
raise
except Exception as exc: # noqa: BLE001 - translate SDK/network errors for CLI callers
raise FutuPortfolioError(f"查询 Futu 真实持仓失败: {exc}") from exc
finally:
_safe_close(context)
if skipped_short_count:
logger.info("已跳过 %d 个 Futu SHORT 空头持仓", skipped_short_count)
if skipped_unknown_side_count:
logger.warning(
"已跳过 %d 个持仓方向不是 LONG 的 Futu 持仓",
skipped_unknown_side_count,
)
return codes
def _market_prefix(code: str) -> str:
"""Extract the Futu market prefix from a qualified security code."""
return code.split(".", 1)[0] if "." in code else ""
View on GitHub (pinned to 5159bd72e8)
Solutions
- Read the embedded original message (exc) — it names the real cause; address that (start FutuOpenD, unlock trade, fix port).
- Confirm FutuOpenD is running and reachable, and the account is a REAL account type as required by the loader.
- Upgrade futu-api and FutuOpenD to matching versions if the inner error is a protocol/parse error.
- Retry once after restoring the gateway; the loader itself does not retry.
Defensive patterns
Strategy: try-catch
Try / catch
from src.brokers.futu.portfolio import FutuPortfolioError
try:
codes = load_futu_stock_codes()
except FutuPortfolioError as exc:
cause = exc.__cause__
logger.error("Futu query failed: %s (root: %s)", exc, cause)
return [] # or surface to user Prevention
- Ensure FutuOpenD is running and unlocked before analysis jobs
- Keep gateway and SDK versions matched
- Catch FutuPortfolioError at the orchestration layer — it wraps every failure mode of this loader
When it happens
Trigger: OpenQuoteContext construction or position_list raising futu.api errors (ret != RET_OK paths are separately handled, so this is for actual exceptions): connection refused to FutuOpenD, unlock-trade timeouts, JSON decode failures on malformed responses, AttributeError on unexpected row types.
Common situations: FutuOpenD not running or listening on a different port; trade password not unlocked; network interruption between the host and the gateway; futu-api internal errors.
Related errors
- 查询 Futu 持仓证券类型失败: {exc}
- futu-api==10.8.6808 的网络层仅支持 IPv4;FUTU_OPEND_HOST 当前为 {host!r
- 查询 Futu 真实账户失败: {data}
- 查询 Futu 真实账户失败: {exc}
- 查询 Futu 真实持仓失败: {data}
AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15).
Data as JSON: /api/errors/664558b867c349ff.
Report an issue: GitHub.