Backtesting A Vanilla Call Option Or Warrant On Python

How can we approximate how well a call option or Warrant would’ve done for a specific stock(s)?

The Derivative Tester
5 min readFeb 26, 2022
Photo by Dan Russo on Unsplash

Now, in my chain of learning, we want to eventually backtest a bunch of trading strategies. But first, we should take a step-by-step approach, which in this case means we have to figure out how to backtest a call option, whether it is “Buy” or “Sell”.

I’ve divided this into 4 key sections:

  1. Defining what a call option is and what the payoff for a call option is (additionally I will also include the payoff for call warrants)
  2. List out the extensions used and also the terms of the option/warrant we will be testing.
  3. Creating the call option backtest dataset
  4. Visualizing and looking at the final result

What is a Call Option Or Call Warrant?

Call options are contracts that give the option owner the right but not the obligation to buy the underlying asset at a specified price (i.e. strike price) within a specific period (i.e. tenor). Call options have a few key details:

  1. Can be a Buy or Sell option.
  2. Can be European or American
  3. For Exchange Traded Options, usually standardized contracts developed by an exchange. For OTC Markets, could be customizable and issued by a Financial Institution.
  4. Could be unlimited losses if it was short.
  5. Margin might be required for the option writer.

For Buy Call Options,

Payoff = MAX(0, Spot Price — Strike Price)

Profit= Payoff — Premium Paid

For Sell Call Options,

Payoff = MIN(0, Strike Price — Spot Price)

Profit= Payoff + Premium Paid

How about Call Warrants? Structured Call Warrants are instruments issued usually by third-party financial institutions such as banks, and give warrant holders a right but not the obligation to buy the underlying asset at a specified price (i.e. strike price) within a specific period (i.e. tenor). However, warrants and options have some key differences:

  1. Only has Buy Warrants.
  2. Generally Cash Settled.
  3. Losses capped at (total investment sum + transaction fees).
  4. No margin generally.

For Structured Call Warrants,

Payoff Percentage = MAX(0, Spot Price — Strike Price) / Initial Price

Profit Percentage = Payoff Percentage — Premium Percentage

Profit = Notional * Profit Percentage

Of course, I am not an expert and the items do not cover every single feature and point for options or warrants above, but given that we are just doing the backtest for these items, the definitions should be more than enough.

If you want to read more, you can look at the various articles available online on websites such as Investopedia or SGX. I’ll add some links at the bottom for all of you to refer to.

Now, we will be building code to see how much profit (for options) or profit percentage (for warrants) we would have gotten on a historical basis

Setting Up Our Code:

These are the imports I use for this project:

Following that, we want to define the important points for the call option or warrant before we begin our backtest. Primarily, we want to input our option tenor, strike price percentage, premium, stock basket we are testing, whether it is a buy or sell, and whether it is an option or warrant. In this case, we are going to test AMZN, AAPL, MSFT, and GOOGL, and for simplicity’s sake, we are going to be only testing European options (i.e. only exercisable at maturity).

How Do We Create A Dataset To Test With:

First, we want to download the historical dataset. In this case, we are downloading the daily data for all the stocks for the last 15 days, and selecting only the close price data.

Now, taking the Close data, we want to create the strike data based on the Close data (here we are assuming that the initial spot price of the options we are testing are based on day close price). We also want to create a dataset that shows the final closing price of the option at maturity. By assuming each year’s number of business days is 252 days, we can shift the end prices up the index by 252 * Tenor (In Years).

Here we come to the slightly harder part. we want to decide if:

a. Buy or Sell

b. Is it a Warrant or Option

For the Buy Call Option, Buy Call Warrant, and Sell Call Option, we can apply the formulas above. However, since there isn’t a Sell Call Warrant, we have to churn out an error that stops the code and prevents it from executing the code. This can be done using sys.exit function. Note that for the Warrant backtest the data will be in percentage.

Following that we can delete the rows of data with N/A data before printing to check our final dataset.

Visualizing The Data & Assessing It:

This data is based on percentages, given that we are testing a Buy Warrant in our example. Now we can just use the following simple code to visualize our data and also describe the stats:

What if we want to try to observe a bit more? For example, what if I want to see how the distribution comparison for each stock and which stocks have the highest potential for positive outlier performance? We can use a Box and Whisker plot to visualize the comparison between the 4 stocks:

From the comparison, we can see that from a historical basis, it looks like AMZN has the highest positive outlier return whereas it seems like AAPL seems to have the most positive returns out of the 4 stocks. Once again, past performance is not a good predictor of future results but this is just one example of a way we could look at the data.

We can also use a histogram to visualize the data for AMZN, for example. This helps you drill down a bit further into the data.

Hope all of you had fun and that it was an interesting topic for all of you to start assessing the historical performance of some derivatives.

In my next topic, I will assess the performance of a European Call Spread. Stay tuned!

THANK YOU :)

For those who have not, please read the background and disclaimers here.

Some links & items you guys can read:

SGX Guide To Structured Warrants

--

--