使用 Python 实践现代投资组合理论

翻译自:
https://www.tidy-finance.org/python/modern-portfolio-theory.html

在上一章中,我们展示了如何下载和分析股票市场数据,并通过图表和统计摘要进行分析。现在,我们转向金融领域的一个典型问题:投资者应该如何在预期收益、方差和相关性不同的资产之间分配财富,以优化其投资组合的表现?现代投资组合理论(MPT)由马科维茨(Markowitz,1952)引入,通过形式化风险与预期收益之间的权衡,彻底改变了我们对这类投资决策的思考方式。马科维茨的框架为现代金融的大部分内容奠定了基础,也为他赢得了 1990 年的瑞典国家银行经济学奖。

MPT 依赖于这样一个事实:投资组合的风险不仅取决于单个资产的波动性,还取决于资产收益之间的相关性。这一洞见突显了分散化的强大力量:将低相关性或负相关性的资产与给定投资组合结合,可以降低整体投资组合的风险。这一原则通常用“水果篮”来类比:如果你只有苹果,而苹果变质了,你就会失去一切。但如果是一个包含多种水果的篮子,即使有些水果变质了,其他水果仍然可以保持新鲜。

MPT 的核心是均值 - 方差分析,它从两个维度评估投资组合:预期收益和风险,风险定义为投资组合收益的方差。通过平衡这两个要素,投资者可以构建出在给定风险水平下最大化预期收益,或在期望收益水平下最小化风险的投资组合。在本章中,我们首先推导出最优投资组合决策,并在 R 中实现均值 - 方差方法。

在本章中,我们使用以下软件包:

1
2
3
4
5
6
7
import pandas as pd
import numpy as np
import yfinance as yf

from plotnine import *
from mizani.formatters import percent_format
from adjustText import adjust_text

我们引入 adjustText 软件包,用于在本章的图表中添加文本标签(Flyamer,2024)。

资产组合

假设投资者可以投资于 ( n ) 种不同的风险资产。每种资产 ( i ) 提供预期收益 ( \mu_i ),表示持有该资产一个时期所预期获得的利润。投资者可以通过选择每种资产的组合权重 ( w_i ) 来分配其财富。我们规定组合权重之和必须等于 1,以确保投资者完全投资。不存在将资金藏在床垫下之类的外部选择。本章的核心问题是:投资者应该如何在这些资产之间分配财富,以优化其投资组合的表现?

根据马科维茨(1952),投资组合选择涉及两个阶段:首先,基于观察和经验形成对未来证券表现的预期;其次,利用这些预期来选择投资组合。在实践中,这两个步骤无法分开。你需要历史数据或其他考虑因素来生成未来收益分布的估计值。只有这样,才能在估计基础上进行最优决策。为了概念上的简化,我们目前专注于后一部分,并假设资产收益的实际分布是已知的。在后续章节中,我们将讨论在考虑估计不确定性时出现的问题。为了提供一些有意义的说明,我们依赖历史数据来计算资产预期收益和收益方差 - 协方差的合理代理值,但我们将在假设这些是收益分布的真实参数的情况下进行工作。

因此,我们采用在“处理股票收益”中介绍的方法,下载道琼斯工业平均指数的成分股作为示例投资组合,以及它们的每日调整后收盘价。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import ssl
ssl._create_default_https_context = ssl._create_unverified_context

url = ("https://www.ssga.com/us/en/institutional/etfs/library-content/"
"products/fund-data/etfs/us/holdings-daily-us-en-dia.xlsx")

symbols = (pd.read_excel(url, skiprows=4, nrows=30)
.get("Ticker")
.tolist()
)

ssl._create_default_https_context = ssl.create_default_context

prices_daily = (yf.download(
tickers=symbols,
start="2000-01-01",
end="2023-12-31",
progress=False,
auto_adjust=False,
multi_level_index=False
))

prices_daily = (prices_daily
.stack()
.reset_index(level=1, drop=False)
.reset_index()
.rename(columns={"Ticker": "symbol", "Date": "date", "Adj Close": "adjusted"})
)

为了确保股票组合的稳定性并简化分析,我们确保所有股票在整个样本期内都有交易:

1
2
3
4
5
6
prices_daily = (prices_daily
.groupby("symbol")
.apply(lambda x: x.assign(counts=x["adjusted"].dropna().count()))
.reset_index(drop=True)
.query("counts == counts.max()")
)

我们计算样本平均收益\frac{1}{T} \sum_{t=1}^{T} r_{i,t}, 其中 r_{i,t} 是资产 i 在时期 t 的收益, T 是总时期数。正如前面提到的,我们将简单平均值向量视为资产的真实预期收益。为了简化计算并便于解释,我们从现在开始关注月度收益。

