Skip to content

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 Portfolio Optimization 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.

Python
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.

Python
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.

Python
log_returns = np.log(adj_close_df / adj_close_df.shift(1))
log_returns = log_returns.dropna()

Section 4: Calculate Covariance Matrix

We compute the covariance matrix using the annualized log returns.

Python
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.

Python
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.

Python
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.

Python
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.

Python
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.

In conclusion, we have built the Black Scholes model in Python. With just a few lines of code, you can determine the theoretical price of both call and put options.

Find discounts on finance certificates, courses and products here.

Affiliate disclosure: I may earn a commission if you subscribe through this link.

Partner Discount

Financial Modeling Prep

Explore Financial Data APIs with FMP

25% Off Through This Link

FMP is an optional API source for extending this data workflow. The tutorial's free provider and code remain unchanged; FMP requires a separate integration. Check plan-specific history, security and region coverage, and adjustment conventions. Follow this link and confirm the discount and total before paying.

  • Financial statements and market data APIs
  • Plan-specific datasets and request limits

Contact Me

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

Search
Contact Ryan

Get in touch

Contact Me

Have a question or want to work together? Fill out the form below and we'll get back to you as soon as possible.

Contact Form Demo

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Results appear as you type. Use the arrow keys to move through them.