Untitled
unknown
plain_text
2 years ago
3.6 kB
10
Indexable
from scipy.optimize import minimize
# Function to Optimize
def minimize_return_variance_joint(weights, covariance_matrix, expected_returns, gamma=0.2):
return -np.sum(expected_returns * weights) + gamma * weights.T.dot(covariance_matrix).dot(weights)
def minimize_neg_returns(weights, covariance_matrix, expected_returns):
return -np.sum(expected_returns * weights)
def negative_sharpe_ratio(weights, covariance_matrix, expected_returns):
portfolio_return = np.dot(weights, expected_returns)
portfolio_variance = np.dot(weights.T, np.dot(covariance_matrix, weights))
sharpe_ratio = (portfolio_return) / np.sqrt(portfolio_variance)
return -sharpe_ratio
def compute_optimal_weights(covariance_matrix, expected_returns,
optimization_fn=minimize_neg_returns):
"""
Compute the optimal weights for a given covariance matrix and expected returns.
:param covariance_matrix: The covariance matrix of the assets.
:param expected_returns: The expected returns of the assets.
:return: The mean-variance optimal weights subject to constraints on the sum of weights and individual weights.
"""
num_assets = len(expected_returns)
objective_fn = lambda weights: optimization_fn(weights, covariance_matrix, expected_returns)
# Constraints
# -1 <= w_i <= 1 for all i
# sum(w_i) = 1
weight_sum_constraint = {'type': 'eq', 'fun': lambda weights: np.sum(weights) - 1.0}
bounds = tuple((-1, 1) for _ in range(num_assets))
initial_weights = np.ones(num_assets) / num_assets
# Sequential Least SQuares Programming (SLSQP).
# https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html
# https://docs.scipy.org/doc/scipy/reference/optimize.minimize-slsqp.html
results = minimize(objective_fn,
initial_weights,
method='SLSQP',
bounds=bounds,
constraints=[weight_sum_constraint],
options={'disp': False})
return results
def compute_returns_info(allocation_mat_df, holdout_df, N):
predicted_indicies = [r.rstrip('_hat').lstrip('A_') for r in allocation_mat_df.columns]
# NOTE: We don't need to shift the dataframe because
# at construction we stored the "forward returns" in the dataframe, and are using the
# stock "forward returns" to predict the "index forward returns"
realized_returns = holdout_df[predicted_indicies]
portfolio_returns = np.sum(allocation_mat_df.iloc[:N, :].values * realized_returns.iloc[:N, :].values, axis=1)
portfolio_returns_df = pd.DataFrame(portfolio_returns, columns=['Portfolio_Returns'])
# Compute average returns, std, and sharpe
avg_returns = portfolio_returns_df.mean().values[0]
std_returns = portfolio_returns_df.std().values[0]
sharpe_ratio = avg_returns / std_returns
print(f"Portfolio Average Returns (BPS): {avg_returns : .3f}")
print(f"Portfolio Std Returns: {std_returns : .3f}")
print(f"Portfolio Sharpe Ratio: {sharpe_ratio : .3f}")
# Convert values to annualized
# Assuming t is in days, which may not be true
anual_returns_bps = avg_returns * 252
anual_std_returns = std_returns * np.sqrt(252)
anual_sharpe_ratio = anual_returns_bps / anual_std_returns
print(f"In Annualized Terms (BPS): mu={anual_returns_bps : .3f}, sigma={anual_std_returns : .3f}, Sharpe={anual_sharpe_ratio : .3f}")
portfolio_returns_df.hist(bins=100)
plt.show()
Editor is loading...
Leave a Comment