1
2
3
4
5
6
7
8
9
10
returns_monthly = (prices_daily
.assign(
date=prices_daily["date"].dt.to_period("M").dt.to_timestamp()
)
.groupby(["symbol", "date"], as_index=False)
.agg(adjusted=("adjusted", "last"))
.assign(
ret=lambda x: x.groupby("symbol")["adjusted"].pct_change()
)
)

在 MPT 中,单个资产的风险通常用方差(即 \sigma^2_i)或波动性(即\sigma_i)来量化。我们假设资产的真实波动性由样本标准差给出。

我们使用 std() 函数计算每个资产的样本标准差。

1
2
3
4
5
6
7
assets = (returns_monthly
.groupby("symbol", as_index=False)
.agg(
mu=("ret", "mean"),
sigma=("ret", "std")
)
)

我们可以在图 1 中展示资产收益的分布情况,以波动性为横轴,预期收益为纵轴。

1
2
3
4
5
6
7
8
9
10
11
(
ggplot(assets, aes(x="sigma", y="mu", label="symbol"))
+ geom_point()
+ geom_text(adjust_text={"arrowprops": {"arrowstyle": "-"}})
+ scale_x_continuous(labels=percent_format())
+ scale_y_continuous(labels=percent_format())
+ labs(
x="Volatility", y="Expected return",
title="道琼斯指数成分股的预期收益和波动性"
)
)


图 1:基于月度收益计算的预期收益和波动性,已调整股息支付和股票拆分。

正如前面提到的,MPT 的一个关键创新是考虑资产之间的相互作用。方差 - 协方差矩阵收集了这些信息。同样,我们用样本协方差来近似真实收益的方差-协方差矩阵Σ。

解释很简单:资产之间的正协方差表明这些资产倾向于同向变动,而负协方差则表明资产反向变动。

我们可以使用 cov() 函数,输入收益矩阵。因此,我们需要将收益从数据框转换为一个(T \times N)矩阵,每列代表一个符号,每行代表一个交易日。我们通过使用 pivot_wider() 实现这一点,将新列名从 symbol 列中获取,并将值设置为 ret

1
2
3
4
5
6
7
8
9
returns_wide = (returns_monthly
.pivot(index="date", columns="symbol", values="ret")
.reset_index()
)

sigma = (returns_wide
.drop(columns=["date"])
.cov()
)

图 2 展示了得到的方差 - 协方差矩阵。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
sigma_long = (sigma
.reset_index()
.melt(id_vars="symbol", var_name="symbol_b", value_name="value")
)

sigma_long["symbol_b"] = pd.Categorical(
sigma_long["symbol_b"],
categories=sigma_long["symbol_b"].unique()[::-1],
ordered=True
)

(
ggplot(sigma_long, aes(x="symbol", y="symbol_b", fill="value"))
+ geom_tile()
+ labs(
x="", y="", fill="(Co-)Variance",
title="道琼斯指数成分股的方差 - 协方差矩阵"
)
+ scale_fill_continuous(labels=percent_format())
+ theme(axis_text_x=element_text(angle=45, hjust=1))
)

图 2:基于月度收益计算的方差和协方差,已调整股息支付和股票拆分。

最小方差框架

假设投资者将财富分配到由权重向量 \omega 给出的投资组合中。得到的投资组合收益 \omega^\prime r 的预期收益为\mu_\omega = \omega^{\prime}\mu = \sum_{i=1}^N \omega_i \mu_i。投资组合收益的方差为\sigma^2_\omega = \omega^{\prime}\Sigma\omega = \sum_{i=1}^{N} \sum_{j=1}^{N} \omega_i \omega_j \sigma_{ij},其中 \omega_i 和\omega_j分别是资产 i 和 j 在投资组合中的权重,\sigma_{ij} 是资产 i 和 j 收益之间的协方差。

我们首先考虑一个投资者,他希望投资于一个方差最低的投资组合作为参考点。因此,最小方差投资者的优化问题为

\min_{\omega_1, … \omega_n} \sum_{i=1}^{n} \sum_{j=1}^{n} \omega_i \omega_j \sigma_{ij} = \min_\omega \omega^{\prime}\Sigma\omega.

在完全投资于所有可用资产的条件下,\sum_{i=1}^{N} \omega_i = 1。最小方差投资组合的解析解为

\omega_\text{mvp} = \frac{\Sigma^{-1}\iota}{\iota^{\prime}\Sigma^{-1}\iota},

其中 \iota 是一个全 1 向量,\Sigma^{-1} 是方差 - 协方差矩阵 \Sigma 的逆矩阵。有关推导的详细信息,请参见附录中的证明。在下面的代码块中,我们计算最小方差投资组合的权重:

