27 min read

In this article by Michael Heydt, author of Mastering pandas for Finance, we will examine working with options data provided by Yahoo! Finance using pandas. Options are a type of financial derivative and can be very complicated to price and use in investment portfolios. Because of their level of complexity, there have been many books written that are very heavy on the mathematics of options. Our goal will not be to cover the mathematics in detail but to focus on understanding several core concepts in options, retrieving options data from the Internet, manipulating it using pandas, including determining their value, and being able to check the validity of the prices offered in the market.

(For more resources related to this topic, see here.)

Introducing options

An option is a contract that gives the buyer the right, but not the obligation, to buy or sell an underlying security at a specific price on or before a certain date. Options are considered derivatives as their price is derived from one or more underlying securities. Options involve two parties: the buyer and the seller. The parties buy and sell the option, not the underlying security.

There are two general types of options: the call and the put. Let’s look at them in detail:

  • Call: This gives the holder of the option the right to buy an underlying security at a certain price within a specific period of time. They are similar to having a long position on a stock. The buyer of a call is hoping that the value of the underlying security will increase substantially before the expiration of the option and, therefore, they can buy the security at a discount from the future value.
  • Put: This gives the option holder the right to sell an underlying security at a certain price within a specific period of time. A put is similar to having a short position on a stock. The buyer of a put is betting that the price of the underlying security will fall before the expiration of the option and they will, thereby, be able to gain a profit by benefitting from receiving the payment in excess of the future market value.

The basic idea is that one side of the party believes that the underlying security will increase in value and the other believes it will decrease. They will agree upon a price known as the strike price, where they place their bet on whether the price of the underlying security finishes above or below this strike price on the expiration date of the option.

Through the contract of the option, the option seller agrees to give the buyer the underlying security on the expiry of the option if the price is above the strike price (for a call).

The price of the option is referred to as the premium. This is the amount the buyer will pay to the seller to receive the option. This price of an option depends upon many factors, of which the following are the primary factors:

  • The current price of the underlying security
  • How long the option needs to be held before it expires (the expiry date)
  • The strike price on the expiry date of the option
  • The interest rate of capital in the market
  • The volatility of the underlying security
  • There being an adequate interest between buyer and seller around the given option

The premium is often established so that the buyer can speculate on the future value of the underlying security and be able to gain rights to the underlying security in the future at a discount in the present.

The holder of the option, known as the buyer, is not obliged to exercise the option on its expiration date, but the writer, also referred to as the seller, however, is obliged to buy or sell the instrument if the option is exercised.

Options can provide a variety of benefits such as the ability to limit risk and the advantage of providing leverage. They are often used to diversify an investment portfolio to lower risk during times of rising or falling markets.

There are four types of participants in an options market:

  • Buyers of calls
  • Sellers of calls
  • Buyers of puts
  • Sellers of puts

Buyers of calls believe that the underlying security will exceed a certain level and are not only willing to pay a certain amount to see whether that happens, but also lose their entire premium if it does not. Their goal is that the resulting payout of the option exceeds their initial premium and they, therefore, make a profit. However, they are willing to forgo their premium in its entirety if it does not clear the strike price. This then becomes a game of managing the risk of the profit versus the fixed potential loss.

Sellers of calls are on the other side of buyers. They believe the price will drop and that the amount they receive in payment for the premium will exceed any loss in the price. Normally, the seller of a call would already own the stock. They do not believe the price will exceed the strike price and that they will be able to keep the underlying security and profit if the underlying security stays below the strike by an amount that does not exceed the received premium. Loss is potentially unbounded as the stock increases in price above the strike price, but that is the risk for an upfront receipt of cash and potential gains on loss of price in the underlying instrument.

A buyer of a put is betting that the price of the stock will drop beyond a certain level. By buying a put they gain the option to force someone to buy the underlying instrument at a fixed price. By doing this, they are betting that they can force the sale of the underlying instrument at a strike price that is higher than the market price and in excess of the premium that they pay to the seller of the put option.

