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.