Python 数据处理与分析(六) 设计一个高回报的投资组合(投资回报和风险分析)任务 5:使用Python实现均值-方差组合模型

目录

1. 学习目标

2. 操作讲解

3、作业结果

1.、作业1

2、作业2


1. 学习目标

  1. 使用 Python 实现不同的投资配比
  2. 使用 Python 实现均值-方差模型


2. 操作讲解

通过上一个任务,你对马科维茨的均值-方差投资组合模型已经有所了解了。那么在 Python 中该如何实现呢?整个过程有些复杂,和之前使用Excel的实现也有较大差异。因此我们会将整段代码拆解一下,给你逐个讲解。大体上,这段代码将分为以下几个主要的步骤:

  1. 收集两支股票(阿里巴巴和新东方教育)的信息,主要是从 2020 年初至今,每日的收盘价格
  2. 计算两支股票的每日对数收益率和每日协方差
  3. 设置不同的股票配置比例,例如股票 A 从 0% 到 100%,每次递增 5%,股票 B 从 100% 到 0%,每次递减 5%。并将所有这些不同的配置比例记录在一个列表中,作为最终结果的对照。
  4. 对于每种配置比例,计算综合性的投资对数收益率和基于标准差的不确定性风险。并将它们记录下来
  5. 通过图表,可视化综合收益和综合风险之间的关系,找到最佳平衡点。
    对于之前已经讲述过的代码,这里不再重复,我们只挑出有改变或者新增的部分重点讲解。

首先,我们要收集阿里巴巴和新东方教育这两支股票的信息。接下来,我们需要尝试不同的股票占比。为此,我们尝试从 0% 的阿里巴巴,100% 的新东方教育,到5% 的阿里巴巴,95% 的新东方教育,等等一直到 100% 的阿里巴巴,0% 的新东方教育,一共 20 种可能。代码层面会使用 Python 的循环语句来实现。

# 尝试20种不同的投资配比,例如(0.0, 1.0), (0.05, 0.95), ..., (0.95, 0.05), (1.0, 0.0)
interval = 0.05
rounds = 20
# 定义持股比例的列表,用于存放20种不同投资比例
ratio = []
# 定义综合收益的列表,用于存放20种不同投资比例下的综合收益
combined_returns = []
# 定义综合风险的列表,用于存放20种不同投资比例下的综合风险(标准差)
combined_risks = []

for i in range(0, rounds + 1):
    weight1 = i * interval
    weight2 = 1.0 - i * interval
    # 输出当前的股票占比
    print(weight1, weight2)

其中,for i in range(0, rounds + 1) 是一种新的循环方式,它将产生一系列的 i,从 0,1,2,…,20。

注意,索引是从 0 开始,而且最后不会到 21,而是到 20 结束。这样第一支股票的占比就是 i * interval,而第二支股票的占比就是 1.0 - i * interval。第一次循环的时候,i=0,这两个比例就分别是 0%和100%。以此类推,具体数据如下表:

当然,你可以设置更小的变化幅度 interval,以及对应的组合次数 rounds。

在循环的过程中,对于每种投资比例,我们都要计算综合收益和综合风险,然后使用列表的数据结构记录下来。

具体代码如下:

# 将持股比例加入列表
    ratio.append([weight1, weight2])
    weights = np.array([weight1, weight2])
    # 按照持股比例,计算预期的年均综合收益
    combined_return = np.sum(weights * returns.mean()) * 250
    # 将这个综合收益加入列表
    combined_returns.append(combined_return)

    # 按照持股比例,计算年均的综合标准差,作为风险
    combined_risk = np.sqrt(np.dot(weights.T, np.dot(returns.cov() * 250, weights)))
    # 将这个综合收益加入列表
    combined_risks.append(combined_risk)

这里使用了年均的收益率和风险计算。需要注意的是,相比于综合收益率的计算,综合风险的计算稍微复杂一点,我们使用了矩阵的点乘和矩阵的转置。其中,weights.T 表示矩阵(这里是向量)的转置,np.dot(returns.cov() * 250, weights) 将协方差矩阵和权重向量进行点乘,之间让 weights.T 向量点乘np.dot(returns.cov() * 250, weights) 的结果。最终,使用 np.sqrt() 函数开根号,这一步实现的就是如下公式。

 格式上不如 Excel 那么直观,需要你仔细对照。为了便于理解,我们也可以使用如下循环进行输出。

for i in range(0, len(ratio)):
    print('阿里和新东方的股票配比为', ratio[i], '的时候,综合收益为', combined_returns[i], ',综合风险为', combined_risks[i])

