利用Python进行「基金投资组合优化」(一)

引言

还记得今年年初,A股行情火热的样子吗。我把年终奖投进了🔥热门网红基金里,然后就是绿油油的大半年😢。

最近在外网看了一篇关于股票投资组合优化的文章,于是想着可以把基金投资组合也评估一下,帮助自己复盘,优化投资组合,减少韭菜时刻😢,所以就有了这篇博文,介绍关于基金投资组合优化的一些基础做法。

ps.本文不含任何产品推荐,纯思路分享。

主要思路

投资组合

本文讨论的对象是投资者对不同基金的投资组合,暂不包含对股票、债券、房地产等其他类型资产。

积极的投资组合管理,就是通过战略性地买卖资产,以击败大盘。

现代投资组合理论(MPT)

现代投资组合理论,或也称为均值方差分析,由H. Markowitz制定。MPT的目标是在给定的风险水平下,获取最大化回报。

投资组合中的每一项资产都有自己的预期收益率和风险。通过创建多种资产组合,为预先定义的风险水平提供高回报。同样,可以有多个投资组合为预定义的预期回报提供最低风险。

波动率

价格的变化是该资产波动性(回报如何波动)的重要指标。资产回报的方差计算如下:

s 2 = ∑ i = 1 N ( x i – x ˉ ) 2 / N − 1 s^2 = \sum_{i=1}^N (x_i – \bar{x})^2 / N-1 s2=i=1N(xixˉ)2/N1

波动率被衡量为该资产回报的标准偏差,即方差的平方根。

s = ∑ i = 1 N ( x i – x ˉ ) 2 / N − 1 s = \sqrt{ \sum_{i=1}^N (x_i – \bar{x})^2 / N-1} s=i=1N(xixˉ)2/N1

协方差

协方差用于衡量两种资产回报之间的方向关系。

  • 正协方差,意味着两种资产的回报一起移动。
  • 负协方差,意味着它们反向移动。

通过配置具有负协方差的资产,可以降低投资组合中的风险和波动性。

关于协方差、样本协方差、协方差矩阵、相关系数的详解,可以看这篇博文:
https://blog.csdn.net/Bit_Coders/article/details/115897074

权重分配

每个资产类别的占比,由权重数组 w = [ w 1 , w 2 , . . . , w n ] w=[w_1,w_2,...,w_n] w=[w1,w2,...,wn] 表示。权重数组加和为1。

因此,投资组合优化的问题就是:找到最大化预期收益,同时最小化风险(标准差)的权重最佳值。

投资组合期望回报

资产的期望回报只是其资产价格百分比变化的平均值

投资组合的总预期收益由下式给出:

E ( R p ) = w 1 E ( R 1 ) + w 2 E ( R 2 ) + . . . . . w n E ( R n ) E(R_p) = w_1E(R_1) + w_2E(R_2) + ..... w_nE(R_n) E(Rp)=w1E(R1)+w2E(R2)+.....wnE(Rn)

投资组合方差

σ 2 ( R p ) = ∑ i = 1 n ∑ j = 1 n w i w j C O V ( R i , R j ) \sigma^2(R p) = \sum {i=1}^{n} \sum_{j=1}^{n} w_i w_j COV(R_i, R_j) σ2(Rp)=i=1nj=1nwiwjCOV(Ri,Rj)
其中, w i w_i wi w j w_j wj 表示从 第1 ~ n 资产的权重,而 COV(Ri, Rj) 是由 i 和 j 表示的两个资产的协方差。

代码实践

下面我们以近几年的几款网红基金产品为例,来模拟分析一下这个投资组合。

获取基金净值的变化情况

df = pd.DataFrame()
start_date = '2018-10-22'
end_date = '2021-10-22'
fund_codes = ["005827","001714","270042"]#基金代码 005827:易方达蓝筹,001714:工银瑞信文体产业股票A,513300:纳斯达克100ETF
for fund_code in fund_codes:
	tmp_data = get_fund_data(fund_code, per=49, sdate=start_date, edate=end_date)
	print(f"{fund_code}:\n{tmp_data.head()}")
	# 缺失值填充
	tmp_data = tmp_data.fillna({"日增长率":"0%"})#tmp_data.dropna()
	tmp_data = tmp_data.replace("nan","0%")
	df["date"]=tmp_data["净值日期"]
	df['date']=pd.to_datetime(df['date'])
	df["change_"+fund_code]=tmp_data["日增长率"].apply(lambda x: x[:-1]).astype(float)
	df["log_change_"+fund_code]=df["change_"+fund_code].apply(lambda x: np.log(1 + x))
	df[fund_code]=tmp_data["累计净值"].astype(float)

# 增加“年”字段
df['year'] = pd.to_datetime(df['date'])
for i in range(df.shape[0]):
    df['year'][i]=pd.to_datetime(df['date'])[i].year
print(f"Shape:{df.shape}")
print(df.head())

爬取到的基金数据格式如下:

