# MILP: Linearization / approximation of mean and variance

My question- What is the standard practice to linearize / approximate computations of mean and variance using MILP solvers?

Edit: referencing comments, an idea was proposed to set an upper limit and lower limit, then minimize the range. In context of portfolio optimization, if I specify a minimum desired return, I can minimize the variance around returns.

Suppose here are stock price returns at each timestep indexed by (t, s):

R = np.array(
[[106.68504489,  96.18759117,  82.97144786],
[104.4655367 ,  86.73121512,  80.24690676],
[ 88.90226758,  88.61186455,  72.55923083],
[116.87150529,  89.8232715 ,  83.98530315],
[106.55712934,  98.7856774 ,  88.56319976],
[102.31215119,  96.55448414,  84.04272026],
[ 96.47732234,  88.84869302,  78.85341107],
[107.0409267 ,  88.83174151,  85.33453174],
[ 99.17865699,  87.98629567,  76.36985641],
[ 93.27413486,  86.42718274,  70.47009819]])


I can formulate the problem as follows, using Xpress solver:

problem = xp.problem()

W = {}
# Weights for portfolio allocations
for s in range(3):
W[f's{s}'] = xp.var(name=f's{s}',  vartype = xp.continuous)

# Lower limit and upper limit variables representing the max and min
#   returns across all timesteps. (Objective shrinks range to
#   shrink standard deviation of returns)
L = [xp.var(name=f'lower limit',  vartype = xp.continuous),
xp.var(name=f'upper limit',  vartype = xp.continuous)]

C = []
# 1/ For each timestep, the sum of weighted allocations is greater than lower
#    limit and less than upper limit
for t in range(10):
t_sum = xp.Sum([R[t,s] * W[f's{s}'] for s in range(3)])
c1 = L[0] <= t_sum
c2 = L[1] >= t_sum
C.extend([ c1, c2 ])

# 2/ Each stock weight between [0,1]
for s in range(3):
c1 = 0.0 <= W[f"s{s}"]
c2 = W[f"s{s}"] <= 1.0
C.extend([c1, c2])

# 3/ Weights sum to 100% portfolio allocation
c = xp.Sum([ W[f"s{s}"] for s in range(3) ]) == 1.0
C.append(c)

# 4/ summed allocations over each stock and each timestep
#    greater than some threshold
T = xp.Sum([ xp.Sum([R[t,s] * W[f's{s}'] for s in range(3)])
for t in range(10) ])
c = T >= 1000.0
C.append(c)

O = L[1] - L[0]
problem.setObjective(xp.Sum(O), sense = xp.minimize)
problem.solve()


Ultimately, the portfolio returns 1000, summing the weighted contributions over each timestep. But the true objective is to minimize the variance of these weighted contributions (an array of length 10, one weighted aggregation per timestep.) The standard deviation is 6.49 where the returns are as follows:

104.66273621418969,
101.04906320581212,
88.84632214954465,
111.66072948494481,
105.05997821163048,
101.20295077452609,
95.00768563857933,
103.53297189768307,
97.02247607184876,
91.9550863512409


However, some issues with this approach:

1. Covariance is completely overlooked here, which could be relevant.
2. Need to specify a desired overall return as a constraint and choosing a reasonably high yet still feasible value takes some consideration.
3. In theory, I could swap variance minimization for mean maximization by using a desired variance as a constraint and maximizing the mean return. But this also requires the nontrivial specification of allowable risk.

Is there a way to both maximize the mean and minimize the variance?

• Aug 30, 2023 at 17:25