import matplotlib.pyplot as plt
import numpy as np
def f(x):
return (sum(x**2) - 1)**2 - 2 * x[0]**3
x1 = np.arange(-1.0, 2.5, 0.01)
x2 = np.arange(-1.5, 1.5, 0.01)
X1, X2 = np.meshgrid(x1, x2)
F = f(np.array([X1,X2]))
clev = [-7,-6,-5,-4,-3,-2,-1,-0.8,-0.6,-0.4,-0.2,0,0.2,0.4,0.6,0.8,1,3,5] # contour curves
plt.contourf(X1, X2, F, clev, cmap=plt.cm.Greys, extend='both')
plt.colorbar()
plt.xlabel('$x_1$')
plt.ylabel('$x_2$')
plt.show()
from scipy.optimize import minimize
n = 4 # dimension of parameter space
x0 = np.ones(n) # initial point in parameter space
res = minimize(f, x0, method='BFGS')
x_min = res.x
print("x_min = {0}".format(x_min))
print("f(x_min) = {0}".format(f(x_min)))
def jac_f(x): # Jacobian of f
n = len(x)
d = np.zeros(n)
d[0] = 4 * x[0] * (sum(x**2) - 1) - 6 * x[0]**2
d[1:] = 4 * x[1:] * (sum(x**2) - 1)
return d
n = 4
x0 = np.ones(n)
res = minimize(f, x0, method='Newton-CG', jac=jac_f)
x_min = res.x
print("x_min = {0}".format(x_min))
print("f(x_min) = {0}".format(f(x_min)))
def hess_f(x): # Hessian of f
n = len(x)
dd = 4 * (sum(x**2) - 1) * np.identity(n)
for i in range(n):
for j in range(n):
dd[i,j] = dd[i,j] + 8 * x[i] * x[j]
dd[0,0] = dd[0,0] - 12 * x[0]
return dd
n = 4
x0 = np.ones(n)
res = minimize(f, x0, method='Newton-CG', jac=jac_f, hess=hess_f)
x_min = res.x
print("x_min = {0}".format(x_min))
print("f(x_min) = {0}".format(f(x_min)))
# The inequalities -1 <= x_i <= 1 are actually implemented as bounds, not as inequality constraints.
bounds = [[-1,1], [-1,1], [-1,1], [-1,1]]
# Next, the constraints. In our case we have only one: c_1 = sum(x**2) - 1.
def c(x):
return sum(x**2) - 1
def jac_c(x):
return 2 * x
cons = [] # list of constraints: each constraint is a dictionary with 'type': 'eq' or 'ineq'
cons.append({'type' : 'eq', 'fun' : c, 'jac' : jac_c})
# Initial point in parameter space
x0 = np.array([0.1,0.1,0.1,0.1])
# Finally, we run the solver:
res = minimize(f, x0, method='SLSQP', jac=jac_f, constraints=cons, bounds=bounds)
x_min = res.x
print("x_min = {0}".format(x_min))
print("f(x_min) = {0}".format(f(x_min)))
# Import the data from CSV files (from 2012-01-03 to 2019-12-31)
import pandas as pd
df1 = pd.read_csv('data_AAPL.csv') # Apple data
df2 = pd.read_csv('data_GOOG.csv') # Google data
df3 = pd.read_csv('data_WMT.csv') # Walmart data
# Create a new dataframe "df" with the "Close" column of the three companies
df = pd.DataFrame()
df['AAPL'] = df1['Close']
df['GOOG'] = df2['Close']
df['WMT'] = df3['Close']
# Plot the three time series
plt.plot(df['AAPL'], label='AAPL')
plt.plot(df['GOOG'], label='GOOG')
plt.plot(df['WMT'], label='WMT')
plt.legend(loc='upper left')
plt.xlabel('$t$')
plt.ylabel('$P(t)$')
plt.show()
R = df.diff(periods=1)
plt.plot(R['AAPL'], label='AAPL')
plt.plot(R['GOOG'], label='GOOG')
plt.plot(R['WMT'], label='WMT')
plt.legend(loc='upper left')
plt.xlabel('$t$')
plt.ylabel('$R(t)$')
plt.show()
r = R/df.shift(periods=1)
x = r[1:] # remove the first row of r (since it consists of NaN), and call the data matrix 'x'
plt.plot(r['AAPL'], label='AAPL')
plt.plot(r['GOOG'], label='GOOG')
plt.plot(r['WMT'], label='WMT')
plt.legend(loc='upper left')
plt.xlabel('$t$')
plt.ylabel('$r(t)$')
plt.show()
from statsmodels.tsa.stattools import adfuller
for col in x:
series = x[col].values
result = adfuller(series)
print("p-value for {0}: {1}".format(col,result[1]))
def r_bar(X):
return X.mean()
def rP(X,w):
return np.dot(r_bar(X),w)
def jac_rP(X,w):
return r_bar(X)
def sigmaP2(X,w):
m = len(X)
Y = np.dot(X,w)
return (1/m) * np.dot(Y,Y) - np.dot(r_bar(X),w)**2
def jac_sigmaP2(X,w):
m = len(X)
XX = np.dot(np.transpose(X),X)
return (2/m) * np.dot(XX,w) - np.dot(r_bar(X),w) * r_bar(X)
# minimum desired daily rate of return (to find yearly one, compound: (1+r_star)**n - 1, for n=253 trading days)
r_star = 0.0008
# initial guess
w0 = 0.33 * np.ones(3)
# bounds, function to be minimized and constraints
bounds = [[0,1] for i in range(len(w0))]
def f(w):
return sigmaP2(x,w)
def jac_f(w):
return jac_sigmaP2(x,w)
def c(w):
return sum(w) - 1
def jac_c(w):
return 1.0 * np.ones_like(w)
def c_tilde(w):
return rP(x,w) - r_star
def jac_c_tilde(w):
return jac_rP(x,w)
cons = []
cons.append({'type': 'eq', 'fun': c, 'jac': jac_c})
cons.append({'type': 'ineq', 'fun': c_tilde, 'jac': jac_c_tilde})
# Run the solver
from scipy.optimize import minimize
res = minimize(f, w0, method='SLSQP', jac=jac_f, constraints=cons, bounds=bounds)
w_min1 = res.x
print("w_min = {0}".format(w_min1))
print("sum = {0}".format(sum(w_min1)))
print("rP = {0}".format(rP(x,w_min1)))
print("sigmaP2 = {0}".format(sigmaP2(x,w_min1)))
print("Sharpe = {0}".format(rP(x,w_min1)/np.sqrt(sigmaP2(x,w_min1))))
# maximum acceptable volatility
sigma_star2 = 0.00009
# initial guess
w0 = 0.33 * np.ones(3)
# bounds, function to be minimized and constraints
bounds = [[0,1] for i in range(len(w0))]
def f(w):
return -rP(x,w)
def jac_f(w):
return -jac_rP(x,w)
def c(w):
return sum(w) - 1
def jac_c(w):
return 1.0 * np.ones_like(w)
def c_tilde(w):
return sigma_star2 - sigmaP2(x,w)
def jac_c_tilde(w):
return -jac_sigmaP2(x,w)
cons = []
cons.append({'type': 'eq', 'fun': c, 'jac': jac_c})
cons.append({'type': 'ineq', 'fun': c_tilde, 'jac': jac_c_tilde})
# Run the solver
from scipy.optimize import minimize
res = minimize(f, w0, method='SLSQP', jac=jac_f, constraints=cons, bounds=bounds)
w_min2 = res.x
print("w_min = {0}".format(w_min2))
print("sum = {0}".format(sum(w_min2)))
print("rP = {0}".format(rP(x,w_min2)))
print("sigmaP2 = {0}".format(sigmaP2(x,w_min2)))
print("Sharpe = {0}".format(rP(x,w_min2)/np.sqrt(sigmaP2(x,w_min2))))
# initial guess
w0 = 0.33 * np.ones(3)
# bounds, function to be minimized and constraints
bounds = [[0,1] for i in range(len(w0))]
def f(w):
return -rP(x,w)/np.sqrt(sigmaP2(x,w))
def jac_f(w):
return jac_sigmaP2(x,w) * rP(x,w)/(2 * sigmaP2(x,w)**(3/2)) - jac_rP(x,w)/np.sqrt(sigmaP2(x,w))
def c(w):
return sum(w) - 1
def jac_c(w):
return 1.0 * np.ones_like(w)
cons = []
cons.append({'type': 'eq', 'fun': c, 'jac': jac_c})
# Run the solver
from scipy.optimize import minimize
res = minimize(f, w0, method='SLSQP', jac=jac_f, constraints=cons, bounds=bounds)
w_min3 = res.x
print("w_min = {0}".format(w_min3))
print("sum = {0}".format(sum(w_min3)))
print("rP = {0}".format(rP(x,w_min3)))
print("sigmaP2 = {0}".format(sigmaP2(x,w_min3)))
print("Sharpe = {0}".format(rP(x,w_min3)/np.sqrt(sigmaP2(x,w_min3))))
bounds = [[0,1] for i in range(len(w0))]
r_max = max(r_bar(r))
r_min = min(r_bar(r))
r_interval = np.linspace(r_min,r_max,num=30)
sigma2s = []
for r_star in r_interval:
def f(w):
return sigmaP2(x,w)
def jac_f(w):
return jac_sigmaP2(x,w)
def c1(w):
return sum(w) - 1
def jac_c1(w):
return 1.0 * np.ones_like(w)
def c2(w):
return rP(x,w) - r_star
def jac_c2(w):
return jac_rP(x,w)
cons = []
cons.append({'type': 'eq', 'fun': c1, 'jac': jac_c1})
cons.append({'type': 'eq', 'fun': c2, 'jac': jac_c2})
w0 = 0.33 * np.ones(3)
from scipy.optimize import minimize
res = minimize(f, w0, method='SLSQP', jac=jac_f, constraints=cons, bounds=bounds)
sigma2s.append(sigmaP2(x,res.x))
plt.plot(sigmaP2(x,w_min1),rP(x,w_min1),'ro',label='min $\sigma^2$ for $r = 0.0008$')
plt.plot(sigmaP2(x,w_min2),rP(x,w_min2),'bo',label='max $r$ for $\sigma^2 = 0.00009$')
plt.plot(sigmaP2(x,w_min3),rP(x,w_min3),'go',label='max $r/\sigma$')
plt.plot(sigma2s,r_interval,'k',label='efficient frontier')
plt.xlabel("$\sigma^2$")
plt.ylabel("r")
plt.gca().set_xlim(left=0)
plt.gca().set_ylim(bottom=0)
plt.legend()
plt.show()