Skip to content
All library documents

Minimum-Variance Portfolio Optimization with SLSQP

Article Quant Q&A · Author: Tom

Summary

The document presents a Python workflow for exploring portfolio risk and return from a table of asset returns. It generates random long-only weights that sum to one, estimates annualized portfolio return and volatility from sample means and covariances, and plots the resulting points as an efficient-frontier illustration. It also defines portfolio return, volatility, and Sharpe ratio functions and uses SciPy's SLSQP optimizer to find a maximum-Sharpe portfolio.

For the minimum-variance portfolio, the example minimizes the squared portfolio volatility under the same fully invested and long-only constraints. The author reports that the optimizer appears to leave weights equal to the starting allocation, but provides no answer or diagnosis in the document. The code therefore illustrates the objective and constraint setup without establishing why optimization fails or whether it succeeds on the data. It does not include the return file or report convergence diagnostics, so the claimed behavior cannot be independently assessed.

Key ideas

  • Portfolio volatility is computed from asset weights and the return covariance matrix.
  • The example imposes a fully invested constraint and bounds each asset weight between zero and one.
  • SLSQP is used to maximize the Sharpe ratio and separately minimize variance.
  • The reported minimum-variance behavior is unexplained, and the underlying return data is unavailable.

Tags

Full text
# Minimum Variance Portfolio Problem Python


# Minimum Variance Portfolio Problem Python












I have a problem with the MVP-optimization and scipy. My code is the following. The Maximum-Sharpe-Ratio-Portfolio works. But if I want to optimise the MVP, scipy optimiser doesn't seem to work, because the asset weights are still equal weighted like the input weights for the optimisation.

The data ist available at Dropbox: Returns

```
# Read in returns
ret = pd.read_excel('/Users/XXX/Desktop/Data/003 Data/Returns.xlsx') # Import
ret.drop('DataDate', axis=1, inplace=True)
# No. of assets
no_assets=len(ret.columns.tolist())

#Standard simulation
MC_returns =[]
MC_vols =[]
N=1000
#In a loop, generate portfolio weights and make sure they add up to 1 (one).
for p in range(N):
    weights=np.random.rand(no_assets)
    weights/= np.sum(weights)
    MC_returns.append(np.sum(ret.mean()*weights)*252)
    MC_vols.append (np.sqrt(np.dot(weights.T, np.dot(ret.cov()*252, weights))))

# Plot
plt.scatter(MC_vols,MC_returns, s=1.5)
plt.xlabel('Vol ') # Bezeichnung der x-Achse
plt.ylabel('Return') # Bezeichnung der y-Achse
plt.title('Efficient Frontier') # Titel des Diagramms
plt.show()

# Function fpr portfolio standard deviation, return and sharpe ratio
def portfolio(weights):
    weights=np.array(weights)
    P_ret=np.sum(ret.mean()*weights)*252
    P_vol=np.sqrt(np.dot(weights.T,np.dot(ret.cov()*252, weights)))
    return np.array([P_ret,P_vol, P_ret/P_vol])

# negative sharpe should be optimized
def Sharpe(weights):
    return -portfolio(weights)[2]
# Set up the constraint that portfolio weights add up to one.
cons=({'type':'eq','fun':lambda x: np.sum(x)-1})
# Set up boundaries for the portfolio weights (between 0 and 1).
bnds=tuple((0,1) for x in range(no_assets))

#Optimization
opt_S=sco.minimize(Sharpe, no_assets*[1.0/no_assets], method='SLSQP', bounds=bnds, constraints=cons)

opt_S['x'].round(3)

MSRP = portfolio(opt_S['x']).round(3) # ok works

# Plot
plt.scatter(MC_vols,MC_returns, s=1.5)
plt.scatter(x=MSRP[1], y=MSRP[0], c='gold', marker='D', s=10)
plt.xlabel('Vol ')
plt.ylabel('Return')
plt.title('Efficient Frontier')
plt.show()

# Same for portfolio variance
def Variance(weights):
    return portfolio(weights)[1]**2
#Set up the constraint that portfolio weights add up to one.
cons=({'type':'eq','fun':lambda x: np.sum(x)-1})
# Set up boundaries for the portfolio weights (between 0 and 1).
bnds=tuple((0,1) for x in range(no_assets))

#Optimisation function.
opt_V=sco.minimize (Variance, no_assets*[1.0/no_assets], method='SLSQP', bounds=bnds, constraints=cons)
#Print portfolio weights.
opt_V['x'].round(3)

MVP = portfolio(opt_V['x']).round(3)

plt.scatter(MC_vols,MC_returns, s=1.5)
plt.scatter(x=MSRP[1], y=MSRP[0], c='gold', marker='D', s=10)
plt.scatter(x=MVP[1], y=MVP[0], c='gold', marker='D', s=10)
plt.xlabel('Vol ')
plt.ylabel('Return')
plt.title('Efficient Frontier')
plt.show()
```

Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.