Improve your Sharpe ratio with a simple hedge

January 4, 2025
Facebook logo.
Twitter logo.
LinkedIn logo.
Get this code in Google Colab

Improve your Sharpe ratio with a simple hedge

It’s hard enough to make money in the market.

But once you do, keeping it is the key to continued profitability.

Most non-professional investors and traders like us, miss an effective way to manage portfolio risk called hedging.

Hedging is the process of taking an offsetting position in an asset to neutralize exposure to that asset.

I spend a lot of time explaining hedging in my course, Getting Started With Python for Quant Finance.

By reading today’s newsletter, you’ll download factor data and construct a hedge to manage risk.

Let's go!

Improve your Sharpe ratio with a simple hedge

The Fama-French model enhances portfolio risk management by explaining stock returns through multiple factors.

These factors help investors understand market risk, size, value, profitability, and investment strategies.

In practice, hedging against these factors involves techniques like diversification, factor-based investing, and using derivatives. Investors adjust portfolios to align with desired risk exposures, using ETFs and options for precision. Dynamic rebalancing and quantitative models further aid in managing factor risks effectively.

Professionals use these strategies to stabilize portfolios amid economic shifts and changing market sentiments. They apply sophisticated tools to predict factor movements and make informed decisions. By doing so, they aim to enhance portfolio resilience and optimize returns.

Let's see how it works with Python.

Imports and set up

Import the libraries for the analysis.

1import yfinance as yf
2import pandas_datareader.data as web
3import pandas as pd
4import datetime as dt
5import statsmodels.api as sm
6import warnings
7warnings.filterwarnings("ignore")

Then define the stock tickers we want to analyze and specify the date range for our historical data. Then we download the adjusted closing prices for these stocks using the yfinance library.

1tickers = ['AAPL', 'MSFT', 'GOOGL']
2start_date = "2020-01-01"
3end_date = "2024-12-31"
4
5stock_data = yf.download(
6    tickers, 
7    start=start_date, 
8    end=end_date
9)['Adj Close']
10
11port_returns = (
12    stock_data
13    .pct_change()
14    .sum(axis=1)
15)
16
17port_returns.name = "port_returns"

We define a list of stock tickers for Apple, Microsoft, and Google, specifying the time period from January 1, 2020, to December 31, 2024.

Using yfinance, we download the adjusted closing prices for these stocks, which reflects the stock's actual market price adjusted for splits and dividends.

We then compute the daily percentage change to calculate the portfolio's daily returns by summing up the individual returns of each stock.

Finally, we name this series "port_returns" for clarity in subsequent analysis.

Fetch Fama French factors and preprocess data

Now we will fetch the Fama French factors, which are widely used in finance to analyze and explain returns. We'll then align this data with our stock returns.

1fama_french = web.DataReader(
2    "F-F_Research_Data_Factors_daily",
3    "famafrench",
4    start_date,
5    end_date
6)[0]
7
8fama_french = fama_french / 100  # Convert to decimals
9fama_french.index = fama_french.index.tz_localize("UTC")
10
11data = fama_french.join(port_returns, how="inner")
12
13excess_returns = data.port_returns - data.RF

We retrieve daily Fama French factors using the pandas_datareader library, which provides the size, value, and market risk factors. These factors are in percentage form, so we convert them into decimals for accurate computations.

The index of this data is then localized to UTC to match the timezone of our stock data.

We join the Fama French data with our portfolio returns, keeping only the dates common to both datasets. Finally, we calculate the excess returns of the portfolio by subtracting the risk-free rate (RF) from the portfolio returns.

Model excess returns using Fama French factors

Next, we prepare the independent variables for our regression model, add a constant term, and fit the model to the data. This will help us understand the influence of Fama French factors on our excess returns.

1X = data[["SMB", "HML"]]
2X = sm.add_constant(X)
3
4model = sm.OLS(excess_returns, X).fit()
5
6hedge_weights = -model.params[1:]

We select the Small-Minus-Big (SMB) and High-Minus-Low (HML) factors from our dataset as our independent variables.

To account for the intercept in our regression model, we add a constant to this matrix. We then fit an Ordinary Least Squares (OLS) regression model using statsmodels to explain the excess returns of our portfolio by these factors.

The resulting model's parameters (excluding the constant) are negated to calculate the hedge weights, indicating how much exposure to each factor is needed to minimize risk.

Simulate and analyze the hedged portfolio

Finally, we use the hedge weights to create a hedged portfolio and simulate its returns. We compare these with the unhedged returns to analyze the impact of hedging.

1hedge_portfolio = (data[["SMB", "HML"]] @ hedge_weights).dropna()
2
3hedged_portfolio_returns = port_returns.loc[hedge_portfolio.index] + hedge_portfolio
4
5hedge = pd.DataFrame({
6    "unhedged_returns": port_returns.loc[hedged_portfolio_returns.index],
7    "hedged_returns": hedged_portfolio_returns
8})
9
10hedge.mean() / hedge.std()

We calculate the hedged portfolio returns by applying the hedge weights to the SMB and HML factors, effectively offsetting their impact on the portfolio.

We then add these hedged returns to the original portfolio returns, aligning them by date to ensure accuracy.

We construct a DataFrame to hold both unhedged and hedged returns for comparison.

Finally, we compute the Sharpe ratio for both sets of returns, which measures risk-adjusted performance. By hedging, we improve the Sharpe ratio 35 basis points!

Your next steps

Try experimenting with different stock tickers or adjusting the date range to see how the portfolio performance changes. You could also explore incorporating additional factors into the model to better capture the nuances of market behavior.

Finally, this analysis uses the same data over the entire analysis period, which could introduce lookahead bias. In a few lines of code, you can create a rolling regression to avoid this.

Man with glasses and a wristwatch, wearing a white shirt, looking thoughtfully at a laptop with a data screen in the background.