深入解析 PyPortfolioOpt 和 Riskfolio-Lib 这两个专注于投资组合优化的 Python 库

好的,我们来深入解析 PyPortfolioOpt 和 Riskfolio-Lib 这两个专注于投资组合优化的 Python 库,并提供具体的代码示例。

目录

  1. 引言
  2. 核心对比维度
  3. PyPortfolioOpt 深入解析
  4. Riskfolio-Lib 深入解析
  5. 总结对比
  6. 选择建议
  7. 注意事项

1. 引言

PyPortfolioOpt 和 Riskfolio-Lib 是 Python 量化金融生态中进行投资组合优化的两大主力军。它们都旨在将复杂的优化理论转化为易于使用的代码接口,但各自的设计哲学、功能侧重和模型覆盖范围有所不同。本篇将对两者进行更深入的剖析和比较。

2. 核心对比维度

  • 模型覆盖范围: 支持的优化理论和具体模型。
  • 功能深度: 在收益/风险估计、约束处理、后处理等方面的细节功能。
  • API 设计与易用性: 代码接口的直观性、学习曲线。
  • 灵活性与扩展性: 自定义和集成的便利程度。
  • 风险管理侧重: 对不同风险度量(如波动率、CVaR、回撤)的支持程度。
  • 可视化: 内置图形化展示能力。
  • 社区与维护: 项目活跃度和支持情况。

3. PyPortfolioOpt 深入解析

3.1 核心优势与设计哲学

PyPortfolioOpt 的核心设计哲学是易用性对经典模型的良好实现。它旨在降低使用门槛,让用户能够快速、便捷地应用 Markowitz 均值-方差模型及其变种、Black-Litterman 模型以及 HRP 等常用方法。其 API 设计力求直观,贴近金融实践中的概念。

3.2 主要功能模块详解

  1. 预期收益估计 (expected_returns):

    • 提供多种计算预期年化收益率的方法:
      • mean_historical_return: 历史算术平均收益率。
      • ema_historical_return: 指数加权移动平均历史收益率。
      • capm_return: 基于资本资产定价模型 (CAPM) 的预期收益。
    • 允许用户传入自定义的预期收益向量。
  2. 风险模型 (risk_models):

    • 计算资产的协方差矩阵,这是风险度量(波动率)的基础。
    • 提供多种协方差矩阵估计方法:
      • sample_cov: 样本协方差矩阵(简单但可能不稳定)。
      • exp_cov: 指数加权移动协方差矩阵。
      • 收缩估计 (Shrinkage): 如 ledoit_wolf, oracle_approximating,通过向特定结构(如等相关矩阵)收缩来提高样本外稳定性,非常实用。
      • CovarianceShrinkage 类提供统一接口。
    • 支持半协方差 (semicovariance) 用于计算下行风险。
    • 支持协方差矩阵的各种修复方法(如处理非正定矩阵)。
  3. 优化器 (efficient_frontier, cla, hierarchical_portfolio, black_litterman):

    • EfficientFrontier: 核心模块,用于均值-方差优化。
      • 内置目标函数:max_sharpe(), min_volatility(), efficient_risk(), efficient_return()
      • 底层使用 scipy.optimize 进行二次规划求解。
      • 支持添加多种约束:权重上下限 (add_constraint, add_objective),头寸数量限制,行业/分组约束等。
    • CLA (Critical Line Algorithm): 精确计算整个有效前沿,无需预设风险或收益目标。
    • hierarchical_portfolio: 实现分层风险平价 (HRP) 算法。
    • black_litterman: 实现 Black-Litterman 模型,结合市场均衡和主观观点。
  4. 后处理 (discrete_allocation):

    • 将优化得到的理想(连续)权重转换为实际可交易的离散股数。
    • 提供基于整数规划 (cvxpy) 或贪心算法的方法。
    • 计算剩余未分配的现金。

3.3 优点

  • 易学易用: API 设计直观,文档清晰,示例丰富,学习曲线相对平缓。
  • 经典模型覆盖: 很好地实现了最常用、最经典的组合优化模型(MVO, BL, HRP)。
  • 健壮的协方差估计: 内置多种实用的协方差收缩方法,提高了优化结果的稳定性。
  • 良好的集成性: 易于与 Pandas 等数据科学库配合使用。
  • 实用的后处理: 离散分配功能非常贴合实际交易需求。