On the other hand, the seller of the put is betting that they can make an offer on an instrument that is perceived to lose value in the future. They will offer the option for a price that gives them cash upfront, and they plan that at maturity of the option, they will not be forced to purchase the underlying instrument. Therefore, it keeps the premium as pure profit. Or, the price of the underlying instruments drops only a small amount so that the price of buying the underlying instrument relative to its market price does not exceed the premium that they received.

Notebook setup

The examples in this article will be based on the following configuration in IPython:

In [1]:
   import pandas as pd
   import numpy as np
   import pandas.io.data as web
   from datetime import datetime
 
   import matplotlib.pyplot as plt
   %matplotlib inline
 
   pd.set_option('display.notebook_repr_html', False)
   pd.set_option('display.max_columns', 7)
   pd.set_option('display.max_rows', 15) 
   pd.set_option('display.width', 82) 
   pd.set_option('precision', 3)

Options data from Yahoo! Finance

Options data can be obtained from several sources. Publicly listed options are exchanged on the Chicago Board Options Exchange (CBOE) and can be obtained from their website. Through the DataReader class, pandas also provides built-in (although in the documentation referred to as experimental) access to options data.

The following command reads all currently available options data for AAPL:

In [2]:
   aapl_options = web.Options('AAPL', 'yahoo')
 aapl_options = aapl_options.get_all_data().reset_index()

This operation can take a while as it downloads quite a bit of data. Fortunately, it is cached so that subsequent calls will be quicker, and there are other calls to limit the types of data downloaded (such as getting just puts).

For convenience, the following command will save this data to a file for quick reload at a later time. Also, it helps with repeatability of the examples. The data retrieved changes very frequently, so the actual examples in the book will use the data in the file provided with the book. It saves the data for later use (it’s commented out for now so as not to overwrite the existing file). Here’s the command we are talking about:

In [3]:
   #aapl_options.to_csv('aapl_options.csv')

This data file can be reloaded with the following command:

In [4]:
   aapl_options = pd.read_csv('aapl_options.csv', 
                             parse_dates=['Expiry'])

Whether from the Web or the file, the following command restructures and tidies the data into a format best used in the examples to follow:

In [5]:
   aos = aapl_options.sort(['Expiry', 'Strike'])[
     ['Expiry', 'Strike', 'Type', 'IV', 'Bid', 
         'Ask', 'Underlying_Price']] 
   aos['IV'] = aos['IV'].apply(lambda x: float(x.strip('%')))

Now, we can take a look at the data retrieved:

In [6]:
   aos
 
Out[6]:
           Expiry Strike Type     IV   Bid   Ask Underlying_Price
   158 2015-02-27     75 call 271.88 53.60 53.85           128.79
   159 2015-02-27     75 put 193.75 0.00 0.01           128.79
   190 2015-02-27     80 call 225.78 48.65 48.80           128.79
   191 2015-02-27     80 put 171.88 0.00 0.01           128.79
   226 2015-02-27     85 call 199.22 43.65 43.80           128.79

There are 1,103 rows of options data available. The data is sorted by Expiry and then Strike price to help demonstrate examples.

Expiry is the data at which the particular option will expire and potentially be exercised. We have the following expiry dates that were retrieved. Options typically are offered by an exchange on a monthly basis and within a short overall duration from several days to perhaps two years. In this dataset, we have the following expiry dates:

In [7]:
   aos['Expiry'].unique()
 
Out[7]:
   array(['2015-02-26T17:00:00.000000000-0700',
         '2015-03-05T17:00:00.000000000-0700',
         '2015-03-12T18:00:00.000000000-0600',
         '2015-03-19T18:00:00.000000000-0600',
         '2015-03-26T18:00:00.000000000-0600',
         '2015-04-01T18:00:00.000000000-0600',
         '2015-04-16T18:00:00.000000000-0600',
         '2015-05-14T18:00:00.000000000-0600',
         '2015-07-16T18:00:00.000000000-0600',
         '2015-10-15T18:00:00.000000000-0600',
         '2016-01-14T17:00:00.000000000-0700',
         '2017-01-19T17:00:00.000000000-0700'], 
dtype='datetime64[ns]')

