跳到主内容
研究主线资产定价与组合构建 · 第30–36讲4/7模块导览 →
讲义

第33讲:投资组合分析 (Portfolio Analysis)

计算组合收益、夏普比率、回撤和市场 Beta,绘制滚动风险图,并说明频率、基准与旧分析库限制。

完成状态保存在当前浏览器,可随时取消。

投资组合分析是量化金融领域的核心学科,旨在通过科学的方法评估和管理投资组合的风险与收益。在当今复杂多变的金融市场中,投资者面临着资产价格波动、宏观经济不确定性以及日益增长的市场数据量等挑战。传统的经验判断已不足以应对这些问题,因此,数据驱动的投资组合分析工具成为优化资产配置、评估绩效并有效控制风险的关键。本章旨在为读者提供一个系统化的学习路径,通过现代Python工具的应用,帮助您从零开始掌握投资组合分析的理论与实践。

通过本章的学习,您将能够实现以下目标:

  • 掌握数据获取技能:利用yfinance从Yahoo Finance获取实时金融市场数据,并进行预处理。

  • 理解核心绩效指标:计算并解读夏普比率、最大回撤、市场贝塔等关键指标,评估投资组合的表现。

  • 进行专业可视化分析:使用pyfolio生成直观的图表,深入剖析投资组合的收益与风险特征。

  • 应用高级风险分析技术:探索回撤分析、因子暴露分析等方法,提升风险管理能力。

  • 通过实战练习巩固知识:结合实际案例和诊断任务,将理论转化为解决现实问题的能力。

1. 环境准备与数据获取#

在进行投资组合分析之前,我们需要搭建一个功能强大的Python环境,并获取可靠的金融市场数据。这一小节将详细介绍所需工具的安装和数据获取的完整流程。

1.1 安装必要库#

为了完成本章的任务,您需要安装以下Python库。这些库各有其独特的功能,共同构成了投资组合分析的技术基础:

  • yfinance:一个轻量级但功能强大的库,用于从Yahoo Finance下载金融数据。它支持股票、指数、ETF等多种资产类型的数据获取。

  • pyfolio:专为投资组合分析设计的开源工具,提供绩效评估、可视化图表和风险分析功能,是量化投资者的得力助手。

  • empyrical:专注于风险和绩效指标计算的库,支持夏普比率、最大回撤、贝塔等常用指标的高效计算。

  • matplotlib:Python中最流行的绘图库,用于创建各种静态和动态图表,帮助我们直观展示分析结果。

安装这些库的命令如下,在终端或命令行中运行:

bash

code
pip install yfinance pyfolio empyrical matplotlib

安装注意事项:

  • 确保您的Python版本在3.7或以上,以避免兼容性问题。

  • 如果遇到安装失败,可以尝试使用pip install --upgrade pip更新pip后重试。

1.2 使用yfinance获取数据#

以下是一个通过yfinance具体的示例,展示如何获取标普500指数(SPY)以及个股(如苹果AAPL和微软MSFT)的收盘价数据,并计算日收益率:

code
import yfinance as yf
import pandas as pd

# 获取标普500和个股数据
start = '2014-01-01'
end = '2015-01-01'
benchmark = yf.download('SPY', start=start, end=end)['Close']
assets = yf.download(['AAPL', 'MSFT'], start=start, end=end)['Close']

# 计算收益率
benchmark_rets = benchmark.pct_change().dropna()
portfolio_rets = assets.pct_change().mean(axis=1).dropna()  # 假设等权投资

数据检查与预处理:

  • yfinance返回的数据可能包含多级列索引(如Close, Open等),使用['Close']可以简化结构。

  • 确保日期索引为datetime格式。如果不是,可通过以下代码修正:assets.index = pd.to_datetime(assets.index)

  • 如果需要查看数据的前几行,可以使用print(assets.head())进行检查。


2. 关键绩效指标解析#

投资组合的绩效评估依赖于一系列关键指标,这些指标帮助我们量化收益与风险的关系。以下是对三个核心指标的详细解析,包括定义、公式、代码实现和解读。

2.1 夏普比率(Sharpe Ratio)#

夏普比率是由诺贝尔经济学奖得主威廉·夏普提出的经典指标,用于衡量投资组合在承担单位风险时获得的超额收益。它是风险调整后收益的黄金标准。

公式

Sharpe=E(RpRf)σpSharpe = \frac{E(R_p - R_f)}{\sigma_p}
  • RpR_p: 投资组合的预期收益率(通常用历史平均收益率近似)。

  • RfR_f: 无风险利率(短期分析中常简化为0,长期分析可参考美国10年期国债收益率)。

  • σpσ_p: 投资组合收益率的标准差,表示波动率或总风险。

code
from empyrical import sharpe_ratio

