File size: 2,443 Bytes
e74d865
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fdf1936
e74d865
 
 
 
 
 
 
fdf1936
e74d865
 
 
 
fdf1936
e74d865
fdf1936
e74d865
 
 
 
fdf1936
e74d865
 
 
 
 
 
 
 
fdf1936
e74d865
 
fdf1936
e74d865
 
fdf1936
e74d865
 
 
 
 
 
 
 
fdf1936
e74d865
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import pandas as pd
import numpy as np
from scipy.optimize import minimize

def optimize_portfolio(data, risk_tolerance):
    """
    Performs Modern Portfolio Theory (MPT) optimization with diversification constraints.

    Args:
        data (pandas.DataFrame): DataFrame containing historical stock prices.
        risk_tolerance (float): Risk tolerance level (higher value means higher risk).

    Returns:
        dict: Dictionary containing optimized asset weights for a diversified portfolio.
    """
    try:
        # Calculate daily returns & covariance matrix
        returns = data.pct_change().dropna()
        mean_returns = returns.mean()
        cov_matrix = returns.cov()

        num_assets = len(mean_returns)
        risk_free_rate = 0.01  # Example: Assume a 1% risk-free rate

        # Objective Function: Maximize Sharpe Ratio
        def negative_sharpe_ratio(weights):
            portfolio_return = np.sum(mean_returns * weights)
            portfolio_std_dev = np.sqrt(np.dot(weights.T, np.dot(cov_matrix, weights)))
            sharpe_ratio = (portfolio_return - risk_free_rate) / portfolio_std_dev
            return -sharpe_ratio

        # Constraints:
        constraints = [
            {"type": "eq", "fun": lambda x: np.sum(x) - 1},  # Sum of weights must be 1
        ]

        # Set Diversification Constraints
        max_allocation = 0.50  # Max 50% allocation per asset
        min_assets_allocated = 2  # At least 2 assets must have nonzero allocation

        def diversification_constraint(weights):
            return np.count_nonzero(weights) - min_assets_allocated  # Enforce minimum assets

        constraints.append({"type": "ineq", "fun": diversification_constraint})

        # Set Bounds (Per-Asset Allocation Limits)
        bounds = tuple((0.05, max_allocation) for _ in range(num_assets))  # Min 5%, Max 50%

        # Initialise Equal Weights
        initial_weights = np.array([1 / num_assets] * num_assets)

        # Optimise Portfolio Allocation
        optimized_results = minimize(
            negative_sharpe_ratio,
            initial_weights,
            method="SLSQP",
            bounds=bounds,
            constraints=constraints,
        )

        # Extract Optimal Weights
        asset_weights = dict(zip(data.columns, optimized_results.x))
        return asset_weights

    except Exception as e:
        print(f"Error optimizing portfolio: {e}")
        return None