For each option’s expiration date, there are multiple options available, split between puts and calls, and with different strike values, prices, and associated risk values.

As an example, the option with the index 158 that expires on 2015-02-27 is for buying a call on AAPL with a strike price of $75. The price we would pay for each share of AAPL would be the bid price of $53.60. Options typically sell 100 units of the underlying security, and, therefore, this would mean that this option would cost of 100 x $53.60 or $5,360 upfront:

In [8]:
   aos.loc[158]
 
Out[8]:
   Expiry             2015-02-27 00:00:00
   Strike                               75
   Type                              call
   IV                                 272
   Bid                               53.6
   Ask                               53.9
   Underlying_Price                   129
   Name: 158, dtype: object

This $5,360 does not buy us the 100 shares of AAPL. It gives us the right to buy 100 shares of AAPL on 2015-02-27 at $75 per share. We should only buy if the price of AAPL is above $75 on 2015-02-27. If not, we will have lost our premium of $5360 and purchasing below will only increase our loss.

Also, note that these quotes were retrieved on 2015-02-25. This specific option has only two days until it expires. That has a huge effect on the pricing:

  • We have paid $5,360 for the option to buy 100 shares of AAPL on 2015-02-27 if the price of AAPL is above $75 on that date.
  • The price of AAPL when the option was priced was $128.79 per share. If we were to buy 100 shares of AAPL now, we would have paid $12,879 now.
  • If AAPL is above $75 on 2015-02-27, we can buy 100 shares for $7500.

There is not a lot of time between the quote and Expiry of this option. With AAPL being at $128.79, it is very likely that the price will be above $75 in two days.

Therefore, in two days:

  • We can walk away if the price is $75 or above. Since we paid $5360, we probably wouldn’t want to do that.
  • At $75 or above, we can force execution of the option, where we give the seller $7,500 and receive 100 shares of AAPL. If the price of AAPL is still $128.79 per share, then we will have bought $12,879 of AAPL for $7,500+$5,360, or $12,860 in total. In technicality, we will have saved $19 over two days! But only if the price didn’t drop.
  • If for some reason, AAPL dropped below $75 in two days, we kept our loss to our premium of $5,360. This is not great, but if we had bought $12,879 of AAPL on 2015-02-5 and it dropped to $74.99 on 2015-02-27, we would have lost $12,879 – $7,499, or $5,380. So, we actually would have saved $20 in loss by buying the call option.

It is interesting how this math works out. Excluding transaction fees, options are a zero-loss game. It just comes down to how much risk is involved in the option versus your upfront premium and how the market moves. If you feel you know something, it can be quite profitable. Of course, it can also be devastatingly unprofitable.

We will not examine the put side of this example. It would suffice to say it works out similarly from the side of the seller.

Implied volatility

There is one more field in our dataset that we didn’t look at—implied volatility (IV). We won’t get into the details of the mathematics of how this is calculated, but this reflects the amount of volatility that the market has factored into the option.

This is different than historical volatility (typically the standard deviation of the previous year of returns).

In general, it is informative to examine the IV relative to the strike price on a particular Expiry date. The following command shows this in tabular form for calls on 2015-02-27:

In [9]:
   calls1 = aos[(aos.Expiry=='2015-02-27') & (aos.Type=='call')]
   calls1[:5]
 
Out[9]:
           Expiry Strike Type     IV   Bid   Ask Underlying_Price
   158 2015-02-27     75 call 271.88 53.60 53.85           128.79
   159 2015-02-27     75   put 193.75 0.00   0.01           128.79
   190 2015-02-27     80 call 225.78 48.65 48.80           128.79
   191 2015-02-27     80   put 171.88 0.00   0.01           128.79
   226 2015-02-27     85 call 199.22 43.65 43.80           128.79

It appears that as the strike price approaches the underlying price, the implied volatility decreases. Plotting this shows it even more clearly:

In [10]:
   ax = aos[(aos.Expiry=='2015-02-27') & (aos.Type=='call')] \
           .set_index('Strike')[['IV']].plot(figsize=(12,8))
   ax.axvline(calls1.Underlying_Price.iloc[0], color='g');