# 计算夏普比率(假设无风险利率为0)
sharpe = sharpe_ratio(portfolio_rets)
print(f"夏普比率: {sharpe:.2f}")

解读:

  • 正值:夏普比率大于0表明投资组合收益率超过无风险利率;数值越大,表现越优。

  • 经验阈值:通常,夏普比率大于1被认为是不错的表现,大于2则非常优秀。

  • 局限性:夏普比率假设收益服从正态分布,对非对称分布或极端事件(如市场崩盘)的敏感度较低。

2.2 最大回撤(Max Drawdown)#

最大回撤衡量投资组合从历史最高点到随后最低点所经历的最大亏损幅度,是评估极端风险的重要指标。它直接反映了投资者可能面临的最坏情况。

公式:

最大回撤通常通过计算累积收益率曲线的高点与低点之差来确定:

Max Drawdown=PpeakPtroughPpeak\text{Max Drawdown} = \frac{P_{\text{peak}} - P_{\text{trough}}}{P_{\text{peak}}}

代码实现:

code
from empyrical import max_drawdown
dd = max_drawdown(portfolio_rets)
print(f"最大回撤: {abs(dd)*100:.1f}%")

解读:

  • 数值意义:例如,最大回撤为-20%,表示投资组合在最差时期损失了20%的价值。

  • 投资决策:回撤越小,表明投资组合在市场下跌时的抗风险能力越强。投资者通常希望将其控制在心理承受范围内(如10%-20%)。

  • 局限性:最大回撤只关注单一最大损失事件,不反映回撤的持续时间或频率。

2.3 市场贝塔(Beta)#

市场贝塔衡量投资组合相对于市场(如标普500)的系统性风险,反映其对市场整体波动的敏感度。它是资本资产定价模型(CAPM)中的核心参数。

公式

β=Cov(rp,rm)Var(rm)\beta = \frac{Cov(r_p, r_m)}{Var(r_m)}

其中:

β\beta 表示贝塔系数

Cov(rp,rm)Cov(r_p, r_m) 表示投资组合收益率与市场收益率的协方差

Var(rm)Var(r_m)表示市场收益率的方差

rpr_p表示投资组合收益率

rmr_m 表示市场收益率

code
from empyrical import beta
market_beta = beta(portfolio_rets, benchmark_rets)
print(f"市场贝塔: {market_beta:.2f}")

解读:

  • β=1\beta = 1:投资组合与市场波动一致。

  • β>1\beta > 1:投资组合波动大于市场(如1.5表示市场涨跌1%,组合涨跌1.5%),风险较高。

  • β<1\beta < 1:投资组合波动小于市场(如0.8表示市场涨跌1%,组合涨跌0.8%),风险较低。

  • 负值:β<0\beta < 0表示投资组合与市场反向波动,常见于对冲策略。

小练习:计算组合的年化波动率#

任务:计算投资组合的年化波动率,并以百分比形式输出,作为风险的直观度量。 代码实现:

code
# 提示:使用empyrical.annual_volatility()

3. 可视化分析#

可视化分析是将复杂数据转化为直观洞见的关键步骤。以下介绍三种常用的可视化方法,帮助您深入理解投资组合的表现和风险。

3.1 累积收益对比#

通过绘制投资组合与基准(如标普500)的累积收益率曲线,评估其相对表现和长期趋势。

代码实现:

code
import pyfolio as pf
import matplotlib.pyplot as plt

# 创建双图布局
fig, ax = plt.subplots(2, 1, figsize=(10, 8))

# 绘制滚动累积收益率
pf.plotting.plot_rolling_returns(portfolio_rets, benchmark_rets, ax=ax[0])
ax[0].set_title("滚动累积收益率对比")

# 绘制日收益率
pf.plotting.plot_returns(portfolio_rets, ax=ax[1])
ax[1].set_title("日收益率")

plt.tight_layout()
plt.show()

说明:

  • 上图(滚动累积收益率):展示投资组合与基准从起始点累积的收益,适合观察长期趋势。

  • 下图(日收益率):展示每日收益的波动情况,帮助识别短期风险点。

  • 自定义:figsize=(10, 8)调整图表大小,tight_layout()优化布局。

3.2 月度收益热力图#

通过热力图展示投资组合的月度收益分布,快速识别高收益或亏损月份,以及潜在的季节性模式。

code
pf.plot_monthly_returns_heatmap(portfolio_rets)
plt.title("月度收益热力图")
plt.show()

说明:

  • 颜色编码:深绿色表示高收益,深红色表示大亏损,浅色表示接近零的收益。

  • 应用场景:如果某个月份持续表现不佳,可能提示季节性风险或策略缺陷。

3.3 滚动指标分析#

分析投资组合的滚动贝塔和滚动夏普比率,观察其风险和绩效随时间的变化:

