Path: blob/master/src/FMNM/portfolio_optimization.py
1700 views
import numpy as np1from scipy.optimize import minimize, Bounds, LinearConstraint234def optimal_weights(MU, COV, Rf=0, w_max=1, desired_mean=None, desired_std=None):5"""6Compute the optimal weights for a portfolio containing a risk free asset and stocks.7MU = vector of mean8COV = covariance matrix9Rf = risk free return10w_max = maximum weight bound for the stock portfolio11desired_mean = desired mean of the portfolio12desired_std = desired standard deviation of the portfolio13"""1415if (desired_mean is not None) and (desired_std is not None):16raise ValueError("One among desired_mean and desired_std must be None")17if ((desired_mean is not None) or (desired_std is not None)) and Rf == 0:18raise ValueError("We just optimize the Sharpe ratio, no computation of efficient frontier")1920N = len(MU)21bounds = Bounds(0, w_max)22linear_constraint = LinearConstraint(np.ones(N, dtype=int), 1, 1)23weights = np.ones(N)24x0 = weights / np.sum(weights) # initial guess2526def sharpe_fun(w):27return -(MU @ w - Rf) / np.sqrt(w.T @ COV @ w)2829res = minimize(30sharpe_fun,31x0=x0,32method="trust-constr",33constraints=linear_constraint,34bounds=bounds,35)36print(res.message + "\n")37w_sr = res.x38std_stock_portf = np.sqrt(w_sr @ COV @ w_sr)39mean_stock_portf = MU @ w_sr40stock_port_results = {41"Sharpe Ratio": -sharpe_fun(w_sr),42"stock weights": w_sr.round(4),43"stock portfolio": {44"std": std_stock_portf.round(6),45"mean": mean_stock_portf.round(6),46},47}4849if (desired_mean is None) and (desired_std is None):50return stock_port_results5152elif (desired_mean is None) and (desired_std is not None):53w_stock = desired_std / std_stock_portf54if desired_std > std_stock_portf:55print(56"The risk you take is higher than the tangency portfolio risk \57==> SHORT POSTION"58)59tot_port_mean = Rf + w_stock * (mean_stock_portf - Rf)60return {61**stock_port_results,62"Bond + Stock weights": {63"Bond": (1 - w_stock).round(4),64"Stock": w_stock.round(4),65},66"Total portfolio": {"std": desired_std, "mean": tot_port_mean.round(6)},67}6869elif (desired_mean is not None) and (desired_std is None):70w_stock = (desired_mean - Rf) / (mean_stock_portf - Rf)71if desired_mean > mean_stock_portf:72print(73"The return you want is higher than the tangency portfolio return \74==> SHORT POSTION"75)76tot_port_std = w_stock * std_stock_portf77return {78**stock_port_results,79"Bond + Stock weights": {80"Bond": (1 - w_stock).round(4),81"Stock": w_stock.round(4),82},83"Total portfolio": {"std": tot_port_std.round(6), "mean": desired_mean},84}858687