Skip to content

做什么(One-liner)

多资产 Markowitz / 风险平价组合优化。scipy.optimize SLSQP 求 3 种目标(max Sharpe / min variance / equal risk contribution)+ 20-point efficient frontier。Long-only、sum=1 约束。

怎么用

Canvas 拖 portfolio_optimization_v1,上游接 ≥2 个 price_factor_v1(或直接给 symbols 参数),选 method(sharpe / min_variance / risk_parity)。输出最优权重 dict + 年化 return/vol/Sharpe + efficient_frontier。

核心公式

μ = mean(returns) × 252          # 年化收益向量
Σ = cov(returns)  × 252          # 年化协方差矩阵

目标函数:
  sharpe       : minimize -(μ·w - rf) / sqrt(w'Σw)
  min_variance : minimize w'Σw
  risk_parity  : minimize Σ_i (RC_i - σ_p/n)²

约束:
  sum(w) = 1, 0 ≤ w_i ≤ 1
  Starting w₀ = [1/n, ..., 1/n]

Solver: scipy.optimize.minimize(method='SLSQP', ftol=1e-9, maxiter=500)

Efficient frontier: 20 points, linspace(μ.min, μ.max);
  for each target_ret: minimize w'Σw s.t. sum=1, μ·w=target_ret

假设与适用场景

假设:yfinance 数据可用 + Σ 正定 + long-only + sum=1 + 1y lookback + 252 年化 + SLSQP 求解。

适用:跨资产 strategic allocation、efficient frontier 可视化、Markowitz 经典场景。

不适用:leverage、short selling、Black-Litterman priors、robust / shrinkage、multi-period、transaction costs。

输入 / 输出契约

{symbols, period?, method?, risk_free_rate?}{symbols, method, period, weights: dict, annual_return, annual_volatility, sharpe_ratio, efficient_frontier: list[20], optimization_success, windows}

Unknown method → fallback to sharpe(静默)。< 2 symbols → error。

已知局限

  1. SLSQP corner solutions:某些 seed 下 sharpe method 收敛到单一资产 — 正常 convex optimization 行为,不是 bug
  2. risk_parity 目标非 convex → SLSQP 找 local minimum(通常足够)
  3. ftol=1e-9 / maxiter=500 / frontier 20 点 / bounds [0, 1] 均 hardcoded
  4. risk_free_rate 仅 sharpe 使用
  5. optimization_success=false 时静默 fallback equal-weight — caller 需检查
  6. 不输出 per-asset risk contribution(可计算但未暴露)

参考文献

Markowitz (1952) 原始 + Maillard et al. (2010) ERC + Kraft (1988) SLSQP algorithm + DeMiguel-Garlappi-Uppal (2009) 1/N baseline。详见 frontmatter。

Golden Test

tests/golden/fixtures/tier2/portfolio_optimize_route/3-method parametrized over 4-symbol synthetic returns。1e-10 tolerance post-round(4)。9 tests:

  • matches_reference[sharpe/min_variance/risk_parity] — 3 method byte-equal
  • determinism — 5 reruns byte-identical
  • weights_sum_to_one — normalization invariant(所有方法)
  • long_only_constraint — w ∈ [0, 1](所有方法)
  • efficient_frontier_structure — 20 点、每个 {return, volatility}、升序排序
  • rejects_single_symbol — <2 → error
  • invalid_method_falls_back_to_sharpe — unknown method → sharpe 等价

Note on testing history: 早期 golden(closed PR #39)错误地 target 了 legacy dead function analyze_portfolio_optimization —— 用户 sharp 观察 "Canvas 上看着没问题" 触发 post-audit,发现 Canvas 实际 POST /portfolio/optimize(本 operator)。2026-04-20 fix 删除 legacy 函数 + 重建此 golden。

Changelog

  • 1.0.0 (2026-04-20) — 首次 Active(targets real Canvas route after legacy cleanup)

Verifiable intelligence for the decisions that demand scrutiny.