import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import scipy.optimize as sco
import yfinance as yf

# 下载资产历史数据
assets = ['AAPL', 'MSFT', 'GOOGL', 'AMZN', 'TSLA']  # 资产代码
data = yf.download(assets, start="2020-01-01", end="2023-01-01")['Adj Close']
print(data)

# 计算每日收益率
returns = data.pct_change().dropna()
print(returns)

# 计算年化预期收益和协方差矩阵
annual_returns = returns.mean() * 252
cov_matrix = returns.cov() * 252
print(annual_returns)
print(cov_matrix)

# 定义组合的预期收益和风险（方差）
def portfolio_performance(weights, mean_returns, cov_matrix):
    returns = np.sum(weights * mean_returns)
    volatility = np.sqrt(np.dot(weights.T, np.dot(cov_matrix, weights)))
    return returns, volatility

# 定义目标函数：最小化风险（方差）
def minimize_volatility(weights, mean_returns, cov_matrix):
    return portfolio_performance(weights, mean_returns, cov_matrix)[1]

# 定义约束条件
def constraint_sum(weights):
    return np.sum(weights) - 1

# 初始化权重和边界（每个资产的权重在0-1之间）
num_assets = len(assets)
bounds = tuple((0, 1) for asset in range(num_assets))
initial_weights = num_assets * [1. / num_assets]  # 初始等权重

# 定义约束条件：总权重为1
constraints = ({'type': 'eq', 'fun': constraint_sum})

# 优化组合，使得组合的方差最小
opt_result = sco.minimize(minimize_volatility, initial_weights, args=(annual_returns, cov_matrix),
                          method='SLSQP', bounds=bounds, constraints=constraints)

# 提取最优权重
optimal_weights = opt_result.x

# 计算最优组合的收益和风险
optimal_return, optimal_volatility = portfolio_performance(optimal_weights, annual_returns, cov_matrix)

# 输出最优组合结果
print("最优资产组合权重:")
for i, asset in enumerate(assets):
    print(f"{asset}: {optimal_weights[i]:.2%}")
    
print(f"\n最优组合的年化预期收益: {optimal_return:.2%}")
print(f"最优组合的年化风险（标准差）: {optimal_volatility:.2%}")

# 可视化有效前沿
def plot_efficient_frontier(mean_returns, cov_matrix, num_portfolios=10000):
    results = np.zeros((3, num_portfolios))
    for i in range(num_portfolios):
        weights = np.random.random(num_assets)
        weights /= np.sum(weights)
        portfolio_return, portfolio_volatility = portfolio_performance(weights, mean_returns, cov_matrix)
        results[0, i] = portfolio_return
        results[1, i] = portfolio_volatility
        results[2, i] = results[0, i] / results[1, i]  # 计算夏普比率

    plt.figure(figsize=(10, 6))
    plt.scatter(results[1, :], results[0, :], c=results[2, :], cmap='viridis')
    plt.colorbar(label='Sharpe Ratio')
    plt.scatter(optimal_volatility, optimal_return, c='red', marker='*', s=200)  # 最优组合
    plt.title('Efficient Frontier')
    plt.xlabel('Volatility (Risk)')
    plt.ylabel('Return')
    plt.show()

# 绘制有效前沿
plot_efficient_frontier(annual_returns, cov_matrix)