1
2
3
iota = np.ones(sigma.shape[0])
sigma_inv = np.linalg.inv(sigma.values)
omega_mvp = (sigma_inv @ iota) / (iota @ sigma_inv @ iota)

图 3 展示了得到的投资组合权重。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
assets = assets.assign(omega_mvp=omega_mvp)

assets["symbol"] = pd.Categorical(
assets["symbol"],
categories=assets.sort_values("omega_mvp")["symbol"],
ordered=True
)

(
ggplot(assets,
aes(y="omega_mvp", x="symbol", fill="omega_mvp>0"))
+ geom_col()
+ coord_flip()
+ scale_y_continuous(labels=percent_format())
+ labs(x="", y="",
title="最小方差投资组合权重")
+ theme(legend_position="none")
)

图 3:权重基于月度收益的历史时刻,已调整股息支付和股票拆分。

在继续讨论其他投资组合之前,我们将最小方差投资组合的收益和波动性收集到一个数据框中:

1
2
3
4
5
6
7
8
9
mu = assets["mu"].values
mu_mvp = omega_mvp @ mu
sigma_mvp = np.sqrt(omega_mvp @ sigma.values @ omega_mvp)
summary_mvp = pd.DataFrame({
"mu": [mu_mvp],
"sigma": [sigma_mvp],
"type": ["最小方差投资组合"]
})
summary_mvp
mu sigma type
0 0.008356 0.032144 最小方差投资组合

有效投资组合

在许多情况下,实现最低可能的方差可能并非期望的结果。相反,我们将有效投资组合的概念进行推广,在最小化投资组合方差的同时,投资者还希望获得一个 最低预期收益 \omega^{\prime}\mu \geq \bar{\mu}. 。换句话说,当 \bar\mu\geq \omega_\text{mvp}^{\prime}\mu 时,投资者愿意接受更高的投资组合方差以换取更高的预期收益。

例如,假设投资者希望获得至少与过去平均收益最高的资产相同的平均收益:

1
mu_bar = assets["mu"].max()

形式上,优化问题可以表示为

\min_\omega \omega^{\prime}\Sigma\omega \text{ s.t. } \omega^{\prime}\iota = 1 \text{ and } \omega^{\prime}\mu\geq\bar\mu.

有效投资组合的解析解可以推导为:

\omega_{efp} = \frac{\lambda^*}{2}\left(\Sigma^{-1}\mu -\frac{D}{C}\Sigma^{-1}\iota \right),

其中 \lambda^* = 2\frac{\bar\mu - D/C}{E-D^2/C}, C = \iota’\Sigma^{-1}\iota, D=\iota’\Sigma^{-1}\mu 以及 E=\mu’\Sigma^{-1}\mu。有关详细信息,请再次参阅附录中的证明。

下面的代码实现了这一优化问题的解析解,并将得到的投资组合收益和风险收集到一个数据框中。

1
2
3
4
5
6
7
8
9
10
11
12
13
C = iota @ sigma_inv @ iota
D = iota @ sigma_inv @ mu
E = mu @ sigma_inv @ mu
lambda_tilde = 2 * (mu_bar - D / C) / (E - (D ** 2) / C)
omega_efp = omega_mvp + (lambda_tilde / 2) * (sigma_inv @ mu - D * omega_mvp)
mu_efp = omega_efp @ mu
sigma_efp = np.sqrt(omega_efp @ sigma.values @ omega_efp)

summary_efp = pd.DataFrame({
"mu": [mu_efp],
"sigma": [sigma_efp],
"type": ["有效投资组合"]
})

图 4 展示了最小方差和有效投资组合的平均收益与波动性相对于指数成分股的情况。正如预期的那样,有效投资组合的预期收益更高,但波动性也高于最小方差投资组合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
summaries = pd.concat(
[assets, summary_mvp, summary_efp], ignore_index=True
)

(
ggplot(summaries,
aes(x="sigma", y="mu"))
+ geom_point(
data=summaries.query("type.isna()")
)
+ geom_point(
data=summaries.query("type.notna()"), color="#F21A00", size=3
)
+ geom_label(aes(label="type"), adjust_text={"arrowprops": {"arrowstyle": "-"}})
+ scale_x_continuous(labels=percent_format())
+ scale_y_continuous(labels=percent_format())
+ labs(
x="Volatility", y="Expected return",
title="有效投资组合与最小方差投资组合"
)
)

图 4:大圆点表示最小方差投资组合和有效投资组合的位置,后者实现了与过去收益最高的股票相同的预期收益。小圆点表示各个成分股的位置。

该图说明了显著的分散化收益:与其将所有财富分配到过去平均收益最高的单一资产中(伴随着巨大的波动性),有效投资组合承诺实现相同的预期收益,但波动性要低得多。

