ZhuLinsen/daily_stock_analysis · error · ValueError
不支持的 portfolio: {portfolio}
Error message
不支持的 portfolio: {portfolio} What it means
_resolve_portfolio_stock_codes accepts exactly one portfolio value, 'futu'; anything else non-empty raises ValueError. The CLI argparse choices already block invalid values for command-line users — this guard exists for API/programmatic callers that construct an argparse.Namespace or call the function with arbitrary strings.
Source
Thrown at main.py:544
settings_from_config,
)
result = refresh_remote_stock_index_cache(settings_from_config(config))
if result.refreshed:
logger.info("[stock-index] 分析前已刷新股票索引缓存: %s", result.cache_path)
elif result.error:
logger.debug("[stock-index] 分析前刷新未完成,继续使用本地索引: %s", result.error)
except Exception as exc: # noqa: BLE001 - stock index freshness must not block analysis.
logger.warning("[stock-index] 分析前刷新股票索引失败,继续执行分析: %s", exc)
def _resolve_portfolio_stock_codes(args: argparse.Namespace) -> Optional[List[str]]:
"""Resolve an optional broker portfolio into the analysis stock list."""
portfolio = str(getattr(args, "portfolio", "") or "").strip().lower()
if not portfolio:
return None
if portfolio != "futu": # argparse prevents this for CLI callers; keep API callers safe.
raise ValueError(f"不支持的 portfolio: {portfolio}")
from src.brokers.futu.portfolio import load_futu_stock_codes
stock_codes = [
canonical_stock_code(code)
for code in load_futu_stock_codes()
if (code or "").strip()
]
logger.info("portfolio=futu 已覆盖 stocks/STOCK_LIST,使用 %d 只真实正股", len(stock_codes))
return stock_codes
def _prime_daily_market_context(
config: Config,
pipeline: Any,
*,
region: str,
no_market_review: bool,View on GitHub (pinned to 5159bd72e8)
Solutions
- Pass portfolio='futu' (case-insensitive) or leave it empty to use stocks/STOCK_LIST config.
- If you are adding a new broker, extend this function and the argparse choices together, plus its loader under src/brokers/.
- Validate the portfolio enum at the API boundary before it reaches this function, so callers get a 4xx instead of an unhandled ValueError.
Example fix
# before args.portfolio = 'ibkr' _resolve_portfolio_stock_codes(args) # ValueError: 不支持的 portfolio: ibkr # after args.portfolio = 'futu' _resolve_portfolio_stock_codes(args)
Defensive patterns
Strategy: validation
Validate before calling
SUPPORTED_PORTFOLIOS = {"futu"}
portfolio = (getattr(args, "portfolio", "") or "").strip().lower()
if portfolio and portfolio not in SUPPORTED_PORTFOLIOS:
raise ValueError(f"portfolio must be one of {sorted(SUPPORTED_PORTFOLIOS)}") Type guard
def is_supported_portfolio(value: str) -> bool:
return not value or value.strip().lower() in {"futu"} Try / catch
try:
codes = _resolve_portfolio_stock_codes(args)
except ValueError as e:
# map to 4xx at API boundary, or default to stocks config
codes = None
logger.warning("portfolio ignored: %s", e) Prevention
- Validate the portfolio enum at the API boundary before building the argparse Namespace.
- When adding a broker, update resolver + argparse choices + loader in one change.
- Keep the supported set in one constant shared by CLI and API validation.
When it happens
Trigger: Calling main's portfolio resolution path with args.portfolio set to anything other than 'futu' or ''/None (e.g. 'ibkr', 'FUTU' before lower() is fine, but 'futu5', 'tiger', or a typo). API layers that build a Namespace from request payloads without validating the enum.
Common situations: A new broker integration added to an API client but not to this resolver; downstream code passing user input straight into the namespace; case variations are handled (.lower()) but whitespace-only is treated as empty by .strip().
Related errors
- Backend executable not found: ${backendPath}
- Invalid share image record ID
- Desktop backend URL is unavailable
- Desktop backend origin is invalid
- Desktop share images require the configured backend origin
AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15).
Data as JSON: /api/errors/e43d9761e69045e9.
Report an issue: GitHub.