Mastering pandas for Finance

The shape of this curve is important as it defines points where options are considered to be either in or out of the money. A call option is referred to as in the money when the options strike price is below the market price of the underlying instrument. A put option is in the money when the strike price is above the market price of the underlying instrument. Being in the money does not mean that you will profit; it simply means that the option is worth exercising.

Where and when an option is in our out of the money can be visualized by examining the shape of its implied volatility curve. Because of this curved shape, it is generally referred to as a volatility smile as both ends tend to turn upwards on both ends, particularly, if the curve has a uniform shape around its lowest point. This is demonstrated in the following graph, which shows the nature of in/out of the money for both puts and calls:

Mastering pandas for Finance

A skew on the smile demonstrates a relative demand that is greater toward the option being in or out of the money. When this occurs, the skew is often referred to as a smirk.

Volatility smirks

Smirks can either be reverse or forward. The following graph demonstrates a reverse skew, similar to what we have seen with our AAPL 2015-02-27 call:

Mastering pandas for Finance

In a reverse-skew smirk, the volatility for options at lower strikes is higher than at higher strikes. This is the case with our AAPL options expiring on 2015-02-27. This means that the in-the-money calls and out-of-the-money puts are more expensive than out-of-the-money calls and in-the-money puts.

A popular explanation for the manifestation of the reverse volatility skew is that investors are generally worried about market crashes and buy puts for protection. One piece of evidence supporting this argument is the fact that the reverse skew did not show up for equity options until after the crash of 1987.

Another possible explanation is that in-the-money calls have become popular alternatives to outright stock purchases as they offer leverage and, hence, increased ROI. This leads to greater demand for in-the-money calls and, therefore, increased IV at the lower strikes.

The other variant of the volatility smirk is the forward skew. In the forward-skew pattern, the IV for options at the lower strikes is lower than the IV at higher strikes. This suggests that out-of-the-money calls and in-the-money puts are in greater demand compared to in-the-money calls and out-of-the-money puts:

Mastering pandas for Finance

The forward-skew pattern is common for options in the commodities market. When supply is tight, businesses would rather pay more to secure supply than to risk supply disruption. For example, if weather reports indicate a heightened possibility of an impending frost, fear of supply disruption will cause businesses to drive up demand for out-of-the-money calls for the affected crops.

Calculating payoff on options

The payoff of an option is a relatively straightforward calculation based upon the type of the option and is derived from the price of the underlying security on expiry relative to the strike price. The formula for the call option payoff is as follows:

Mastering pandas for Finance

The formula for the put option payoff is as follows:

Mastering pandas for Finance

We will model both of these functions and visualize their payouts.

The call option payoff calculation

An option gives the buyer of the option the right to buy (a call option) or sell (a put option) an underlying security at a point in the future and at a predetermined price. A call option is basically a bet on whether or not the price of the underlying instrument will exceed the strike price. Your bet is the price of the option (the premium). On the expiry date of a call, the value of the option is 0 if the strike price has not been exceeded. If it has been exceeded, its value is the market value of the underlying security.

The general value of a call option can be calculated with the following function:

In [11]:
   def call_payoff(price_at_maturity, strike_price):
       return max(0, price_at_maturity - strike_price)

When the price of the underlying instrument is below the strike price, the value is 0 (out of the money). This can be seen here:

In [12]:
   call_payoff(25, 30)
 
Out[12]:
   0

When it is above the strike price (in the money), it will be the difference of the price and the strike price:

In [13]:
   call_payoff(35, 30)
 
Out[13]:
   5

The following function returns a DataFrame object that calculates the return for an option over a range of maturity prices. It uses np.vectorize() to efficiently apply the call_payoff() function to each item in the specific column of the DataFrame:

In [14]:
   def call_payoffs(min_maturity_price, max_maturity_price, 
                   strike_price, step=1):
       maturities = np.arange(min_maturity_price, 
                             max_maturity_price + step, step)
       payoffs = np.vectorize(call_payoff)(maturities, strike_price)
       df = pd.DataFrame({'Strike': strike_price, 'Payoff': payoffs}, 
                         index=maturities)
       df.index.name = 'Maturity Price'
   return df

