马科维茨资产组合模型(Markowitz Mean-Variance Model, 简称MM),是由美国经济学家哈里·马科维茨(Harry Markowitz)于1952年提出的一种用于指导投资者如何构建投资组合以实现风险与收益最佳平衡的方法。该理论基于一系列假设,包括投资者根据证券的期望收益率和方差来评估风险,并且在给定的风险水平上追求最高的预期回报,或是在给定的预期回报水平上寻求最小的风险。
马科维茨模型已被广泛应用于金融领域,帮助机构和个人投资者优化其投资策略。例如,在构建股票、债券等不同类型的资产组合时,可以通过计算各资产之间的协方差矩阵来确定最优权重配置,从而达到降低整体风险的目的。此外,随着计算机技术的发展,现在还可以利用软件工具快速求解复杂的组合优化问题。
尽管马科维茨模型具有重要的理论价值,但在实践中也存在一些局限性:
首先,我们需要安装并导入必要的Python库。这里我们将使用numpy
、pandas
、matplotlib
和scipy
等库来进行数据分析和可视化,同时还会用到pandas_datareader
来获取股票的历史价格数据。
安装依赖库(如果尚未安装)
pip install numpy pandas matplotlib scipy pandas-datareader tushare
导入库
import numpy as np
import pandas as pd
import pandas_datareader.data as web
from datetime import datetime
import matplotlib.pyplot as plt
from scipy.optimize import minimize
plt.style.use('seaborn-darkgrid')
为了获取中国A股市场的股票数据,我们可以选择使用Tushare API。请确保您已经注册了Tushare账号,并获得了API密钥。接下来设置您的Tushare API token,并初始化API接口。
import tushare as ts
your_token = 'YOUR_TUSHARE_API_TOKEN' # 替换为您的Tushare API Token
pro = ts.pro_api(your_token)
假设我们要分析四只中国A股市场的股票:平安银行(000001.SZ)、万科A(000002.SZ)、白云机场(600004.SH)和东风汽车(600006.SH)。我们将从2014年1月1日开始收集这些股票的日收盘价数据。
stock_code_list = ['000001.SZ', '000002.SZ', '600004.SH', '600006.SH']
start_date = '2014-01-01'
end_date = datetime.today().strftime('%Y-%m-%d')
# 创建一个空的DataFrame,用于存放股票价格数据
prices = pd.DataFrame()
for stock_code in stock_code_list:
if 'trade_date' not in prices.columns:
df = pro.daily(ts_code=stock_code, start_date=start_date, end_date=end_date)
df['trade_date'] = pd.to_datetime(df['trade_date'])
prices['trade_date'] = df['trade_date']
prices[stock_code] = pro.daily(ts_code=stock_code, start_date=start_date, end_date=end_date)['close']
# 数据清洗,重新按交易日期排序
prices.dropna(inplace=True)
prices.set_index('trade_date', inplace=True)
prices.sort_index(ascending=True, inplace=True)
# 查看前几行数据
print(prices.head())
接下来,我们计算每只股票的日对数收益率,并构建收益率的协方差矩阵。这一步骤对于后续计算投资组合的风险至关重要。
# 计算日对数收益率
returns = np.log(prices / prices.shift(1))
# 计算收益率均值
mean_returns = returns.mean()
# 计算协方差矩阵
cov_matrix = returns.cov()
# 显示收益率均值及协方差矩阵
print("收益率均值:\n", mean_returns)
print("\n协方差矩阵:\n", cov_matrix)
为了探索不同权重下投资组合的表现,我们可以随机生成多个投资组合,并记录它们的预期收益率、标准差(风险)以及夏普比率。这里我们将生成5000个随机投资组合。
num_portfolios = 5000
results = np.zeros((3 + len(stock_code_list), num_portfolios))
for i in range(num_portfolios):
weights = np.random.random(len(stock_code_list))
weights /= sum(weights) # 确保权重之和等于1
portfolio_return = np.sum(mean_returns * weights) * 252 # 年化收益率
portfolio_std_dev = np.sqrt(np.dot(weights.T, np.dot(cov_matrix, weights))) * np.sqrt(252) # 年化标准差
results[0, i] = portfolio_return
results[1, i] = portfolio_std_dev
results[2, i] = (portfolio_return - 0.03) / portfolio_std_dev # 假设无风险利率为3%
for j in range(len(weights)):
results[j + 3, i] = weights[j]
# 将结果转换为DataFrame
result_df = pd.DataFrame(results.T,
columns=['ret', 'stdev', 'sharpe'] + [f'weight_{i}' for i in range(len(stock_code_list))])
# 找到夏普比率最高的投资组合
max_sharpe_portfolio = result_df.iloc[result_df['sharpe'].idxmax()]
min_variance_portfolio = result_df.iloc[result_df['stdev'].idxmin()]
print("最大夏普比率的投资组合:\n", max_sharpe_portfolio)
print("\n最小方差的投资组合:\n", min_variance_portfolio)
最后,我们将绘制出所有随机投资组合的散点图,并标记出最大夏普比率和最小方差的投资组合位置。此外,还可以添加一条表示资本配置线(Capital Allocation Line, CAL)的直线,它连接了无风险资产与市场组合。
# 绘制随机投资组合的散点图
plt.figure(figsize=(10, 6))
plt.scatter(result_df['stdev'], result_df['ret'], c=result_df['sharpe'], cmap='viridis')
plt.colorbar(label='Sharpe Ratio')
plt.xlabel('Volatility (Std. Deviation)')
plt.ylabel('Expected Return')
# 标记最大夏普比率和最小方差的投资组合
plt.scatter(max_sharpe_portfolio[1], max_sharpe_portfolio[0], c='red', s=50, edgecolors='black', label='Max Sharpe Ratio')
plt.scatter(min_variance_portfolio[1], min_variance_portfolio[0], c='blue', s=50, edgecolors='black', label='Min Variance')
# 添加资本配置线
risk_free_rate = 0.03 # 假设无风险利率为3%
plt.plot([0, max_sharpe_portfolio[1]], [risk_free_rate, max_sharpe_portfolio[0]], linestyle='--', color='gray', lw=1, label='Capital Allocation Line')
plt.legend()
plt.title('Efficient Frontier with Random Portfolios')
plt.show()
除了通过蒙特卡洛模拟寻找近似解之外,我们还可以利用scipy.optimize.minimize
函数直接求解最优投资组合。这里的目标是最小化投资组合的标准差,同时满足给定的预期回报率约束条件。
def portfolio_performance(weights, mean_returns, cov_matrix, risk_free_rate=0.03):
"""计算投资组合的预期收益、标准差和夏普比率"""
returns = np.sum(mean_returns * weights) * 252
std = np.sqrt(np.dot(weights.T, np.dot(cov_matrix, weights))) * np.sqrt(252)
sharpe = (returns - risk_free_rate) / std
return std, returns, sharpe
def neg_sharpe_ratio(weights, mean_returns, cov_matrix, risk_free_rate=0.03):
"""负夏普比率,用于优化时最小化"""
return -portfolio_performance(weights, mean_returns, cov_matrix, risk_free_rate)[2]
def portfolio_volatility(weights, mean_returns, cov_matrix):
"""投资组合波动率"""
return portfolio_performance(weights, mean_returns, cov_matrix)[0]
# 定义约束条件
constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1}) # 权重之和必须等于1
bounds = tuple((0, 1) for asset in range(len(stock_code_list))) # 每个资产的权重范围是[0, 1]
# 目标:最大化夏普比率
optimal_weights = minimize(neg_sharpe_ratio, num_assets*[1./num_assets,], args=(mean_returns, cov_matrix, risk_free_rate),
method='SLSQP', bounds=bounds, constraints=constraints)
optimal_weights = optimal_weights['x'].round(4)
# 输出最优权重
print("最优投资组合权重:", dict(zip(stock_code_list, optimal_weights)))
# 计算最优投资组合的性能指标
std_opt, ret_opt, sharpe_opt = portfolio_performance(optimal_weights, mean_returns, cov_matrix)
print(f"最优投资组合的年化标准差: {std_opt:.2%}")
print(f"最优投资组合的年化预期回报: {ret_opt:.2%}")
print(f"最优投资组合的夏普比率: {sharpe_opt:.2f}")
# 在图表中标记最优投资组合
plt.figure(figsize=(10, 6))
plt.scatter(result_df['stdev'], result_df['ret'], c=result_df['sharpe'], cmap='viridis')
plt.colorbar(label='Sharpe Ratio')
plt.xlabel('Volatility (Std. Deviation)')
plt.ylabel('Expected Return')
plt.scatter(std_opt, ret_opt, c='green', s=50, edgecolors='black', label='Optimal Portfolio')
plt.scatter(max_sharpe_portfolio[1], max_sharpe_portfolio[0], c='red', s=50, edgecolors='black', label='Max Sharpe Ratio')
plt.scatter(min_variance_portfolio[1], min_variance_portfolio[0], c='blue', s=50, edgecolors='black', label='Min Variance')
plt.plot([0, std_opt], [risk_free_rate, ret_opt], linestyle='--', color='gray', lw=1, label='Capital Allocation Line')
plt.legend()
plt.title('Efficient Frontier with Optimal Portfolio')
plt.show()
马科维茨资产组合模型为现代金融学奠定了坚实的基础,它揭示了如何通过合理的资产配置来改善投资组合的风险-回报特性。然而,随着金融市场结构的变化以及人们对投资理解的深入,后续的研究者们也在不断改进和完善这一经典理论,如引入行为金融学视角解释非理性投资者的行为模式,或者采用更先进的统计方法提高模型预测能力等。总之,马科维茨模型仍然是理解和实践资产管理不可或缺的一部分。