Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
cantaro86
GitHub Repository: cantaro86/Financial-Models-Numerical-Methods
Path: blob/master/src/FMNM/portfolio_optimization.py
1700 views
1
import numpy as np
2
from scipy.optimize import minimize, Bounds, LinearConstraint
3
4
5
def optimal_weights(MU, COV, Rf=0, w_max=1, desired_mean=None, desired_std=None):
6
"""
7
Compute the optimal weights for a portfolio containing a risk free asset and stocks.
8
MU = vector of mean
9
COV = covariance matrix
10
Rf = risk free return
11
w_max = maximum weight bound for the stock portfolio
12
desired_mean = desired mean of the portfolio
13
desired_std = desired standard deviation of the portfolio
14
"""
15
16
if (desired_mean is not None) and (desired_std is not None):
17
raise ValueError("One among desired_mean and desired_std must be None")
18
if ((desired_mean is not None) or (desired_std is not None)) and Rf == 0:
19
raise ValueError("We just optimize the Sharpe ratio, no computation of efficient frontier")
20
21
N = len(MU)
22
bounds = Bounds(0, w_max)
23
linear_constraint = LinearConstraint(np.ones(N, dtype=int), 1, 1)
24
weights = np.ones(N)
25
x0 = weights / np.sum(weights) # initial guess
26
27
def sharpe_fun(w):
28
return -(MU @ w - Rf) / np.sqrt(w.T @ COV @ w)
29
30
res = minimize(
31
sharpe_fun,
32
x0=x0,
33
method="trust-constr",
34
constraints=linear_constraint,
35
bounds=bounds,
36
)
37
print(res.message + "\n")
38
w_sr = res.x
39
std_stock_portf = np.sqrt(w_sr @ COV @ w_sr)
40
mean_stock_portf = MU @ w_sr
41
stock_port_results = {
42
"Sharpe Ratio": -sharpe_fun(w_sr),
43
"stock weights": w_sr.round(4),
44
"stock portfolio": {
45
"std": std_stock_portf.round(6),
46
"mean": mean_stock_portf.round(6),
47
},
48
}
49
50
if (desired_mean is None) and (desired_std is None):
51
return stock_port_results
52
53
elif (desired_mean is None) and (desired_std is not None):
54
w_stock = desired_std / std_stock_portf
55
if desired_std > std_stock_portf:
56
print(
57
"The risk you take is higher than the tangency portfolio risk \
58
==> SHORT POSTION"
59
)
60
tot_port_mean = Rf + w_stock * (mean_stock_portf - Rf)
61
return {
62
**stock_port_results,
63
"Bond + Stock weights": {
64
"Bond": (1 - w_stock).round(4),
65
"Stock": w_stock.round(4),
66
},
67
"Total portfolio": {"std": desired_std, "mean": tot_port_mean.round(6)},
68
}
69
70
elif (desired_mean is not None) and (desired_std is None):
71
w_stock = (desired_mean - Rf) / (mean_stock_portf - Rf)
72
if desired_mean > mean_stock_portf:
73
print(
74
"The return you want is higher than the tangency portfolio return \
75
==> SHORT POSTION"
76
)
77
tot_port_std = w_stock * std_stock_portf
78
return {
79
**stock_port_results,
80
"Bond + Stock weights": {
81
"Bond": (1 - w_stock).round(4),
82
"Stock": w_stock.round(4),
83
},
84
"Total portfolio": {"std": tot_port_std.round(6), "mean": desired_mean},
85
}
86
87