3.4 缺点

  • 模型相对有限: 对一些更现代或特定的风险模型(如 CVaR, CDaR, Omega Ratio 优化)支持不足。
  • 约束灵活性: 虽然支持多种约束,但在定义非常复杂的自定义约束时可能不如通用优化库灵活。
  • 风险度量单一: 主要围绕波动率(或下行偏差)作为风险度量。

3.5 代码示例:最大化夏普比率

import pandas as pd
import numpy as np
from pypfopt import EfficientFrontier, risk_models, expected_returns

# 1. 准备数据 (假设已有包含调整后收盘价的 DataFrame `prices`)
# 创建示例价格数据 (实际应加载真实数据)
dates = pd.date_range(start='2020-01-01', end='2022-12-31', freq='B') # 工作日
n_assets = 5
np.random.seed(0)
prices_data = np.random.randn(len(dates), n_assets).cumsum(axis=0) + 100
assets = [f'Asset_{i}' for i in range(n_assets)]
prices = pd.DataFrame(prices_data, index=dates, columns=assets)

# 2. 计算预期收益率和协方差矩阵
# 使用简单的历史均值和样本协方差
mu = expected_returns.mean_historical_return(prices)
# 使用更稳健的 Ledoit-Wolf 收缩协方差矩阵
S = risk_models.CovarianceShrinkage(prices).ledoit_wolf()

print("预期年化收益率:")
print(mu)
print("\n年化协方差矩阵 (Ledoit-Wolf):")
print(S.round(4))

# 3. 初始化 EfficientFrontier 对象
# 可以设置权重上下限,例如每个资产权重在 0 到 0.3 之间
ef = EfficientFrontier(mu, S, weight_bounds=(0, 0.3))

# 4. 执行优化:最大化夏普比率
# 假设无风险利率为 0.02
raw_weights = ef.max_sharpe(risk_free_rate=0.02)

# 清理权重(例如,去除极小的权重并重新归一化)
cleaned_weights = ef.clean_weights()

print("\n优化得到的原始权重:")
print(raw_weights) # 字典形式
print("\n清理后的权重:")
print(cleaned_weights) # 字典形式,更易读

# 5. 查看预期组合表现
expected_annual_return, annual_volatility, sharpe_ratio = ef.portfolio_performance(verbose=True, risk_free_rate=0.02)

# 6. (可选)离散分配
# 假设投资组合总金额为 100,000 美元,获取最近一天的价格
from pypfopt import DiscreteAllocation
latest_prices = prices.iloc[-1]
total_portfolio_value = 100000

da = DiscreteAllocation(cleaned_weights, latest_prices, total_portfolio_value=total_portfolio_value)
allocation, leftover = da.greedy_portfolio() # 使用贪心算法

print("\n离散分配 (股数):")
print(allocation)
print(f"剩余现金: ${leftover:.2f}")

4. Riskfolio-Lib 深入解析

4.1 核心优势与设计哲学

Riskfolio-Lib 的核心优势在于其模型的多样性和对风险的深度关注。它不仅包含经典模型,更着重于实现多种基于不同风险度量的优化方法(如 CVaR, CDaR)、风险平价策略以及基于聚类的现代组合构建技术 (HRP, HERC)。其设计围绕一个核心的 Portfolio 类展开,通过设置不同的参数来调用不同的模型和风险度量。

