Asset allocation is the most important decision that any investor needs to face. They need to decide how to spread their total capital over certain assets (in this case, stocks). When considering the allocation, the investor wants to balance the risk and the potential reward. At the same time, the allocation depends on factors such as individual goals, risk tolerance, and the investment horizon.
The key framework used in asset allocation is the Modern Portfolio Theory (MPT), which was introduced by the Nobel Prize winner Harry Markowitz. MPT describes how investors can construct portfolios to maximize their expected returns for a given level of risk or, conversely, minimize risk for a given level of expected return. The mathematical framework used to achieve this is called mean-variance optimization.
The main insight from MPT is that investors should not evaluate an asset’s performance alone. Instead, they should evaluate how it would impact the performance of a portfolio of assets. Another important takeaway is the concept of diversification, which means that owning different kinds of assets reduces risk. That is because the loss or gain of a particular security has less impact on the overall portfolio’s performance.
Your task
In the dynamic realm of finance, data scientists/analysts are often tasked with finding optimal investment strategies. Imagine you're one such analyst, and you were asked to build an effective portfolio comprising FAANG stocks – Facebook (Meta), Apple, Amazon, Netflix, and Google. Your goal is to maximize returns while mitigating risk.
In this project, you are tasked to find the optimal allocation to the FAANG stocks based on historical stock price data spanning the years 2020-2023. The dataset is stored in the faang_stocks.csv file. For each trading day, it contains the close prices of the five tech companies.
# Importing libraries
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from pypfopt.efficient_frontier import EfficientFrontier
from pypfopt import risk_models
from pypfopt import expected_returns
# Setting the plotting style to be colorblind-friendly
plt.style.use("seaborn-colorblind")
# Loading data
stock_prices_df = pd.read_csv("faang_stocks.csv", index_col="Date")
# Changing the index to a datetime type allows for easier filtering and plotting.
stock_prices_df.index = pd.to_datetime(stock_prices_df.index)
stock_prices_df# Plotting the stock prices
stock_prices_df.plot(title="FAANG stock prices from years 2020-2023");# The expected returns and the annualized Sharpe ratio of an equally-weighted portfolio (used as a benchmark to compare expected returns/sharpe ratios of differently weighted portfolios)
# percentage returns
returns = stock_prices_df.pct_change().dropna()
# asset weights
weights = np.array([0.2, 0.2, 0.2, 0.2, 0.2])
# weight adjusted returns
pf_returns = returns.dot(weights)
# expected return (mean of weight adjusted returns)
benchmark_exp_return = pf_returns.mean()
# sharpe ratio (risk adjusted return)
benchmark_sharpe_ratio = benchmark_exp_return / pf_returns.std() * np.sqrt(252)
print(benchmark_sharpe_ratio)# Portfolio that minimizes volatility
# Calculate expected returns mu
mu = expected_returns.mean_historical_return(stock_prices_df, compounding=False)
# Calculate the covariance matrix Sigma (risk)
Sigma = risk_models.sample_cov(stock_prices_df)
'''
Obtain the efficient frontier (bunch of differet portfolios. Each portfolio has different weights on the assets that result in portfolios with different risks and returns. the portfolios that have the highest returns for a level of risk or lowest risk for a level of return are considered efficient)
'''
ef = EfficientFrontier(mu, Sigma)
# Calculate weights for the minimum volatility portfolio
raw_weights_minvol = ef.min_volatility()
mv_portfolio = pd.Series(raw_weights_minvol)
# Find the minimized volatility
mv_portfolio_vol = ef.portfolio_performance(risk_free_rate=0)[1]
print(mv_portfolio_vol)# Create a new instance of EfficientFrontier
ef = EfficientFrontier(mu, Sigma)
# Calculate weights for the max sharpe portfolio
raw_weights_maxsharpe = ef.max_sharpe(risk_free_rate=0)
ms_portfolio = pd.Series(raw_weights_maxsharpe)
# Find the sharpe ratio
ms_portfolio_sharpe = ef.portfolio_performance(risk_free_rate=0)[2]
print(ms_portfolio_sharpe)