EigenLedger 的四种优化器有什么差别?
下表的「求解目标」与「默认估计方法」均来自源码,不是文档转述。
| optimizer 取值 | 求解目标 | 默认收益估计 | 默认风险估计 | 适用场景 | 注意点 |
|---|---|---|---|---|---|
"EF" | 最大夏普(性价比最高) | mean_historical_return | sample_cov | 希望按历史性价比配权,标的预期收益差异明显 | 用历史均值当预期收益,本质是把过去涨幅外推 |
"MEANVAR" | 在年化波动不超过 max_vol 的前提下求最优 | capm_return | ledoit_wolf | 有明确波动预算(如产品风控线) | max_vol 默认 0.15,与报告里的年化波动口径不同 |
"HRP" | 层次风险平价:按相关性结构分配风险 | 不使用 | 不使用(直接从价格算) | 标的多、相关性复杂,不想猜预期收益 | 天然更分散,但不追求收益最优,也不接受 min/max 约束 |
"MINVAR" | 最小化组合波动 | capm_return | ledoit_wolf | 把回撤与波动放在第一位 | 低波动通常伴随低收益;四个优化器中唯一没有缺失值填充 |
| 自定义函数 | 你的逻辑 | 自行决定 | 自行决定 | 接入内部打分、等风险贡献、外部模型 | 返回值必须是与 portfolio 顺序一致的权重列表 |
四种优化器都在构造 Engine 时就完成求解,也就是 Engine(..., optimizer="EF") 这一行就会联网取历史价格。断网或标的缺失会让构造直接失败,报错点不在分析函数里。
EigenLedger 的优化器实际调用了 PyPortfolioOpt 的哪个方法?
理解这一层,你才能把报错信息对应到具体环节。
| 优化器 | 构造与目标函数 | 约束与正则 | 报错时先查什么 |
|---|---|---|---|
EF | EfficientFrontier(mu, S) → ef.max_sharpe() | L2 正则 gamma=diversification;可选权重上下限 | 预期收益是否有足够差异(全为负时最大夏普可能无解) |
MEANVAR | EfficientFrontier(mu, S) → ef.efficient_risk(max_vol) | 同上 | max_vol 是否低于给定标的集合的可达最小波动 |
HRP | HRPOpt(returns_from_prices(prices)) → optimize() | 无 L2 正则、无权重上下限 | 相关性矩阵是否可逆(标的高度重复时容易出问题) |
MINVAR | EfficientFrontier(mu, S) → ef.min_volatility() | L2 正则与权重上下限均生效 | 价格矩阵是否含 NaN(该分支不做填充) |
| 自定义函数 | 直接调用你传入的函数并取其返回值 | 由你的函数内部处理 | 返回值长度与顺序是否与组合一致 |
min_weights / max_weights 会被转成向量约束 x >= min_weights / x <= max_weights。约束过紧(例如所有标的都必须 ≥0.3 而标的只有 3 个)会导致求解器无解,报错来自底层 cvxpy,信息里通常不会出现「EigenLedger」字样,排查时容易迷失。换 EigenLedger 的估计方法,权重会差多少?
expected_returns 与 risk_model 只接受白名单取值,传错会直接断言失败。
| 参数 | 可选值 | 直觉含义 | 什么时候换 |
|---|---|---|---|
expected_returns | mean_historical_return | 用历史平均收益作为预期 | 标的差异明显、区间较长(EF 默认) |
ema_historical_return | 近期收益权重更高 | 想弱化远期数据影响 | |
capm_return | 用市场 Beta 推导预期收益 | 认为个股跑不赢自身风险敞口(MEANVAR/MINVAR 默认) | |
risk_model | sample_cov | 样本协方差,最直接 | 样本充足、标的少(EF 默认) |
semicovariance | 只统计下行波动 | 更在意亏损方向的波动 | |
exp_cov | 近期样本权重更大 | 市场结构近期发生变化 | |
ledoit_wolf 系列 / oracle_approximating | 收缩估计,缓解样本噪声 | 标的数量接近或超过样本长度时(MEANVAR/MINVAR 默认) |
| 其它参数 | 作用范围 | 调大的效果 | 副作用 |
|---|---|---|---|
diversification(默认 1) | EF / MEANVAR / MINVAR | 权重更平均,集中度下降 | 偏离原始最优目标,夏普可能下降 |
max_vol(默认 0.15) | 仅 MEANVAR | 允许更高波动,收益目标更激进 | 设得过低会直接无解 |
min_weights / max_weights | EF / MEANVAR / MINVAR | 限制单标的下限或上限 | 约束叠加后可行域可能为空 |
| 回测区间 | 全部 | 区间越长越平滑 | 区间选择本身会影响最优权重,这就是过拟合的来源 |
risk_manager | 仅分析阶段生效 | 按阈值提前截断收益序列 | 截断后区间变短,年化与夏普都被改写 |
| 标的数量 | 全部 | 可分散的维度更多 | 标的数接近样本长度时协方差估计不可靠 |
怎么证明 EigenLedger 的「优化真的有用」?
库提供了 optimize_portfolio(),会用同一区间画出优化前后的净值曲线。
确认引擎里有优化器
没设
optimizer就调用,会直接抛Exception: You didn't define any optimizer in your portfolio!。先算等权基准
函数内部先按 1/n 等权算一条「未优化」的净值曲线,作为对比基准。
再按优化权重算第二条曲线
零权重的标的会从组合中剔除后再计算,因此两条曲线的标的集合可能不同。
看两条曲线的差
输出图为蓝色「未优化」与红色「已优化」。差异明显不代表优化更好——要看同期的回撤与波动是否也一起变差。
| 你会看到的现象 | 含义 | 需不需要处理 | 建议做法 |
|---|---|---|---|
| 优化后曲线明显高于等权 | 该区间内优化权重确实更优 | 不代表未来同样成立 | 换 2 段以上区间重跑,看结论是否稳定 |
| 优化权重集中在一两只标的 | 目标函数把权重推向历史最优解 | 需要处理 | 用 max_weights 或提高 diversification |
| 优化后波动更大 | 最大夏普不等于最小风险 | 取决于你的目标 | 风险优先就换 MINVAR 或 MEANVAR |
| 权重为 0 的标的消失 | 源码会剔除零权重标的 | 正常现象 | 记录被剔除的标的,作为「模型不看好」的证据 |
| 两条曲线几乎重合 | 标的本身相关性极高 | 不必处理 | 换标的集合再试,或直接接受等权 |
哪三个细节会让 EigenLedger 权重失去意义?
都写在源码里,但不会报错。
缺失价格被填成 1
EF、MEANVAR、HRP 三个分支会把缺失价格填充为 1(源码注释解释为「公司还没上市时不能填 0」)。若区间起点早于某标的上市日,该标的会先出现一段人为的低价区,收益与协方差都被污染。
权重会被取整
求解后统一走 clean_weights(),权重被截断到较小位数,且零权重会被删除。因此权重之和不严格等于 1,直接拿去做资金分配前需要重新归一化。
MinVar 不做填充
MINVAR 分支没有缺失值填充。同样是含缺口的数据,换一个优化器就可能从「结果偏移」变成「直接报错」,四个优化器彼此不等价。
什么场景选 EigenLedger 的哪个优化器?
按「你的约束条件」来选,而不是按「哪个听起来高级」来选。
| 你的约束 | 推荐选项 | 理由 | 下一步该验证什么 |
|---|---|---|---|
| 有明确的波动上限(风控线) | MEANVAR + max_vol | 目标函数直接就是「不超过波动上限的最优解」 | 用报告里的年化波动复核,注意两套口径不同 |
| 最怕回撤,收益次要 | MINVAR | 直接最小化波动,通常也压低回撤 | 检查收益是否被压到不可接受 |
| 标的多、相关性结构复杂 | HRP | 不猜预期收益,按相关性聚类分配风险 | 看权重是否过于平均、是否有被完全忽略的标的 |
| 想让权重差异体现预期收益差 | EF | 最大夏普会把权重推向高性价比标的 | 换两段区间验证权重稳定性 |
| 已有内部打分模型 | 自定义函数 | 库允许直接传入返回权重列表的函数 | 核对顺序与长度,并确认零权重处理符合预期 |
| 不想做任何假设 | 不用优化器(等权) | 等权是难以被打败的基线,也便于解释 | 把等权结果作为对照写进报告 |
| 对照维度 | EigenLedger 路线 | 本机技能路线 | 建议 |
|---|---|---|---|
| 组合优化 | 四种优化器可复现、参数明确 | quant-analyst 技能声明支持组合优化 | 两套结果交叉验证后再用 |
| 权重解释 | 需自己读源码理解目标函数 | 可用自然语言追问理由 | 教学或沟通场景选后者 |
| 批量试参数 | 脚本化,可循环遍历 | 偏交互式 | 批量实验选前者 |
| 结果留档 | 脚本 + 版本即可复现 | 对话记录留痕 | 正式研究选前者 |
| 权重集中度披露 | 可打印 clean_weights 结果 | 取决于技能输出形态 | 对外材料必须给出前两大权重占比 |
| 约束条件表达 | 用 min_weights / max_weights 显式声明 | 用自然语言描述约束 | 约束复杂时选前者,并注意可行性 |
披露:本机技能目录已核验(29 个技能),EigenLedger 与 EasyClaw 无已证实集成;两边的组合优化实现与输出口径不同,不可互相替代。
关于组合优化的常见问题
四个优化器能同时用吗?
不能。optimizer 是单值参数,只能取一个值;想对比就得跑四次,各自构造一个 Engine。注意 Engine 构造期就会求解并取数,四次对比意味着四次独立下载。
为什么优化后有些标的权重是 0?
目标函数在约束下求得的最优解把权重压到了 0,随后源码会把零权重标的从组合列表中删除。如果这种集中度不可接受,可叠加 min_weights 或提高 diversification(L2 正则强度),以官方实现为准。
max_vol=0.15 是年化波动吗?
它是交给 PyPortfolioOpt 的 efficient_risk 的目标波动水平,与报告里 QuantStats 计算的年化波动不是同一套实现。两者数值接近但不保证相等,跨口径对比前请先各算一遍,以官方实现为准。
为什么换个时间段权重就完全变了?
因为预期收益与协方差都由区间内数据估计,区间变了输入就变了。这也是均值方差类优化的共识问题:样本噪声被当作真实信号。缓解手段是收缩估计(ledoit_wolf)、加权重约束、或改用不依赖预期收益的 HRP。
自定义优化器函数要注意什么?
函数必须返回与标的顺序一一对应的权重列表;源码会把返回值直接当作权重使用,因此长度不符会在后续取数阶段报错,顺序不对则不会报错但结果完全错误。建议在函数内部加断言,以官方实现为准。
优化一定会提高收益吗?
不一定。优化的是目标函数,不是未来收益。最大夏普通常提高历史风险调整收益,最小波动优化通常会降低收益水平;两者都只对历史区间成立。把优化结果当作「未来更赚钱」的证据是典型误读。
可以拿优化权重直接下单吗?
不建议。库不处理交易成本、最小交易单位、涨跌停与流动性,且权重经过取整且和为 1 的约束并不严格。实际下单需要自己再做一轮约束转换与风控校验,本页内容仅供研究与技术评估。