请注意,由于Python浮点数精度的原因,可能会出现0.15000…或者0.64999…的情况,实际上分别对应的就是0.15和0.65。

你还可以使用Python进行可视化,使用下述代码将散点图输出。

combination = pd.DataFrame({'Return': combined_returns, 'Risk': combined_risks})
combination.plot(x='Risk', y='Return', kind='scatter', figsize=(10, 6))
plt.xlabel('Expected Risk')
plt.ylabel('Expected Return')
plt.show()

从结果可以看到,在持股 55% 的阿里巴巴、45% 的新东方教育的时候,我们达到了一个均衡点,也就是 58.68% 的综合收益和 42.41% 的综合风险。当然,如果你尝试的股票时间周期和这里不同,那么结果也会有所不同。

完整的代码我列在这里,便于你阅读和实践。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# 使用多个股票代码,构建新的dataframe
BABA = pd.read_csv('./BABA.csv', sep='\t', index_col=0)
EDU = pd.read_csv('./EDU.csv', sep='\t', index_col=0)

stock_data = pd.DataFrame()
stock_data['BABA'] = BABA['Adj Close']
stock_data['EDU'] = EDU['Adj Close']

# 获取两支股票的每日对数收益率
returns = np.log(stock_data / stock_data.shift(1))
print(returns)

# 输出两支股票的协方差矩阵
print(returns.cov())

# 尝试20种不同的投资配比,例如(0.0, 1.0), (0.05, 0.95), ..., (0.95, 0.05), (1.0, 0.0)
interval = 0.05
rounds = 20
# 定义持股比例的列表,用于存放20种不同投资比例
ratio = []
# 定义综合收益的列表,用于存放20种不同投资比例下的综合收益
combined_returns = []
# 定义综合风险的列表,用于存放20种不同投资比例下的综合风险(标准差)
combined_risks = []

for i in range(0, rounds + 1):
    weight1 = i * interval
    weight2 = 1.0 - i * interval
    # 输出当前的股票占比
    print(weight1, weight2)

    # 将持股比例加入列表
    ratio.append([weight1, weight2])
    weights = np.array([weight1, weight2])
    # 按照持股比例,计算预期的年均综合收益
    combined_return = np.sum(weights * returns.mean()) * 250
    # 将这个综合收益加入列表
    combined_returns.append(combined_return)

    # 按照持股比例,计算年均的综合标准差,作为风险
    combined_risk = np.sqrt(np.dot(weights.T, np.dot(returns.cov() * 250, weights)))
    # 将这个综合收益加入列表
    combined_risks.append(combined_risk)

print('阿里和新东方的股票配比', ratio)
print('综合收益率', combined_returns)
print('综合风险', combined_risks)

for i in range(0, len(ratio)):
    print('阿里和新东方的股票配比为', ratio[i], '的时候,综合收益为', combined_returns[i], ',综合风险为', combined_risks[i])

combination = pd.DataFrame({'Return': combined_returns, 'Risk': combined_risks})
combination.plot(x='Risk', y='Return', kind='scatter', figsize=(10, 6))
plt.xlabel('Expected Risk')
plt.ylabel('Expected Return')
plt.show()

好了,内容我讲完了,轮到你来动手练习了,请参照上述 Python 代码的实现,完成下面两个小任务。

  • 请将上述代码中的对数收益率,替换为简单收益率,看看均值-方差模型的结果有何不同;
  • 完成上一步之后,请使用均值-方差模型,分析腾讯和新东方教育这两支股票的投资组合。

在完成练习的同时,请你注意把关键过程和最终结果截图,放到一个文件夹中打包,并用“任务5”来命名,我们在项目的「课后作业」部分为你设置了统一提交的入口。


3、作业结果

1.、作业1

# -*- coding: utf-8 -*-
# @Software: PyCharm
# @File : Task5-1.py
# @Author : Benjamin
# @Time : 2021/9/13 11:45


import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# 使用多个股票代码,构建新的dataframe
BABA = pd.read_csv('./BABA.csv', sep='\t', index_col=0)
EDU = pd.read_csv('./EDU.csv', sep='\t', index_col=0)

stock_data = pd.DataFrame()
stock_data['BABA'] = BABA['Adj Close']
stock_data['EDU'] = EDU['Adj Close']

# 获取两支股票的每日对数收益率
#returns = np.log(stock_data / stock_data.shift(1))
#print(returns)

# 获取全部股票的每日简单收益率
returns = stock_data / stock_data.shift(1) - 1
print(returns)



# 输出两支股票的协方差矩阵
print(returns.cov())

