Skip to main content
Glama

Constrained Optimization MCP Server

portfolio_optimization.pyโ€ข14.6 kB
#!/usr/bin/env python3 """ Advanced Portfolio Optimization using Constrained Optimization MCP Server This example demonstrates various portfolio optimization strategies including: - Markowitz mean-variance optimization - Black-Litterman model - Risk parity optimization - ESG-constrained optimization - Multi-period optimization This example demonstrates: - Convex optimization with CVXPY - Complex financial constraints - Risk management techniques - Performance attribution """ import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns from constrained_opt_mcp.models.cvxpy_models import ( CVXPYProblem, CVXPYVariable, CVXPYConstraint ) from constrained_opt_mcp.solvers.cvxpy_solver import solve_cvxpy_problem def generate_financial_data(n_assets=20, n_periods=252): """Generate realistic financial data for portfolio optimization""" np.random.seed(42) # Generate asset names sectors = ['Technology', 'Healthcare', 'Financial', 'Consumer', 'Energy', 'Industrial'] assets = [] for i in range(n_assets): sector = sectors[i % len(sectors)] assets.append(f"{sector}_{i+1}") # Generate returns using factor model n_factors = 3 factor_loadings = np.random.normal(0, 1, (n_assets, n_factors)) factor_returns = np.random.normal(0, 0.02, (n_periods, n_factors)) # Idiosyncratic returns idiosyncratic = np.random.normal(0, 0.01, (n_periods, n_assets)) # Total returns returns = factor_returns @ factor_loadings.T + idiosyncratic # Calculate statistics expected_returns = np.mean(returns, axis=0) cov_matrix = np.cov(returns.T) # Add ESG scores esg_scores = np.random.uniform(60, 95, n_assets) # Add sector information sector_info = [sectors[i % len(sectors)] for i in range(n_assets)] return { 'assets': assets, 'returns': returns, 'expected_returns': expected_returns, 'cov_matrix': cov_matrix, 'esg_scores': esg_scores, 'sectors': sector_info } def markowitz_optimization(data, risk_aversion=1.0, constraints=None): """Markowitz mean-variance optimization""" n_assets = len(data['assets']) expected_returns = data['expected_returns'] cov_matrix = data['cov_matrix'] # Create problem problem = CVXPYProblem( name="Markowitz Portfolio Optimization", problem_type="convex" ) # Create variables weights = CVXPYVariable( name="weights", shape=(n_assets,), var_type="continuous" ) problem.add_variable(weights) # Objective: maximize return - risk_aversion * risk portfolio_return = expected_returns @ weights portfolio_risk = cp.quad_form(weights, cov_matrix) problem.set_objective( objective_type="maximize", expression=portfolio_return - risk_aversion * portfolio_risk ) # Basic constraints problem.add_constraint(CVXPYConstraint( name="weights_sum_to_one", constraint_type="equality", expression=cp.sum(weights), constant=1.0 )) problem.add_constraint(CVXPYConstraint( name="no_short_selling", constraint_type="greater_equal", expression=weights, constant=0.0 )) # Add custom constraints if provided if constraints: for constraint in constraints: problem.add_constraint(constraint) # Solve the problem solution = solve_cvxpy_problem(problem) if solution.is_optimal: optimal_weights = solution.variable_values['weights'] portfolio_return_val = np.dot(expected_returns, optimal_weights) portfolio_risk_val = np.sqrt(np.dot(optimal_weights, np.dot(cov_matrix, optimal_weights))) return { 'weights': optimal_weights, 'expected_return': portfolio_return_val, 'risk': portfolio_risk_val, 'sharpe_ratio': portfolio_return_val / portfolio_risk_val } else: return None def black_litterman_optimization(data, views, confidences, tau=0.05): """Black-Litterman model for portfolio optimization""" n_assets = len(data['assets']) expected_returns = data['expected_returns'] cov_matrix = data['cov_matrix'] # Market capitalization weights (equal weight as proxy) market_caps = np.ones(n_assets) / n_assets # Implied equilibrium returns pi = 2 * risk_aversion * np.dot(cov_matrix, market_caps) # Black-Litterman formula P = np.array(views) # Pick matrix Q = np.array([0.05, 0.03]) # View returns Omega = np.diag(confidences) # Uncertainty matrix # Black-Litterman expected returns M1 = np.linalg.inv(tau * cov_matrix) M2 = P.T @ np.linalg.inv(Omega) @ P M3 = M1 @ pi M4 = P.T @ np.linalg.inv(Omega) @ Q bl_expected_returns = np.linalg.inv(M1 + M2) @ (M3 + M4) # Update data with BL expected returns data_bl = data.copy() data_bl['expected_returns'] = bl_expected_returns return markowitz_optimization(data_bl, risk_aversion=1.0) def risk_parity_optimization(data): """Risk parity portfolio optimization""" n_assets = len(data['assets']) cov_matrix = data['cov_matrix'] # Create problem problem = CVXPYProblem( name="Risk Parity Portfolio", problem_type="convex" ) # Create variables weights = CVXPYVariable( name="weights", shape=(n_assets,), var_type="continuous" ) problem.add_variable(weights) # Risk contributions portfolio_variance = cp.quad_form(weights, cov_matrix) risk_contributions = [] for i in range(n_assets): risk_contrib = weights[i] * (cov_matrix[i, :] @ weights) / portfolio_variance risk_contributions.append(risk_contrib) # Objective: minimize sum of squared deviations from equal risk contributions target_risk_contrib = 1.0 / n_assets objective = cp.sum([cp.square(rc - target_risk_contrib) for rc in risk_contributions]) problem.set_objective( objective_type="minimize", expression=objective ) # Constraints problem.add_constraint(CVXPYConstraint( name="weights_sum_to_one", constraint_type="equality", expression=cp.sum(weights), constant=1.0 )) problem.add_constraint(CVXPYConstraint( name="no_short_selling", constraint_type="greater_equal", expression=weights, constant=0.0 )) # Solve the problem solution = solve_cvxpy_problem(problem) if solution.is_optimal: optimal_weights = solution.variable_values['weights'] portfolio_return = np.dot(data['expected_returns'], optimal_weights) portfolio_risk = np.sqrt(np.dot(optimal_weights, np.dot(cov_matrix, optimal_weights))) return { 'weights': optimal_weights, 'expected_return': portfolio_return, 'risk': portfolio_risk, 'sharpe_ratio': portfolio_return / portfolio_risk } else: return None def esg_constrained_optimization(data, min_esg_score=80, sector_limits=None): """ESG-constrained portfolio optimization""" n_assets = len(data['assets']) expected_returns = data['expected_returns'] cov_matrix = data['cov_matrix'] esg_scores = data['esg_scores'] sectors = data['sectors'] # Create constraints constraints = [] # ESG constraint constraints.append(CVXPYConstraint( name="esg_constraint", constraint_type="greater_equal", expression=esg_scores @ weights, constant=min_esg_score )) # Sector limits if sector_limits: for sector, max_weight in sector_limits.items(): sector_indices = [i for i, s in enumerate(sectors) if s == sector] if sector_indices: sector_weights = [weights[i] for i in sector_indices] constraints.append(CVXPYConstraint( name=f"sector_limit_{sector}", constraint_type="less_equal", expression=cp.sum(sector_weights), constant=max_weight )) return markowitz_optimization(data, risk_aversion=1.0, constraints=constraints) def efficient_frontier(data, risk_aversion_range=(0.1, 5.0), n_points=20): """Generate efficient frontier""" risk_aversions = np.linspace(risk_aversion_range[0], risk_aversion_range[1], n_points) returns = [] risks = [] for risk_aversion in risk_aversions: result = markowitz_optimization(data, risk_aversion=risk_aversion) if result: returns.append(result['expected_return']) risks.append(result['risk']) return np.array(returns), np.array(risks) def visualize_portfolio_results(data, results, title="Portfolio Optimization Results"): """Visualize portfolio optimization results""" fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(15, 12)) # 1. Portfolio weights weights = results['weights'] assets = data['assets'] # Sort by weight sorted_indices = np.argsort(weights)[::-1] top_assets = [assets[i] for i in sorted_indices[:10]] top_weights = [weights[i] for i in sorted_indices[:10]] ax1.bar(range(len(top_assets)), top_weights, color='skyblue', alpha=0.7) ax1.set_xlabel('Assets') ax1.set_ylabel('Weight') ax1.set_title('Top 10 Portfolio Weights') ax1.set_xticks(range(len(top_assets))) ax1.set_xticklabels(top_assets, rotation=45, ha='right') ax1.grid(True, alpha=0.3) # 2. Risk-return scatter individual_returns = data['expected_returns'] individual_risks = np.sqrt(np.diag(data['cov_matrix'])) ax2.scatter(individual_risks, individual_returns, alpha=0.6, s=50, label='Individual Assets') ax2.scatter(results['risk'], results['expected_return'], color='red', s=200, marker='*', label='Optimal Portfolio') ax2.set_xlabel('Risk (Volatility)') ax2.set_ylabel('Expected Return') ax2.set_title('Risk-Return Profile') ax2.legend() ax2.grid(True, alpha=0.3) # 3. Sector allocation sectors = data['sectors'] sector_weights = {} for i, sector in enumerate(sectors): if sector not in sector_weights: sector_weights[sector] = 0 sector_weights[sector] += weights[i] ax3.pie(sector_weights.values(), labels=sector_weights.keys(), autopct='%1.1f%%') ax3.set_title('Sector Allocation') # 4. Risk contribution cov_matrix = data['cov_matrix'] portfolio_variance = np.dot(weights, np.dot(cov_matrix, weights)) risk_contributions = [] for i in range(len(weights)): risk_contrib = weights[i] * (cov_matrix[i, :] @ weights) / portfolio_variance risk_contributions.append(risk_contrib) ax4.bar(range(len(assets)), risk_contributions, color='lightcoral', alpha=0.7) ax4.set_xlabel('Assets') ax4.set_ylabel('Risk Contribution') ax4.set_title('Risk Contribution by Asset') ax4.set_xticks(range(0, len(assets), 5)) ax4.set_xticklabels([assets[i] for i in range(0, len(assets), 5)], rotation=45) ax4.grid(True, alpha=0.3) plt.tight_layout() plt.show() def compare_optimization_strategies(data): """Compare different portfolio optimization strategies""" print("Comparing Portfolio Optimization Strategies") print("=" * 50) # Markowitz optimization print("1. Markowitz Mean-Variance Optimization") markowitz_result = markowitz_optimization(data, risk_aversion=1.0) if markowitz_result: print(f" Expected Return: {markowitz_result['expected_return']:.3f}") print(f" Risk: {markowitz_result['risk']:.3f}") print(f" Sharpe Ratio: {markowitz_result['sharpe_ratio']:.3f}") # Risk parity print("\n2. Risk Parity Optimization") risk_parity_result = risk_parity_optimization(data) if risk_parity_result: print(f" Expected Return: {risk_parity_result['expected_return']:.3f}") print(f" Risk: {risk_parity_result['risk']:.3f}") print(f" Sharpe Ratio: {risk_parity_result['sharpe_ratio']:.3f}") # ESG-constrained print("\n3. ESG-Constrained Optimization") esg_result = esg_constrained_optimization(data, min_esg_score=80) if esg_result: print(f" Expected Return: {esg_result['expected_return']:.3f}") print(f" Risk: {esg_result['risk']:.3f}") print(f" Sharpe Ratio: {esg_result['sharpe_ratio']:.3f}") # Efficient frontier print("\n4. Efficient Frontier Analysis") returns, risks = efficient_frontier(data) plt.figure(figsize=(10, 6)) plt.plot(risks, returns, 'b-', linewidth=2, label='Efficient Frontier') plt.scatter(risks, returns, c='blue', alpha=0.6) if markowitz_result: plt.scatter(markowitz_result['risk'], markowitz_result['expected_return'], color='red', s=100, marker='*', label='Markowitz') if risk_parity_result: plt.scatter(risk_parity_result['risk'], risk_parity_result['expected_return'], color='green', s=100, marker='s', label='Risk Parity') if esg_result: plt.scatter(esg_result['risk'], esg_result['expected_return'], color='orange', s=100, marker='^', label='ESG-Constrained') plt.xlabel('Risk (Volatility)') plt.ylabel('Expected Return') plt.title('Portfolio Optimization Strategies Comparison') plt.legend() plt.grid(True, alpha=0.3) plt.show() if __name__ == "__main__": print("Advanced Portfolio Optimization") print("=" * 50) # Generate sample data data = generate_financial_data(n_assets=20, n_periods=252) print(f"Generated data for {len(data['assets'])} assets") print(f"Expected returns range: {data['expected_returns'].min():.3f} to {data['expected_returns'].max():.3f}") print(f"Average volatility: {np.sqrt(np.diag(data['cov_matrix'])).mean():.3f}") # Run comparison compare_optimization_strategies(data) # Detailed analysis of Markowitz optimization print("\nDetailed Markowitz Analysis:") markowitz_result = markowitz_optimization(data, risk_aversion=1.0) if markowitz_result: visualize_portfolio_results(data, markowitz_result, "Markowitz Portfolio Optimization")

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Sharmarajnish/MCP-Constrained-Optimization'

If you have feedback or need assistance with the MCP directory API, please join our Discord server