Boost a strategy to 1.03 Sharpe ratio with new Gamma levels

March 15, 2025
Facebook logo.
Twitter logo.
LinkedIn logo.

Boost a strategy to 1.03 Sharpe ratio with new Gamma levels

This is the second newsletter about using Gamma exposure to find support and resistance levels in the stock market.

The first issue, focused on finding the Gamma levels. In this issue, we’ll build a simple backtest using the levels.

We’ll use a data sample from my friends at MenthorQ.

MenthorQ uses the options market to generate trading signals for stocks and futures.

Today’s newsletter gives you code to build a simple backtest using their data.

Let’s go!

Boost a strategy to 1.03 Sharpe ratio with new Gamma levels

Gamma exposure plays a major role in options trading by affecting delta sensitivity and market movements.

It is the cumulative Gamma value of options for a specific stock or index. It’s used to help understand market maker activities.

Gamma amplifies delta, impacting pricing and resistance levels.

In practice, high Gamma exposure in call options might lead market makers to sell the underlying asset to adjusted hedges. This can create resistance levels.

Similarly, high gamma in put options often triggers buying, establishing support zones.

Traders use Gamma to anticipate these trends.

We can use these levels to help identify optimal entry and exit points in the market.

Let's see how it works with Python.

Imports and set up

These libraries help us analyze financial data and create visualizations. You can use the sample data here for this analysis.

1import pandas as pd
2import numpy as np
3import matplotlib.pyplot as plt
4import yfinance as yf

We start by loading our data from a CSV file and preparing it for analysis.

1df = pd.read_csv('spy_levels.csv')
2df['Date'] = pd.to_datetime(df['Date'])
3df.set_index('Date', inplace=True)
4levels = df[["Call Resistance 0DTE", "Put Support 0DTE"]]
5levels.columns = ["resistance", "support"]
6
7spy = yf.download("SPY", start=levels.index.min(), end=levels.index.max()).Close
8levels = levels.copy()
9levels.loc[:, "spy"] = spy
10levels[["resistance", "support"]] = levels[["resistance", "support"]].shift()
11levels.dropna(inplace=True)

We load our data from a CSV file and convert the 'Date' column to datetime. We set the 'Date' as the index and select specific columns for our analysis. We then download SPY (S&P 500 ETF) closing prices using yfinance and add them to our dataset. Because the signals generated apply to the following trading day, we shift them one day forward. Finally, we remove any rows with missing data to ensure our analysis is based on complete information.

Implement our trading strategy

Next, we define our trading strategy based on support and resistance levels.

1strategy = levels.copy()
2strategy["position"] = 0
3
4strategy.loc[strategy.spy >= strategy.resistance, 'position'] = -1
5strategy.loc[strategy.spy <= strategy.support, 'position'] = 1
6
7strategy.position = (
8    strategy
9    .position
10    .replace(0, np.nan)
11    .ffill()
12    .fillna(0)
13)
14
15strategy["spy_returns"] = strategy.spy.pct_change()
16strategy["strategy_returns"] =  strategy.spy_returns * strategy.position.shift(1)

We create a copy of our data and add a 'position' column. We set our position to -1 (sell) when SPY price is above resistance and 1 (buy) when it's below support. We then forward-fill our positions to maintain them until the next signal. Finally, we calculate the returns for SPY and our strategy by multiplying SPY returns with our previous day's position.

Evaluate our strategy performance

Finally, we calculate cumulative returns and visualize our strategy's performance.

1strategy['cumulative_returns'] = (1 + strategy.strategy_returns).cumprod()
2np.sqrt(252) * strategy.strategy_returns.mean() / strategy.strategy_returns.std()
3
4plt.figure(figsize=(12, 6))
5plt.plot(strategy.cumulative_returns, label='Strategy')
6plt.plot((1 + strategy.spy_returns).cumprod(), label='Buy and Hold')
7plt.legend()
8plt.title('Strategy Performance vs Buy and Hold')
9plt.ylabel('Cumulative Returns')
10plt.show()

We calculate the cumulative returns for our strategy and compute its Sharpe ratio. We then create a plot comparing our strategy's performance to a simple buy-and-hold approach. This visualization helps us understand how well our strategy performs compared to passive investing in the S&P 500.

The result is a chart that looks like this.

You can see that when the strategy takes a position in January 2025, the results diverge from the buy and hold strategy. This results in a Sharpe ratio of 1.03, not quite beating the SPY’s Sharpe ratio of 1.25 over the same period. Even though the strategy didn’t beat SPY, there is a lot of room for improvements to the strategy!

Your next steps

I recommend checking out MenthorQ’s data. The team are top-notch traders and quants and have build an incredible product. When you sign up, you can get data for any number of instruments for much longer time frames.

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