# 尝试20种不同的投资配比,例如(0.0, 1.0), (0.05, 0.95), ..., (0.95, 0.05), (1.0, 0.0)
interval = 0.05
rounds = 20
# 定义持股比例的列表,用于存放20种不同投资比例
ratio = []
# 定义综合收益的列表,用于存放20种不同投资比例下的综合收益
combined_returns = []
# 定义综合风险的列表,用于存放20种不同投资比例下的综合风险(标准差)
combined_risks = []

for i in range(0, rounds + 1):
    weight1 = i * interval
    weight2 = 1.0 - i * interval
    # 输出当前的股票占比
    print(weight1, weight2)

    # 将持股比例加入列表
    ratio.append([weight1, weight2])
    weights = np.array([weight1, weight2])
    # 按照持股比例,计算预期的年均综合收益
    combined_return = np.sum(weights * returns.mean()) * 250
    # 将这个综合收益加入列表
    combined_returns.append(combined_return)

    # 按照持股比例,计算年均的综合标准差,作为风险
    combined_risk = np.sqrt(np.dot(weights.T, np.dot(returns.cov() * 250, weights)))
    # 将这个综合收益加入列表
    combined_risks.append(combined_risk)

print('阿里和新东方的股票配比', ratio)
print('综合收益率', combined_returns)
print('综合风险', combined_risks)

for i in range(0, len(ratio)):
    print('阿里和新东方的股票配比为', ratio[i], '的时候,综合收益为', combined_returns[i], ',综合风险为', combined_risks[i])

combination = pd.DataFrame({'Return': combined_returns, 'Risk': combined_risks})
combination.plot(x='Risk', y='Return', kind='scatter', figsize=(10, 6))
plt.xlabel('Expected Risk')
plt.ylabel('Expected Return')
plt.show()

2、作业2

# -*- coding: utf-8 -*-
# @Software: PyCharm
# @File : Task5-2.py
# @Author : Benjamin
# @Time : 2021/9/13 11:45


import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# 使用多个股票代码,构建新的dataframe
TC = pd.read_csv('./TC.csv', sep='\t', index_col=0)
EDU = pd.read_csv('./EDU.csv', sep='\t', index_col=0)

stock_data = pd.DataFrame()
stock_data['TC'] = TC['Adj Close']
stock_data['EDU'] = EDU['Adj Close']

# 获取两支股票的每日对数收益率
#returns = np.log(stock_data / stock_data.shift(1))
#print(returns)

# 获取全部股票的每日简单收益率
returns = stock_data / stock_data.shift(1) - 1
print(returns)



# 输出两支股票的协方差矩阵
print(returns.cov())

# 尝试20种不同的投资配比,例如(0.0, 1.0), (0.05, 0.95), ..., (0.95, 0.05), (1.0, 0.0)
interval = 0.05
rounds = 20
# 定义持股比例的列表,用于存放20种不同投资比例
ratio = []
# 定义综合收益的列表,用于存放20种不同投资比例下的综合收益
combined_returns = []
# 定义综合风险的列表,用于存放20种不同投资比例下的综合风险(标准差)
combined_risks = []

for i in range(0, rounds + 1):
    weight1 = i * interval
    weight2 = 1.0 - i * interval
    # 输出当前的股票占比
    print(weight1, weight2)

    # 将持股比例加入列表
    ratio.append([weight1, weight2])
    weights = np.array([weight1, weight2])
    # 按照持股比例,计算预期的年均综合收益
    combined_return = np.sum(weights * returns.mean()) * 250
    # 将这个综合收益加入列表
    combined_returns.append(combined_return)

    # 按照持股比例,计算年均的综合标准差,作为风险
    combined_risk = np.sqrt(np.dot(weights.T, np.dot(returns.cov() * 250, weights)))
    # 将这个综合收益加入列表
    combined_risks.append(combined_risk)

print('阿里和新东方的股票配比', ratio)
print('综合收益率', combined_returns)
print('综合风险', combined_risks)

for i in range(0, len(ratio)):
    print('阿里和新东方的股票配比为', ratio[i], '的时候,综合收益为', combined_returns[i], ',综合风险为', combined_risks[i])

combination = pd.DataFrame({'Return': combined_returns, 'Risk': combined_risks})
combination.plot(x='Risk', y='Return', kind='scatter', figsize=(10, 6))
plt.xlabel('Expected Risk')
plt.ylabel('Expected Return')
plt.show()


如果觉得文章写不错,那就点个赞,点个收藏吧。

可关注微信公众号,后期有推文


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BenjaminQA

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值