005827:
净值日期 单位净值 累计净值 日增长率 申购状态 赎回状态 分红送配
0 2021-10-22 2.7102 2.7102 1.13% 限制大额申购 开放赎回 nan
1 2021-10-21 2.6798 2.6798 0.30% 限制大额申购 开放赎回 nan
2 2021-10-20 2.6718 2.6718 0.38% 限制大额申购 开放赎回 nan
3 2021-10-19 2.6617 2.6617 1.56% 限制大额申购 开放赎回 nan
4 2021-10-18 2.6209 2.6209 -3.20% 限制大额申购 开放赎回 nan
001714:
净值日期 单位净值 累计净值 日增长率 申购状态 赎回状态 分红送配
0 2021-10-22 3.5020 3.6910 0.66% 限制大额申购 开放赎回 nan
1 2021-10-21 3.4790 3.6680 -0.23% 限制大额申购 开放赎回 nan
2 2021-10-20 3.4870 3.6760 -0.66% 限制大额申购 开放赎回 nan
3 2021-10-19 3.5100 3.6990 0.54% 限制大额申购 开放赎回 nan
4 2021-10-18 3.4910 3.6800 -0.99% 限制大额申购 开放赎回 nan
270042:
净值日期 单位净值 累计净值 日增长率 申购状态 赎回状态 分红送配
0 2021-10-21 4.3667 4.6367 0.37% 开放申购 开放赎回 nan
1 2021-10-20 4.3507 4.6207 -0.51% 开放申购 开放赎回 nan
2 2021-10-19 4.3729 4.6429 0.73% 开放申购 开放赎回 nan
3 2021-10-18 4.3410 4.6110 0.89% 开放申购 开放赎回 nan
4 2021-10-15 4.3027 4.5727 0.60% 开放申购 开放赎回 nan

对列数据进行处理后:
在这里插入图片描述

计算基金的波动率

for code in ['005827','001714','270042']:
    idxname = 'change_'+code
    var_data = change_df[idxname].var()
    vol = np.sqrt(var_data*250)
    print(f"{code}的波动率:{vol}")

输出:

在这里插入图片描述
可以看出,坤坤的蓝筹在近一年的波动率是28%左右,是三支基金中最高的。

比较基金之间的相关性

投资组合优化中的常见做法是,获取收益或收益对数来计算协方差和相关性。

# 计算相关性 Correlation 
cov_matrix = change_df.cov()
corr_matrix = change_df.corr()
print("\n资产相关矩阵:",corr_matrix)

在这里插入图片描述
可以看出坤坤的蓝筹和芳芳的文体股票相关性较高,都是大盘成长型产品。

纳斯达克100的产品与前两个产品相关性较弱,可以配置一些用于降低风险。

计算年期望收益

获取每个资产的年期望收益:

t = df.set_index('date')
ann = t.groupby(pd.Grouper(freq='Y'))[["change_005827","change_001714","change_270042"]].mean()

计算组合期望收益

假设 w = [ 0.6 , 0.3 , 0.1 ] w=[0.6,0.3,0.1] w=[0.6,0.3,0.1] 为一组资产配置权重,我们可以计算这个资产组合的期望回报:

# 年期望回报
w = [0.6,0.3,0.1]
ind_er = ann[1:].mean()

# 投资组合期望回报
port_er = (w * ind_er).sum()
print("\n组合期望回报:",port_er)

输出:

组合期望回报: 0.14724326989026063

利用有效边界进行投资组合优化

通过对权重数组 w w w 随机采样不同的数值,可以得到大量的投资组合,将每一个 w w w 对应的期望回报(纵轴)和波动率(横轴)计算出来,绘制成散点图。

portfolios.plot.scatter(x='Volatility', y='Returns', marker='o', s=10, alpha=0.4, grid=True, figsize=[10,10])

可以在图中,明显看到一个清晰的有效边界,靠近边界越近的投资组合,优化效果越好。
在这里插入图片描述
如果我们想要建立一个波动率最小的投资组合:

min_vol_port = portfolios.iloc[portfolios['Volatility'].idxmin()]                   
min_vol_port

这个组合的回报、波动率、各资产权重如下:

Returns 0.143355
Volatility 15.604691
change_005827 weight 0.067550
change_001714 weight 0.356889
change_270042 weight 0.575561

如果我们想要建立一个回报最高的投资组合:

max_ret_port = portfolios.iloc[portfolios['Returns'].idxmax()]                          
max_ret_port

这个组合的回报、波动率、各资产权重如下:

Returns 0.161274
Volatility 21.890702
change_005827 weight 0.019570
change_001714 weight 0.978369
change_270042 weight 0.002061

小结

本文整理了投资组合优化的一些基础概念,期望回报和波动率的计算方法,利用有效边界进行投资组合优化的基础方法。

下一篇改进思路,打算尝试其他更高效的优化方法。欢迎评论区分享你的建议!

参考文章

https://www.investopedia.com/terms/p/portfoliomanagement.asp
https://www.machinelearningplus.com/machine-learning/portfolio-optimization-python-example/
https://blog.csdn.net/u012710043/article/details/111590428

  • 21
    点赞
  • 99
    收藏
    觉得还不错? 一键收藏
  • 20
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值