code
fig, axes = plt.subplots(2, 1, figsize=(10, 8))

# 绘制滚动贝塔
pf.plot_rolling_beta(portfolio_rets, benchmark_rets, ax=axes[0])
axes[0].set_title("滚动贝塔")

# 绘制滚动夏普比率
pf.plot_rolling_sharpe(portfolio_rets, ax=axes[1])
axes[1].set_title("滚动夏普比率")

plt.tight_layout()
plt.show()

说明:

  • 滚动贝塔:使用固定窗口(如6个月)计算投资组合对市场的动态敏感度,揭示风险暴露的变化。

  • 滚动夏普比率:反映风险调整后收益的稳定性,若波动较大,可能提示策略的不一致性。

4. 高级风险分析#

在掌握基本指标和可视化后,我们进一步探索高级风险分析技术,以更全面地评估投资组合。

4.1 回撤分析矩阵#

识别投资组合历史上主要的回撤周期,分析其在不同市场环境下的损失特征:

code
pf.plot_drawdown_periods(portfolio_rets)
plt.title("回撤周期分析")
plt.show()

说明:

  • 图表内容:展示每次回撤的起止时间和幅度,突出最大回撤期。

  • 应用:帮助投资者评估策略在压力情景下的表现,优化止损规则。

4.2 因子暴露分析#

基于Fama-French三因子模型,分析投资组合对市场风险、规模因子(SMB)和价值因子(HML)的暴露,揭示其风格特征:

code
pf.plot_rolling_fama_french(portfolio_rets)
plt.title("Fama-French 三因子滚动暴露")
plt.show()

说明:

  • 因子意义:

    • 市场因子:投资组合对整体市场的敏感度。

    • SMB(小盘-大盘):正暴露表示倾向小盘股,反之倾向大盘股。

    • HML(高账面市值比-低账面市值比):正暴露表示倾向价值股。

  • 用途:识别策略是否偏离预期风格,调整资产配置。

风险指标对比分析#

指标适用场景优点局限性
夏普比率收益近似正态分布简单易用,广泛接受对尾部风险(如黑天鹅事件)不敏感
索提诺比率关注下行风险的投资者聚焦损失,适合风险厌恶者需定义最低可接受回报,计算复杂
最大回撤评估最坏情况下的损失直观反映极端风险只关注单一事件,忽略频率和恢复时间

选择建议:

  • 如果您的收益分布较为平稳,优先使用夏普比率。

  • 如果更关心下跌风险,推荐索提诺比率。

  • 若需评估最坏情景,最大回撤是最佳选择。

5. 组合诊断练习#

通过一个模拟案例,我们将理论应用于实践,强化您的分析能力:

案例数据集

生成一个模拟的投资组合收益率数据:

code
# 生成模拟数据
import numpy as np
dates = pd.date_range('2020-01-01', periods=500)
returns = pd.Series(np.random.normal(0.0005, 0.01, 500), index=dates)

诊断任务

  1. 任务1:计算年化夏普比率
code
from empyrical import sharpe_ratio

sharpe = sharpe_ratio(returns, annualization=252)
print(f"年化夏普比率: {sharpe:.2f}")
  • 说明:annualization=252将日夏普比率年化,结果反映每单位风险的收益水平。
  1. 任务2:绘制滚动贝塔(对比沪深300)
  • 获取沪深300指数数据:
code
csi300 = yf.download('000300.SS', start='2020-01-01', end='2021-05-01')['Close'].pct_change().dropna()
  • 确保时间对齐并绘制滚动贝塔:
code
csi300 = csi300.reindex(returns.index).dropna()
returns_aligned = returns.reindex(csi300.index).dropna()
pf.plot_rolling_beta(returns_aligned, csi300)
plt.title("滚动贝塔(对比沪深300)")
plt.show()
  • 说明:滚动贝塔揭示投资组合对A股市场的动态敏感度。
  1. 任务3:识别最大回撤期
code
pf.plot_drawdown_periods(returns)
plt.title("最大回撤周期")
plt.show()
  • 说明:图表标注了回撤的起止点和幅度,便于识别高风险时段。

附:yfinance高级用法#

code
# 多资产批量下载
data = yf.download(['AAPL', 'MSFT', 'TSLA'],
                   start='2020-01-01',
                   end='2023-01-01',
                   group_by='ticker')

# 数据重整
aapl = data['AAPL']['Close'].rename('AAPL')
msft = data['MSFT']['Close'].rename('MSFT')
portfolio = pd.concat([aapl, msft], axis=1)

本节使用现代 Python 工具完成组合收益、风险与基准比较。实践时可逐步加入:

  1. 交易成本计算

  2. 组合优化模块

  3. 压力测试场景分析