Portfolio Optimization using Python and Modern Portfolio Theory

Portfolio Optimization is an essential aspect of modern investing, and Modern Portfolio Theory (MPT) is a well-known approach to achieve an optimal portfolio. In this article, we will walk through a Python script that demonstrates how to optimize a portfolio of stocks using MPT. We will use Yahoo Finance data and the Scipy library to find the optimal weights of assets in the portfolio that maximize the Sharpe ratio.

Section 1: Define Tickers and Time Range

First, we define a list of tickers for the assets we want to include in the portfolio. In this example, we use five ETFs representing various asset classes: SPY, BND, GLD, QQQ, and VTI. We then set the start and end dates for our analysis. We use a five-year historical time range for our calculations.

				
					tickers = ['SPY','BND','GLD','QQQ','VTI']
end_date = datetime.today()
start_date = end_date - timedelta(days = 5*365)
				
			

Section 2: Download Adjusted Close Prices

Next, we create an empty DataFrame to store the adjusted close prices of each asset. We use the yfinance library to download the data from Yahoo Finance.

				
					adj_close_df = pd.DataFrame()
for ticker in tickers:
    data = yf.download(ticker, start = start_date,end = end_date)
    adj_close_df[ticker] = data['Adj Close']
				
			

Section 3: Calculate Lognormal Returns

We calculate the lognormal returns for each asset and drop any missing values.

				
					console.log( 'Code is Poetry' );
				
			

Section 4: Calculate Covariance Matrix

We compute the covariance matrix using the annualized log returns.

				
					cov_matrix = log_returns.cov() * 252

				
			

Section 5: Define Portfolio Performance Metrics

We define functions to calculate the portfolio standard deviation, expected return, and Sharpe ratio.

				
					def standard_deviation(weights, cov_matrix):
    variance = weights.T @ cov_matrix @ weights
    return np.sqrt(variance)

def expected_return(weights, log_returns):
    return np.sum(log_returns.mean()*weights)*252

def sharpe_ratio(weights, log_returns, cov_matrix, risk_free_rate):
    return (expected_return(weights, log_returns) - risk_free_rate) / standard_deviation(weights, cov_matrix)
				
			

Section 6: Portfolio Optimization

We set the risk-free rate, define a function to minimize the negative Sharpe ratio, and set constraints and bounds for the optimization process.

				
					risk_free_rate = .02

def neg_sharpe_ratio(weights, log_returns, cov_matrix, risk_free_rate):
    return -sharpe_ratio(weights, log_returns, cov_matrix, risk_free_rate)

constraints = {'type': 'eq', 'fun': lambda weights: np.sum(weights) - 1}
bounds = [(0, 0.4) for _ in range(len(tickers))]
initial_weights = np.array([1/len(tickers)]*len(tickers))

optimized_results = minimize(neg_sharpe_ratio, initial_weights, args=(log_returns, cov_matrix, risk_free_rate), method='SLSQP', constraints=constraints, bounds=bounds)
				
			

Section 7: Analyze the Optimal Portfolio

We obtain the optimal weights and calculate the expected annual return, expected volatility, and Sharpe ratio for the optimal portfolio.

				
					optimal_weights = optimized_results.x

print("Optimal Weights:")
for ticker, weight in zip(tickers, optimal_weights):
    print(f"{ticker}: {weight:.4f}")

optimal_portfolio_return = expected_return(optimal_weights, log_returns)
optimal_portfolio_volatility = standard_deviation(optimal_weights, cov_matrix)
optimal_sharpe_ratio = sharpe_ratio(optimal_weights, log_returns, cov_matrix, risk_free_rate)

print(f"Expected Annual Return: {optimal_portfolio_return:.4f}")
print(f"Expected Volatility: {optimal_portfolio_volatility:.4f}")
print(f"Sharpe Ratio: {optimal_sharpe_ratio:.4f}")
				
			

Display the Final Portfolio in a Plot

We create a bar chart to visualize the optimal weights of the assets in the portfolio.

				
					import matplotlib.pyplot as plt

plt.figure(figsize=(10, 6))
plt.bar(tickers, optimal_weights)

plt.xlabel('Assets')
plt.ylabel('Optimal Weights')
plt.title('Optimal Portfolio Weights')

plt.show()
				
			

Conclusion

This Python script demonstrates how to use Modern Portfolio Theory to optimize a portfolio of stocks. By calculating the optimal weights for each asset, we aim to maximize the Sharpe ratio, providing the highest possible risk-adjusted return. This approach can help investors create a well-diversified portfolio and make informed decisions when allocating their investments.

Hire me for your next Project

I can assist you with your financial modeling and quantitative finance projects, leveraging my expertise and experience in the field.

Contact Me

Feel free to reach out to discuss your freelance project needs, and let’s collaborate on bringing your vision to life!