Benchmark Example
In [1]:
Copied!
import pandas as pd
import numpy as np
import cvxpy as cp
from MVmodels import *
import pandas as pd
import numpy as np
import cvxpy as cp
from MVmodels import *
2021 Final: Optimization with portfolio constraints¶
In [2]:
Copied!
dowdata = pd.read_csv('dowstocks.csv',header=None)
vals = dowdata.values
mu = vals[:,0] ; sigma = vals[:,1] ; rho = vals[:,2:]
n = len(mu)
V = np.outer(sigma,sigma)*rho
dowdata = pd.read_csv('dowstocks.csv',header=None)
vals = dowdata.values
mu = vals[:,0] ; sigma = vals[:,1] ; rho = vals[:,2:]
n = len(mu)
V = np.outer(sigma,sigma)*rho
Equally Weighted Portfolio Variance
In [3]:
Copied!
x = np.ones(n) / n
print(mu.T@x)
print(x.T@V@x)
x = np.ones(n) / n
print(mu.T@x)
print(x.T@V@x)
26.76666666666667 472.4788240666667
Minimum Variance Portfolio Variance
In [4]:
Copied!
x = minVariance(V, long_only=True)
print(x.T@V@x)
x = minVariance(V, long_only=True)
print(x.T@V@x)
Restricted license - for non-production use only - expires 2024-10-28 215.39704077638464
Turnover Constraint
The sum of absolute differences in all positions less than 30%
In [5]:
Copied!
xb = np.ones(n) / n
x = cp.Variable(n)
constraints = [np.ones(n)@x == 1, x >= 0, cp.sum(cp.abs(x - xb)) <= 0.3]
prob = cp.Problem(cp.Minimize(cp.quad_form(x, V)), constraints)
prob.solve(solver=cp.GUROBI)
xb = np.ones(n) / n
x = cp.Variable(n)
constraints = [np.ones(n)@x == 1, x >= 0, cp.sum(cp.abs(x - xb)) <= 0.3]
prob = cp.Problem(cp.Minimize(cp.quad_form(x, V)), constraints)
prob.solve(solver=cp.GUROBI)
Out[5]:
333.1612981867172
Constraints on active holdings
Each active holding is less than 2%, and the objective is to maximize the expected returns
In [12]:
Copied!
xb = np.ones(n) / n
x = cp.Variable(n)
constraints = [np.ones(n)@x == 1, cp.sum(cp.abs(x - xb)) <= 0.3]
constraints += [
cp.abs(x[i] - xb[i]) <= 0.02 for i in range(n)
]
prob = cp.Problem(cp.Maximize(mu.T@x), constraints)
prob.solve(solver=cp.GUROBI)
xb = np.ones(n) / n
x = cp.Variable(n)
constraints = [np.ones(n)@x == 1, cp.sum(cp.abs(x - xb)) <= 0.3]
constraints += [
cp.abs(x[i] - xb[i]) <= 0.02 for i in range(n)
]
prob = cp.Problem(cp.Maximize(mu.T@x), constraints)
prob.solve(solver=cp.GUROBI)
Out[12]:
30.33366666666667
Maximize the Sharpe
Other constraints stay the same, now turn the goal into maximizing the Sharpe Ratio. The original method to add constraints in a loop doesn't work out here, change them to more linear constraints.
In [11]:
Copied!
xb = np.ones(n) / n
z = cp.Variable(n)
kappa = cp.Variable(1)
constraints = [mu.T@z == 1, np.ones(n)@z - kappa == 0, kappa >= 0]
constraints += [
z-(0.02+1/30)*kappa<=0, z-(1/30-0.02)*kappa>=0, cp.norm(z-kappa/30,1)-0.3*kappa<=0
]
prob = cp.Problem(cp.Minimize(cp.quad_form(z,V)),constraints)
prob.solve(solver=cp.GUROBI)
x = z.value/kappa.value
print("Sharpe = ", mu.T@x / np.sqrt(x.T@V@x))
xb = np.ones(n) / n
z = cp.Variable(n)
kappa = cp.Variable(1)
constraints = [mu.T@z == 1, np.ones(n)@z - kappa == 0, kappa >= 0]
constraints += [
z-(0.02+1/30)*kappa<=0, z-(1/30-0.02)*kappa>=0, cp.norm(z-kappa/30,1)-0.3*kappa<=0
]
prob = cp.Problem(cp.Minimize(cp.quad_form(z,V)),constraints)
prob.solve(solver=cp.GUROBI)
x = z.value/kappa.value
print("Sharpe = ", mu.T@x / np.sqrt(x.T@V@x))
Sharpe = 1.3257126843873293