Backtest a delta-neutral trading strategy in Python

Daryl
5 min readJul 8, 2024

--

Photo by Kanchanara on Unsplash

One of the most powerful uses of Options, is their ability to insulate an investment portfolio from directional swings in the underlying asset.

If you buy a block of Apple shares your positions:

  • Gains value when Apple stock rises
  • Loses value when Apple stock falls

Your positions is said to have directional exposure, and a delta of 1.

Simply holding your position exposes you to unrealised gains and losses in the stock.

For most retail investors, small losses are part and parcel of investing.

However for larger institutional investors, subject to certain investment mandates (eg. Unrealised loss cannot exceed 15%), sitting on unrealised losses are a no-go.

Eliminating Delta

Luckily, as we shall see, it is possible to combine options together with your stock holdings to partially/totally eliminate delta from your position through actively managing.

In options terminology, this is referred to as delta-hedging.

Offsetting your directional exposure

With holdings of stock in your current inventory, an easy way to offset your directional exposure is write or short, call options.

Short call payoff
Delta for a certain call option

For this particular contract, the Call has a delta of 0.77, meaning that to be completely delta neutral, your ratio of stock to call options should be 77:100.

For many options contracts, they represent control of 100 units of stock/commodities/assets. As such, you ought to purchase/hold 77 shares for every call shorted.

Number of shares held = Number of Short Calls * 100 * delta

If the following day/week, the delta for the calls increase to 0.80, then an additional 3 shares will have to be bought for each call.

Strictly speaking the reverse works too, calls can be shorted/bought to keep their delta in line with your holdings of stock.

However, since calls represent 100 units of stock, this allows you less control over the exact value of delta. (eg. You cannot purchase 0.2 calls to obtain 20 shares worth of delta).

Testing our delta-hedging strategy

The example used in the video above, can be found in the following textbook by John Hull.

In this example, the book starts off with 1000 short calls, representing a short position worth 100,000 shares.

The goal of the exercise is to rebalance the portfolio every week based on the delta of the calls.

When the calls are first bought in week 0, the calls have a delta of 0.522. The total delta of all the calls are thus 52,200.

Given that a share has a delta of 1, we need to purchase/hold 52,200 shares to perfectly balance this out.

This process continues for all the subsequent weeks.

NOTE

If you observe the price of the stock in week 1 and 2, it appears to drop.

Yet, we are selling stock.

This results in realised losses on our stock trades.

However, given that our short call positions have a negative delta. (Gain in value as the stock price falls), we can close out our calls by buying them back at a lower price, realising a gain on our short position.

The net effect is that any gains offset any losses in the portfolio.

Why would you want this

For the most part, the goal of delta hedging is to insulate unknown direction risk.

This tends to happen in the lead up to events such as elections or government announcements.

If bearish news comes to past, and the assets in the portfolio take a hit, the gains from the options will partially/fully offset the loss.

Python code

The Hull textbook has given us a series of of delta values and prices.

Note that delta values are quoted as positive, if we are shorting we need to turn them negative.

delta_values = np.array(delta_values)
delta_values=list(-delta_values) if apple.long_short_option == 'Short'
else delta_values
import numpy as np
prices = [49, 48.12, 47.37, 50.25, 51.75, 53.12, 53, 49.88, 48.5,
49.88, 50.37, 52.13, 51.88, 52.87, 54.87, 54.62, 55.87, 57.25]
delta_values = [0.522, 0.458, 0.4, 0.596, 0.693, 0.774, 0.771, 0.706, 0.674, 0.787, 0.550,
0.413, 0.542, 0.591, 0.768, 0.759, 0.865, 0.978, 0.990, 1, 1]

delta_values = np.array(delta_values)

class Position:
def __init__(self, current_pos_delta,
option_contracts,
shares_held,
cash,
long_short_option,
PnL
):
self.current_pos_delta = current_pos_delta
self.option_contracts = option_contracts
self.shares_held = shares_held
self.cash = cash
self.long_short_option = long_short_option
self.PnL = PnL
pass

apple = Position(0, 1000, 0, 0, 'Short', 0)

def buy_sell(long_short, price, quantity):
if long_short=="Long":
apple.cash -= price*quantity
pass

elif long_short=='Short/Sell':
apple.cash += price*quantity
pass

def delta_hedge(option_contracts,
option_delta,
shares_held,
price
):
new_pos_delta = option_contracts*option_delta*100
delta_hedge = new_pos_delta - apple.current_pos_delta
long_short = "Short/Sell" if delta_hedge>0 else "Long"
shares_held += -new_pos_delta
holdings_value = shares_held*price

buy_sell(long_short=long_short, price=price, quantity=abs(int(delta_hedge)))
apple.PnL = holdings_value+apple.cash

print("|Delta to hedge: ", delta_hedge, "|",
"Shares/Tokens to" ,long_short,": " ,abs(int(delta_hedge)),"|",
"Shares Held: ", int(shares_held), "|",
"Value of Holdings: $", holdings_value ,"|",
"Cash holdings: $", apple.cash, "|",
"PnL: $", apple.PnL, "|")
apple.current_pos_delta = new_pos_delta
pass

delta_values=list(-delta_values) if apple.long_short_option == 'Short' else delta_values

for (delta, price) in zip(delta_values, prices):
delta_hedge(option_contracts=apple.option_contracts,
option_delta=delta,
shares_held=apple.shares_held,
price=price)

Running the code above and getting the output in the command line, we get the following:

--

--

Daryl
Daryl

Written by Daryl

Graduated with a Physics degree, I write about physics, coding and quantitative finance.

No responses yet