需要注意的是,期望收益水平 \bar\mu 反映了投资者的风险偏好。风险偏好较低的投资者可能需要更高的期望收益水平。相比之下,风险偏好较高的投资者可能只选择接近最小方差投资组合期望收益的 \bar\mu。通常,均值 - 方差框架被推导为具有均值 - 方差效用函数和相对风险厌恶系数 \gamma 的投资者的最优决策框架。在附录的证明中,我们表明 \gamma 与期望收益水平 \bar\mu 之间存在一一对应关系,这意味着得到的有效投资组合是等价的,并且不依赖于优化问题的表述方式。

有效前沿

满足条件的投资组合集合,即不存在其他投资组合在给定波动性水平下具有更高的预期收益,被称为 有效前沿 ,参见例如默顿(Merton,1972)。为了推导出构成有效前沿的投资组合,共同基金分离定理非常有帮助。简而言之,该定理指出,一旦我们有了两个有效投资组合(例如最小方差投资组合 \omega_\text{mvp} 和更高期望收益水平的有效投资组合 \omega_\bar\mu ),我们可以通过组合这两个投资组合来描述整个有效前沿。也就是说,这两个投资组合权重的任何线性组合都将再次代表一个有效投资组合。换句话说,有效前沿可以通过以下方程描述:

\omega_{a\mu_1 + (1-a)\mu_2} = a \cdot \omega_{\mu_1} + (1-a) \cdot\omega_{\mu_2},

其中 a 是介于 0 和 1 之间的标量,\omega_{\mu_i} 是实现期望收益 \mu_i 的有效投资组合。证明该定理是很容易的。考虑实现期望收益 \mu_i 的有效投资组合的解析解,这意味着:

a \cdot \omega_{\mu_1} + (1-a) \cdot\omega_{\mu_2} = \left(\frac{a\mu_1 + (1-a)\mu_2- D/C }{E-D^2/C}\right)\left(\Sigma^{-1}\mu -\frac{D}{C}\Sigma^{-1}\iota \right),

这对应于期望收益为 a\mu_1 + (1-a)\mu_2 的有效投资组合。

下面的代码实现了有效前沿的构建,该前沿描述了在每个风险水平下可实现的最高预期收益。

1
2
3
4
5
6
7
8
9
10
11
12
efficient_frontier = (
pd.DataFrame({
"a": np.arange(-1, 2.01, 0.01)
})
.assign(
omega=lambda x: x["a"].map(lambda x: x * omega_efp + (1 - x) * omega_mvp)
)
.assign(
mu=lambda x: x["omega"].map(lambda x: x @ mu),
sigma=lambda x: x["omega"].map(lambda x: np.sqrt(x @ sigma @ x))
)
)

最后,使用 ggplot 绘制图表(见图 5)来展示有效前沿以及两个有效投资组合非常简单。我们还将各个股票添加到同一张图中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

summaries = pd.concat(
[summaries, efficient_frontier], ignore_index=True
)

(
ggplot(summaries,
aes(x="sigma", y="mu"))
+ geom_point(
data = summaries.query("type.isna()")
)
+ geom_point(
data = summaries.query("type.notna()"), color="#F21A00", size=3
)
+ geom_label(aes(label="type"), adjust_text={"arrowprops": {"arrowstyle": "-"}})
+ scale_x_continuous(labels=percent_format())
+ scale_y_continuous(labels=percent_format())
+ labs(
x="Volatility", y="Expected return",
title="有效投资组合与最小方差投资组合"
)
)

图 5:大圆点表示最小方差投资组合和有效投资组合的位置,后者实现了最小方差投资组合期望收益的三倍。小圆点表示各个成分股的位置。

关键要点

均值 - 方差框架是现代金融的基石,强调风险与收益之间的权衡。本章的关键要点包括:

  • 均值 - 方差优化在预期收益与预期投资组合风险之间进行平衡。
  • 投资组合风险既取决于波动性,也取决于资产之间的相关性。
  • 最小方差投资组合在给定一组资产的情况下实现了最低可能的风险。
  • 有效投资组合在每个风险水平下最大化预期收益。

练习

  1. 我们将样本限制为自 2000 年 1 月 1 日以来每天都有交易的资产。讨论这种限制可能引入幸存者偏差的原因,以及它如何影响对未来预期投资组合表现的推断。
  2. 有效前沿描述了在不同风险水平下具有最高预期收益的投资组合。确定单位标准差(风险)预期收益最高的投资组合。著名的业绩衡量指标中,哪一个对应于平均收益与标准差的比率?
  3. 分析资产收益之间的相关系数变化如何影响有效前沿的形状。使用少量资产的假设数据进行可视化,并解释这些变化。

江达小记