pythonoptimizationscipyquantitative-financefinancial

SciPy optimization - args in constraint function


I am trying to build a portfolio optimization algorithm that minimizes Expected Shortfall (CVaR) subject to weighting boundaries and return constraint. While it already works without minimum return requirement, adding return constraint results in the following error: "all the input arrays must have same number of dimensions".

I've spent hours now looking through different forums and examples and still don't know what's causing the error here.

Thank you for your advice!

Code:

#Inputs
w_mkt = np.array([[.5203, .1439, .3358]])
mu = np.array([[.005, .003, .002]])
vol = np.array([[.02, .03, .01]])
rho = np.array([[1.00, 0.50, 0.25],
               [0.50, 1.00, 0.60],
               [0.25, 0.60, 1.00]])
sd_matrix = np.zeros((3,3))
np.fill_diagonal(sd_matrix, vol)
sigma = np.dot(sd_matrix, np.dot(rho, sd_matrix.T))

#Function to be optimized:

def C_VaR(w, mu, sigma, alpha=0.99):
    w = np.matrix(w)
    mu = np.matrix(mu)
    cvar = -np.dot(mu, w.T) + sqrt(np.dot(w, np.dot(sigma, w.T)))/(1-alpha)*norm.pdf(norm.ppf(alpha))
    return cvar

#Boundaries:

b_ = [(0.0, 1.0) for i in range(mu.shape[1])]
b_

#Constraints (return constraint is achivable):

c_ = ({'type':'eq', 'fun': lambda w: sum(w) - 1}, #weights sum up to zero
      {'type':'eq', 
       'fun': lambda w, mu: np.matrix(w).dot(mu.T) - .0036, 
       'args': (mu,)})  #return requirement
c_

#Finally, optimization function:

minCVAR = optimize.minimize(C_VaR, 
                             w_mkt, 
                             args=(mu, sigma), 
                             method="SLSQP",
                             bounds=tuple(b_), 
                             constraints=c_)

Error:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-157-be7467bdec1d> in <module>()
      4                              method="SLSQP",
      5                              bounds=tuple(b_),
----> 6                              constraints=c_)

~/miniconda3/lib/python3.6/site-packages/scipy/optimize/_minimize.py in minimize(fun, x0, args, method, jac, hess, hessp, bounds, constraints, tol, callback, options)
    609     elif meth == 'slsqp':
    610         return _minimize_slsqp(fun, x0, args, jac, bounds,
--> 611                                constraints, callback=callback, **options)
    612     elif meth == 'trust-constr':
    613         return _minimize_trustregion_constr(fun, x0, args, jac, hess, hessp,

~/miniconda3/lib/python3.6/site-packages/scipy/optimize/slsqp.py in _minimize_slsqp(func, x0, args, jac, bounds, constraints, maxiter, ftol, iprint, disp, eps, callback, **unknown_options)
    385             if cons['eq']:
    386                 c_eq = concatenate([atleast_1d(con['fun'](x, *con['args']))
--> 387                                     for con in cons['eq']])
    388             else:
    389                 c_eq = zeros(0)

ValueError: all the input arrays must have same number of dimensions

Solution

  • Changing constraint to 'fun': lambda w, mu: (np.matrix(w).dot(mu.T) - .0036)[0,0] solved the problem.