【AI量金术师:简易代码领悟高深金融术语】02.马科维茨资产组合模型Python实战

目录

    • 1. 马科维茨资产组合模型简介
      • 1.1 模型的起源与发展
      • 1.2 核心概念
    • 2. 模型的基本假设
      • 2.1 投资者行为假设
      • 2.2 市场环境假设
    • 3. 模型的应用与局限性
      • 3.1 实际应用
      • 3.2 局限性探讨
    • 4. Python代码案例:实现马科维茨资产组合模型
      • 4.1 环境准备与数据获取
      • 4.2 数据收集
      • 4.3 计算收益率与协方差矩阵
      • 4.4 随机生成投资组合
      • 4.5 绘制有效前沿
      • 4.6 优化求解最优投资组合
    • 5. 结论与展望

1. 马科维茨资产组合模型简介

1.1 模型的起源与发展

马科维茨资产组合模型(Markowitz Mean-Variance Model, 简称MM),是由美国经济学家哈里·马科维茨(Harry Markowitz)于1952年提出的一种用于指导投资者如何构建投资组合以实现风险与收益最佳平衡的方法。该理论基于一系列假设,包括投资者根据证券的期望收益率和方差来评估风险,并且在给定的风险水平上追求最高的预期回报,或是在给定的预期回报水平上寻求最小的风险。

1.2 核心概念

  • 风险与收益:马科维茨认为,投资决策应同时考虑预期收益与风险,其中风险通过组合投资的方差来衡量。
  • 有效边界:指的是所有可能的投资组合中,在特定风险水平下提供最高预期回报的投资组合集合,或是对于某一预期回报率而言风险最低的投资组合集合。这些组合构成了所谓的“有效前沿”(Efficient Frontier)。
  • 分散化投资:通过将资金分配到多个不同的资产上,可以减少非系统性风险的影响,即那些可以通过增加投资组合中的资产数量而被稀释的风险。系统性风险则是无法通过这种方式消除的,因为它影响了整个市场。

2. 模型的基本假设

2.1 投资者行为假设

  • 投资者是理性的,他们会根据个人对风险的态度选择最优的投资组合;
  • 所有投资者都厌恶风险,倾向于在相同的风险水平下选择预期回报更高的投资组合;
  • 投资者的效用函数为二次形式,意味着他们不仅关心平均回报,也关注回报分布的标准差作为风险度量。

2.2 市场环境假设

  • 投资者能够获取有关未来回报的概率分布信息,并且这些分布符合正态分布;
  • 在分析期内,不存在交易成本、税收等因素的影响;
  • 允许卖空的情况下,所有资产都可以按市场价格借入或卖出;不允许卖空时,则要求每个资产的投资比例非负。

3. 模型的应用与局限性

3.1 实际应用

马科维茨模型已被广泛应用于金融领域,帮助机构和个人投资者优化其投资策略。例如,在构建股票、债券等不同类型的资产组合时,可以通过计算各资产之间的协方差矩阵来确定最优权重配置,从而达到降低整体风险的目的。此外,随着计算机技术的发展,现在还可以利用软件工具快速求解复杂的组合优化问题。

3.2 局限性探讨

尽管马科维茨模型具有重要的理论价值,但在实践中也存在一些局限性:

  • 理性人假设:现实中的人类行为往往偏离完全理性,存在认知偏差等问题;
  • 风险度量:使用标准差作为风险指标虽然简单易行,但它并不能准确反映投资者真正关心的价格下行带来的损失风险;
  • 数据需求:为了精确地估计资产间的相关性和波动性,需要大量的历史数据支持,而这可能会导致模型对未来表现的预测不够准确。

4. Python代码案例:实现马科维茨资产组合模型

4.1 环境准备与数据获取

首先,我们需要安装并导入必要的Python库。这里我们将使用numpypandasmatplotlibscipy等库来进行数据分析和可视化,同时还会用到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)

4.2 数据收集

假设我们要分析四只中国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())

4.3 计算收益率与协方差矩阵

接下来,我们计算每只股票的日对数收益率,并构建收益率的协方差矩阵。这一步骤对于后续计算投资组合的风险至关重要。

# 计算日对数收益率
returns = np.log(prices / prices.shift(1))

# 计算收益率均值
mean_returns = returns.mean()

# 计算协方差矩阵
cov_matrix = returns.cov()

# 显示收益率均值及协方差矩阵
print("收益率均值:\n", mean_returns)
print("\n协方差矩阵:\n", cov_matrix)

4.4 随机生成投资组合

为了探索不同权重下投资组合的表现,我们可以随机生成多个投资组合,并记录它们的预期收益率、标准差(风险)以及夏普比率。这里我们将生成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)

4.5 绘制有效前沿

最后,我们将绘制出所有随机投资组合的散点图,并标记出最大夏普比率和最小方差的投资组合位置。此外,还可以添加一条表示资本配置线(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()

4.6 优化求解最优投资组合

除了通过蒙特卡洛模拟寻找近似解之外,我们还可以利用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()

5. 结论与展望

马科维茨资产组合模型为现代金融学奠定了坚实的基础,它揭示了如何通过合理的资产配置来改善投资组合的风险-回报特性。然而,随着金融市场结构的变化以及人们对投资理解的深入,后续的研究者们也在不断改进和完善这一经典理论,如引入行为金融学视角解释非理性投资者的行为模式,或者采用更先进的统计方法提高模型预测能力等。总之,马科维茨模型仍然是理解和实践资产管理不可或缺的一部分。

你可能感兴趣的:(高深金融术语私厨,人工智能,金融,python)