4.2 主要功能模块详解

  1. 核心类 (Portfolio):

    • 几乎所有的操作都围绕这个类进行。初始化时通常传入收益率 DataFrame (returns=...)。
    • 封装了数据处理、参数设置、优化执行和结果访问等功能。
  2. 资产分配模型 (model=... 参数):

    • 'Classic': 均值-方差类模型。可以通过 obj 参数指定目标函数(如 'Sharpe', 'MinRisk', 'Utility', 'MaxRet'),并通过 rm 参数指定风险度量。
    • 'BL': Black-Litterman 模型。
    • 'FM': 因子模型(需要提供因子数据)。
    • 'RP': 风险平价 (Risk Parity)。
    • 'RB': 风险预算 (Risk Budgeting),允许为资产或组别设定风险贡献目标。
    • 'HRP': 分层风险平价 (Hierarchical Risk Parity)。
    • 'HERC': 分层等风险贡献 (Hierarchical Equal Risk Contribution)。
    • 'NCO': Nested Clustered Optimization。
  3. 风险度量 (rm=... 参数):

    • 这是 Riskfolio-Lib 的一大特色,支持多种风险度量用于优化目标或约束:
      • 'MV' (Mean-Variance): 标准差/方差。
      • 'MAD' (Mean Absolute Deviation): 平均绝对离差。
      • 'MSV' (Mean Semi-Variance): 半方差(下行风险)。
      • 'FLPM''SLPM' (First/Second Lower Partial Moment): 低阶局部矩。
      • 'CVaR' (Conditional Value at Risk / Expected Shortfall): 条件风险价值,关注尾部损失均值。
      • 'CDaR' (Conditional Drawdown at Risk): 条件最大回撤风险。
      • 'EDaR' (Entropic Drawdown at Risk)。
      • 'UCI' (Ulcer Index): 溃疡指数,衡量回撤深度和持续时间。
      • 还有基于 Gini Mean Difference (GMD) 等的度量。
  4. 目标函数 (obj=... 参数):

    • 'MinRisk': 最小化指定的风险度量 (rm)。
    • 'Utility': 最大化效用函数(通常是 收益 - lambda * 风险)。
    • 'Sharpe': 最大化(广义)夏普比率(收益 / 风险度量)。
    • 'MaxRet': 在给定风险约束下最大化收益。
  5. 其他重要参数/方法:

    • hist: 是否使用历史情景法(对非正态分布、复杂风险度量如 CVaR 很重要)。
    • rf: 无风险利率。
    • l: 风险厌恶系数(用于 Utility 目标)。
    • method_mu: 预期收益估计方法。
    • method_cov: 协方差矩阵估计方法(同样支持收缩等)。
    • constraints: 添加各种约束。
    • plot_... 方法: 提供多种可视化功能,如有效前沿、风险贡献、聚类树状图等。

4.3 优点

  • 模型极其丰富: 覆盖范围远超 PyPortfolioOpt,尤其是在风险度量和现代组合理论方面。
  • 风险管理强大: 对 CVaR、CDaR 等尾部风险和回撤风险的优化支持是其核心优势。
  • 灵活性高: 通过参数组合可以实现非常多样化的优化目标和策略。
  • 因子模型支持: 内置了因子模型优化功能。
  • 聚类方法先进: 提供了 HRP, HERC, NCO 等基于聚类的先进配置方法。
  • 可视化能力强: 内置了多种实用的绘图功能。

4.4 缺点

  • 学习曲线较陡: 由于功能和参数众多,初学者可能需要更多时间来熟悉其 API 和概念。
  • API 封装度高: 有时不如 PyPortfolioOpt 的模块化设计直观,所有功能集中在 Portfolio 类中。
  • 文档有时需细读: 虽然文档也比较全,但理解各种参数组合的效果可能需要仔细阅读和实践。

4.5 代码示例:最小化条件风险价值 (CVaR)

import pandas as pd
import numpy as np
import riskfolio as rp  # 通常导入为 rp

# 1. 准备数据 (Riskfolio 通常直接使用收益率 DataFrame)
# 创建示例 **日收益率** 数据 (实际应加载真实数据)
dates = pd.date_range(start='2020-01-01', end='2022-12-31', freq='B')
n_assets = 5
np.random.seed(0)
# 模拟日收益率,均值略为正,有波动
returns_data = np.random.normal(loc=0.0005, scale=0.01, size=(len(dates), n_assets))
assets = [f'Asset_{i}' for i in range(n_assets)]
returns = pd.DataFrame(returns_data, index=dates, columns=assets)

print("日收益率数据 (前5行):")
print(returns.head())

# 2. 初始化 Portfolio 对象
port = rp.Portfolio(returns=returns)

# 3. 设置参数
# 计算预期收益和协方差(可选,不设置则使用历史数据)
# Riskfolio 提供了内置方法,也可以自己算好传入
port.assets_stats(method_mu='hist', method_cov='hist', d=0.94) # d 用于指数加权

# 4. 执行优化:使用经典模型框架,但目标是最小化 CVaR
# model='Classic': 使用均值-方差类框架
# rm='CVaR': 指定风险度量为条件风险价值 (Expected Shortfall)
# obj='MinRisk': 目标是最小化所选的风险度量 (CVaR)
# rf=0: 无风险利率(如果计算夏普类指标时需要)
# l=0: 风险厌恶系数(最小化风险时通常不需要)
# alpha=0.05: CVaR 的置信水平 (例如,关注最差的 5% 情况)
model = 'Classic'
rm = 'CVaR'
obj = 'MinRisk'
hist = True # 使用历史情景法计算 CVaR
alpha = 0.05 # 置信水平 for CVaR

