Extended Mathematical Programming (Trading & Investing Applications)
Extended Mathematical Programming (EMP) is a framework that allows for the formulation and solution of complex optimization problems by integrating various programming paradigms, such as quadratic, nonlinear, mixed integer, and stochastic programming.
This method extends beyond traditional linear and nonlinear programming techniques, which allows for a more nuanced handling of real-world financial scenarios.
Key Takeaways – Extended Mathematical Programming
- Blends multiple programming and optimization paradigms.
- Goes deeper than maximizing one thing (e.g., profit). EMP handles complex goals, constraints, and risks, and can accommodate a large range of real-life goals and constraints.
Fundamentals of EMP
EMP involves constructing mathematical models to represent trading strategies, investment portfolios, or other matters involving financial decision-making.
These models consider multiple factors like market volatility, transaction costs, and liquidity constraints.
EMP excels in optimizing these models by finding the best possible combination of allocation decisions that meet the desired objectives, such as maximizing returns or minimizing risk.
EMP Applications in Trading
In trading, EMP is used for formulating and solving multi-period portfolio optimization problems.
Risk Management
Traders use EMP to manage risk dynamically over time.
This includes adjusting portfolios in response to market changes, managing transaction costs, and adhering to regulatory requirements.
EMP models can also incorporate forecasts and stochastic processes to take into account knowns.
Algorithmic Trading
EMP is used in algorithmic trading, where it’s used to develop strategies that can execute trades at the most opportune times, considering factors like price, volume, and time.
These models are capable of processing vast datasets, extracting patterns, and making high-speed decisions that are beyond human capabilities.
Portfolio Optimization
Portfolio optimization is a primary application of EMP in trading.
These models help in identifying the optimal mix of assets that yield the highest expected return for a given level of risk, or alternatively, the least risk for a given level of expected return, or some combination thereof.
They can incorporate various real-world constraints such as market impact, transaction costs, and portfolio turnover.
Handling Unknowns in EMP
One of the significant advantages of EMP is its ability to handle uncertainties and model complex scenarios.
Stochastic programming, a subset of EMP, is useful in this regard.
It allows for modeling scenarios where certain parameters have probabilistic outcomes (and the parameters themselves might have probabilistic outcomes in a type of multi-order probabilistic structure), which provide a framework for decision-making under uncertainty.
Scenario Analysis
EMP enables investors and traders to perform scenario analysis, which considers a wide range of possible future economic/market conditions.
This is important for stress testing portfolios and strategies against extreme market events.
Math Behind Extended Mathematical Programming
Here is an overview of the mathematics behind EMP:
Standard Mathematical Programming Formulation
- Minimize f(x)
- Subject to g(x) ≤ 0
- h(x) = 0
Where x are the decision variables.
EMP augments this with:
1. Endogenous uncertainty
- Eξ[Q(x, ξ)]
Where ξ is a random vector with expectation Eξ.
This leads to stochastic programming.
2. Equilibrium conditions
- y = arg min/max {g(x,y)}
Where y represents equilibrium decisions like in bilevel programming.
3. Complementarity conditions
- 0 ≤ y ⊥ g(x) ≥ 0
Where ⊥ denotes complementarity between variables y and constraint function g(x).
This handles equilibria and trade-offs.
4. Logical/discrete decisions
- x ∈ S
- S = True/False combinations
Which enables mixing integer, binary, and logical constraints.
The key insight is modeling complex systems requires extending basic optimization.
This stretches mathematical programming syntax and semantics to new forms while retaining computational tractability.
Coding Example – Extended Mathematical Programming
Following an example we’ve used in other articles, let’s do a coding example of extended mathematical programming to optimize this portfolio:
- Stocks: +4-7% forward return, 15% annualized volatility using standard deviation
- Bonds: +0-5% forward return, 10% annualized volatility using standard deviation
- Commodities: +0-4% forward return, 15% annualized volatility using standard deviation
- Gold: +2-5% forward return, 15% annualized volatility using standard deviation
Conceptual Approach
Let’s outline a conceptual approach to how you might set up this problem using EMP principles, focusing on maximizing the expected return for a given level of risk, while considering the ranges of possible returns for each asset class.
Define Variables
Portfolio weights for each asset class (stocks, bonds, commodities, gold), ensuring they sum to 1.
Objective Function
Maximize the portfolio’s expected return, considering the range of possible returns for each asset.
Constraints
- Total portfolio weights equal 1 (full investment constraint).
- Portfolio volatility doesn’t exceed a specified target, calculated based on asset volatilities and portfolio weights.
- Additional constraints as needed (e.g., minimum or maximum investment levels for each asset).
Modeling Uncertainty
Use stochastic programming elements within EMP to model the uncertainty in forward returns, potentially defining scenarios based on the return ranges for each asset.
Solving the Problem
This setup would require solving a nonlinear optimization problem, possibly with stochastic elements, using a solver capable of handling such problems.
Example #1 – Pyomo Library
We’ll use the Pyomo library, a Python library for defining and solving mathematical programs.
This example requires the Pyomo library and a solver like GLPK, CPLEX, or Gurobi installed in your Python environment.
We’ll simulate expected returns based on the average of the provided ranges and use the given volatilities directly.
from pyomo.environ import * # Define model model = ConcreteModel() # Decision Variables: proportion of the portfolio allocated to each asset class model.stocks = Var(bounds=(0, 1)) model.bonds = Var(bounds=(0, 1)) model.commodities = Var(bounds=(0, 1)) model.gold = Var(bounds=(0, 1)) # Parameters: Expected returns (average of provided ranges) & volatilities expected_returns = {'stocks': 0.055, 'bonds': 0.025, 'commodities': 0.02, 'gold': 0.035} volatilities = {'stocks': 0.15, 'bonds': 0.10, 'commodities': 0.15, 'gold': 0.15} # Objective: Maximize expected portfolio return def portfolio_return(model): return sum(expected_returns[asset] * getattr(model, asset) for asset in expected_returns) model.objective = Objective(rule=portfolio_return, sense=maximize) # Constraint: Sum of allocations = 1 (100%) def allocation_constraint(model): return model.stocks + model.bonds + model.commodities + model.gold == 1 model.total_allocation = Constraint(rule=allocation_constraint) # Solve the model solver = SolverFactory('glpk') # or use 'cplex', 'gurobi', etc. depending on what you have installed solver.solve(model) # Print the optimized allocation print("Optimized Portfolio Allocation:") for asset in expected_returns: print(f"{asset.capitalize()}: {getattr(model, asset).value():.4f}")
Example #2 – Without Pyomo
And with standard numpy and scipy libraries.
Here we’re trying to optimize while getting equal risk contribution from each asset class.
import numpy as np from scipy.optimize import minimize # Asset expected returns & volatilities expected_returns = np.array([0.055, 0.025, 0.02, 0.035]) # Stocks, Bonds, Commodities, Gold volatilities = np.array([0.15, 0.10, 0.15, 0.15]) # Assuming uncorrelated assets for simplicity, construct diagonal covariance matrix covariance_matrix = np.diag(volatilities ** 2) # Risk contribution function def risk_contributions(weights, covariance_matrix): portfolio_volatility = np.sqrt(np.dot(weights.T, np.dot(covariance_matrix, weights))) marginal_contrib = np.dot(covariance_matrix, weights) risk_contributions = np.divide(marginal_contrib, portfolio_volatility) return risk_contributions # Objective function: minimize the variance of risk contributions def objective(weights): contributions = risk_contributions(weights, covariance_matrix) return np.std(contributions) # Minimize the standard deviation of contributions # Constraints & bounds constraints = [{'type': 'eq', 'fun': lambda x: np.sum(x) - 1}] # Sum of weights must be 1 bounds = [(0, 1) for _ in range(len(expected_returns))] # Weights between 0 and 1 # Initial guess x0 = np.array([0.25, 0.25, 0.25, 0.25]) # Optimization result = minimize(objective, x0, method='SLSQP', bounds=bounds, constraints=constraints) # Show optimized allocations optimized_allocations = pd.Series(result.x, index=['Stocks', 'Bonds', 'Commodities', 'Gold']) optimized_allocations
Example #3 – Highest Return per Unit of Risk Using Monte Carlo Simulations
We’ll now do this using a Monte Carlo approach:
Simulate Asset Returns
Use Monte Carlo simulations to generate possible future returns for each asset class by considering their expected return ranges and volatilities.
Calculate Portfolio Risk and Return
For a given allocation, calculate the portfolio’s expected return and risk, by taking into account the simulation results.
Optimization
We’ll use a numerical optimizer to find the allocation that equalizes risk contributions from each asset class while maximizing the expected return of the portfolio.
import numpy as np from scipy.optimize import minimize # Parameters n_simulations = 10000 n_assets = 4 expected_returns = np.array([5.5, 2.5, 2, 3.5]) / 100 # Midpoints of expected return ranges volatilities = np.array([15, 10, 15, 15]) / 100 n_years = 1 # Sim future returns np.random.seed(42) simulated_returns = np.random.normal(loc=expected_returns, scale=volatilities, size=(n_simulations, n_assets)) # Function to calculate portfolio return and risk def portfolio_performance(weights, simulated_returns): weighted_returns = simulated_returns @ weights expected_return = np.mean(weighted_returns) risk = np.std(weighted_returns) return expected_return, risk # Objective function: Maximize expected return for equal risk contributions def objective(weights): expected_return, risk = portfolio_performance(weights, simulated_returns) risk_contributions = weights * np.std(simulated_returns, axis=0) / risk risk_parity_penalty = np.sum((risk_contributions - 1 / n_assets) ** 2) return -(expected_return - risk_parity_penalty) # Constraints & bounds constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1}) bounds = tuple((0, 1) for _ in range(n_assets)) # Initial guess initial_guess = np.full(n_assets, 1 / n_assets) # Optimization result = minimize(objective, initial_guess, method='SLSQP', bounds=bounds, constraints=constraints) # Optimized weights optimized_weights = result.x optimized_weights
Results
- Stocks: 21.60%
- Bonds: 34.04%
- Commodities: 22.05%
- Gold: 22.31%
This allocation suggests a well-diversified portfolio that balances risk contributions across different asset classes.
We adhere to the risk parity framework, and also considers the expected returns of each asset.
The optimization process distributed the portfolio’s total risk among the four assets, and ensures that no single asset class disproportionately contributes to the overall risk, while still aiming to maximize the portfolio’s expected return.
Challenges & Considerations
EMP models can be complex and require significant computational resources.
The quality of the results is heavily dependent on the accuracy of the input data and the assumptions underlying the models.
Therefore, continuous monitoring, updating, and validation of the models are important to ensure their relevance and accuracy.