The following command demonstrates the use of this function to calculate payoff of an underlying security at finishing prices ranging from 10 to 25 and with a strike price of 15:

In [15]:
   call_payoffs(10, 25, 15)
 
Out[15]:
                   Payoff Strike
   Maturity Price              
   10                   0     15
   11                   0     15
   12                   0     15
   13                   0     15
   14                   0     15
   ...               ...     ...
   21                   6     15
   22                  7     15
   23                   8     15
   24                   9     15
   25                 10     15
 
   [16 rows x 2 columns]

Using this result, we can visualize the payoffs using the following function:

In [16]:
   def plot_call_payoffs(min_maturity_price, max_maturity_price, 
                         strike_price, step=1):
       payoffs = call_payoffs(min_maturity_price, max_maturity_price, 
                             strike_price, step)
       plt.ylim(payoffs.Payoff.min() - 10, payoffs.Payoff.max() + 10)
       plt.ylabel("Payoff")
       plt.xlabel("Maturity Price")
       plt.title('Payoff of call option, Strike={0}'
                 .format(strike_price))
       plt.xlim(min_maturity_price, max_maturity_price)
       plt.plot(payoffs.index, payoffs.Payoff.values);

The payoffs are visualized as follows:

In [17]:
   plot_call_payoffs(10, 25, 15)

Mastering pandas for Finance

The put option payoff calculation

The value of a put option can be calculated with the following function:

In [18]:
   def put_payoff(price_at_maturity, strike_price):
       return max(0, strike_price - price_at_maturity)

While the price of the underlying is below the strike price, the value is 0:

In [19]:
   put_payoff(25, 20)
 
Out[19]:
   0

When the price is below the strike price, the value of the option is the difference between the strike price and the price:

In [20]:
   put_payoff(15, 20)
 
Out [20]:
   5

This payoff for a series of prices can be calculated with the following function:

In [21]:
   def put_payoffs(min_maturity_price, max_maturity_price, 
                   strike_price, step=1):
       maturities = np.arange(min_maturity_price, 
                             max_maturity_price + step, step)
       payoffs = np.vectorize(put_payoff)(maturities, strike_price)
      df = pd.DataFrame({'Payoff': payoffs, 'Strike': strike_price},
                         index=maturities)
       df.index.name = 'Maturity Price'
       return df

The following command demonstrates the values of the put payoffs for prices of 10 through 25 with a strike price of 25:

In [22]:
   put_payoffs(10, 25, 15)
 
Out [22]:
                   Payoff Strike
   Maturity Price               
   10                   5     15
   11                   4     15
   12                   3     15
   13                  2     15
   14                   1     15
   ...               ...     ...
   21                   0     15
   22                   0     15
   23                   0     15
   24                   0     15
   25                   0      15
 
   [16 rows x 2 columns]

The following function will generate a graph of payoffs:

In [23]:
   def plot_put_payoffs(min_maturity_price, 
                       max_maturity_price, 
                       strike_price, 
                       step=1):
       payoffs = put_payoffs(min_maturity_price, 
                             max_maturity_price, 
                             strike_price, step)
       plt.ylim(payoffs.Payoff.min() - 10, payoffs.Payoff.max() + 10)
       plt.ylabel("Payoff")
      plt.xlabel("Maturity Price")
       plt.title('Payoff of put option, Strike={0}'
                 .format(strike_price))
       plt.xlim(min_maturity_price, max_maturity_price)
       plt.plot(payoffs.index, payoffs.Payoff.values);

The following command demonstrates the payoffs for prices between 10 and 25 with a strike price of 15:

In [24]:
   plot_put_payoffs(10, 25, 15)

Mastering pandas for Finance

Summary

In this article, we examined several techniques for using pandas to calculate the prices of options, their payoffs, and profit and loss for the various combinations of calls and puts for both buyers and sellers.

Resources for Article:


Further resources on this subject:


LEAVE A REPLY

Please enter your comment!
Please enter your name here