# 添加权重约束(可选,例如多头,总和为1)
port.upper_risk_t = None # 如果需要约束组合风险上限
# port.lower_risk_t = None
# port.budget = 1 # 默认权重和为1
port.lower_w = np.array([0]*n_assets) # 权重下限为0 (多头)
port.upper_w = np.array([1]*n_assets) # 权重上限为1

# 运行优化
weights = port.optimization(model=model, rm=rm, obj=obj, rf=0, l=0, hist=hist, alpha=alpha)

print(f"\n优化得到的权重 (最小化 {alpha*100:.1f}% CVaR):")
print(weights.T) # 显示权重 DataFrame

# 5. (可选)查看其他结果或绘图
# 例如,绘制有效前沿(基于 CVaR)
# 注意:绘制有效前沿通常需要计算一系列点,这里只做了单点优化
# 可以通过 port.efficient_frontier() 计算前沿点
# rp.plot_frontier(w_frontier=port.frontier, mu=port.mu, cov=port.cov, returns=port.returns, rm=rm, rf=0, alpha=alpha, cmap='viridis', w=weights, label='Min CVaR Portfolio', marker='*', s=16, c='r', font_size=10)
# rp.plot_pie(w=weights, title='Portfolio Weights (Min CVaR)', others=0.05, nrow=25, cmap = "tab20", height=6, width=10, ax=None)
# (绘图代码需要 matplotlib 环境)

5. 总结对比

特性PyPortfolioOptRiskfolio-Lib
核心优势易用性,经典模型(MVO, BL, HRP)实现好模型丰富度(CVaR, CDaR, HERC等),风险管理深度
主要模型MVO, Black-Litterman, HRP, CLAMVO, BL, RP, RB, HRP, HERC, NCO, FM, 多种风险模型
风险度量主要基于波动率/半方差极其丰富 (StdDev, MAD, SemiVar, CVaR, CDaR, UCI…)
API 设计模块化,相对直观基于中心 Portfolio 类,参数驱动,功能强大但稍复杂
易用性较高,学习曲线平缓中等,需要时间熟悉参数和模型
协方差估计提供多种收缩方法也支持收缩等方法
因子模型不直接支持内置支持
聚类/分层模型HRPHRP, HERC, NCO
后处理离散分配 功能主要关注权重计算
可视化基本绘图(有效前沿、权重)更丰富(风险贡献、聚类树状图、密度图等)

6. 选择建议

  • 入门学习/教学/快速应用经典模型: 选择 PyPortfolioOpt。它的 API 更易上手,文档对经典模型解释清晰,可以快速实现常见的优化任务。
  • 需要高级风险管理/应用现代组合理论: 选择 Riskfolio-Lib。如果你需要用到 CVaR、CDaR 等尾部风险度量,或者对风险平价、因子模型、分层聚类方法(HERC, NCO)感兴趣,Riskfolio-Lib 提供了无与伦比的功能深度。
  • 研究导向/探索不同风险度量: Riskfolio-Lib 更适合,因为它提供了研究不同风险偏好下组合构建的可能性。
  • 需要将优化结果直接用于交易(股数分配): PyPortfolioOpt 的离散分配功能更方便。当然,也可以用 Riskfolio-Lib 算权重,再用 PyPortfolioOpt 或自己写逻辑做分配。

实践中,了解两者并根据具体项目的需求选用或结合使用是最佳策略。

7. 注意事项

  • 数据质量是关键: 任何优化库的输出质量都高度依赖于输入的预期收益和风险(协方差)数据的质量。“Garbage in, garbage out.”
  • 模型假设与局限: 理解每个优化模型背后的金融和数学假设至关重要。例如,均值-方差优化假设收益率为正态分布或投资者只有二次效用函数。
  • 参数敏感性: 优化结果对输入参数(尤其是预期收益)非常敏感。进行敏感性分析或使用更鲁棒的估计方法(如收缩协方差)和优化技术(如 Riskfolio-Lib 的鲁棒优化选项)非常重要。
  • 过拟合风险: 仅基于历史数据进行优化可能导致模型过拟合历史特定情况,样本外表现可能不佳。Black-Litterman 或因子模型等方法试图缓解这个问题。
  • 实际约束: 考虑交易成本、流动性、最小交易单位、税收等现实约束,这些通常需要在优化步骤之外或通过自定义约束加入(如果库支持)。

希望这份更深入的解析和代码示例能帮助你更好地理解和选择 PyPortfolioOpt 与 Riskfolio-Lib!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值