Introduction
QuantConnect provides US options trade and quotes price data for approximately 4000 symbols, each of which has roughly 10 strikes on average. Data is available starting January 1st, 2008. In this tutorial, we will discuss how to use QuantConnect to start your options trading algorithm.
Add Options
Before trading options, you need to add options for a given underlying equity and set the resolution in step Initialize
with AddOption
method. The commonly used parameters will be explained in the method table. Please refer to the link below for details of each method.
Method | Parameters |
---|---|
AddOption(underlying, resolution, fillDataForward) | underlying(string): The underlying equity symbol resolution: Tick, Second, Minute, Hour, or Daily. Default is minute fillDataForward(bool): If true, returns the last available data even if none in that time slice. The default value is true. |
def Initialize(self):
self.SetStartDate(2017, 1, 1) # Set Start Date
self.SetEndDate(2017, 6, 30) # Set End Date
self.SetCash(50000) # Set Strategy Cash
equity = self.AddEquity("GOOG") # Add the underlying stock: Google
option = self.AddOption("GOOG") # Add the option corresponding to underlying stock
self.symbol = option.Symbol
The return value of AddOption
method is an option security object, please refer to the link for detailed properties and methods of option class.
Filter Contracts
After adding the options for specific underlying stock, you can set the filter criteria with SetFilter method to pull contracts using the specified min and max strike and expiration range values you need for a given symbol.
Method | Parameters |
---|---|
SetFilter(minStrike,maxStrike,minExpiry,maxExpiry) | minStrike, maxStrike: The minimum and maximum strike rank relative to market price min Expiry, max Expiry: The range of time to expiration to include, for example, 10 would exclude contracts expiring in less than 10 days |
Here parameters minStrike
and maxStrike
are the relative multipliers with respect to the market price. We use Google(NASDAQ: GOOG) as an example to describe the filter criteria. If today is 01/03/2017, the market price of underlying stock is $776, the strike prices of GOOG options are spaced $2.5. Then SetFilter(-1, +2, 0, 90)
will first look up at the money contracts with strike being K=$777.5 (Here K is 777.5 the next closest multiple to the underlying price of 775 since rarely will an option be ATM exactly). Then SetFilter will look for options with strikes between and including (777.5 - 2.5 * 1, 777.5 + 2.5 * 2). The time to expiration of these options are restricted within 90 days from now on.
For the strike, the exchange normally chooses the strike prices at which options can be written so that they are spaced $2.50, $5, or $10 apart. Typically the spacing is $2.50 when the stock price is between $5 and $25, $5 when the stock price is between $25 and $200, and $10 for stock prices above $200. So you should carefully choose the parameters of minStrike
and maxStrike
in case there are no contracts that satisfy the filter criteria if the range is too small, less than the minimum units of strike prices change.
For the expiry, there are many expiration dates that apply to the different series of options. An option cycle is the pattern of months in which options contracts expire. There are three kinds of common option cycles. The options on the January cycle have contracts available in the first month of each quarter (January, April, July and October). Options assigned to the February cycle use the middle month of each quarter (February, May, August and November). And options in the March cycle have options available during the last month of each quarter (March, June, September and December). In addition, individual stock options typically expire in the current month and the subsequent month.
# filter the contracts with strikes between (market price - 10, market price + 10)
option.SetFilter(-10,10)
# filter the contracts which expires more than 30 days but no longer than 60 days
option.SetFilter(timedelta(30), timedelta(60))
# filter the contracts with strikes between(ATM Strike - 10 * strike space value, market price + 10 * strike space value) and with expiration days less than 180 days
option.SetFilter(-10, +10, 0, 180)
Select Contracts
For QuantConnect API, Slice class provides a data structure for all of an algorithm's data at a single time step. So you need use property Slice.OptionChains
to request options data for this slice.
OptionChains is a collection of OptionChain keyed by the option's underlying symbol. The elements in Slice.OptionChains
have properties Key
(the underlying symbol object) Value
(the option chain).
OptionChain represents an entire chain of option contracts for a single underlying security. In other words, It is a list of option contracts.
OptionContract defines a single option contract at a specific expiration and strike price. For any contract x in option chain, you can use the following statements to check different options properties.
Properties of Option Contract | Description |
---|---|
x.Symbol.Value |
String representation of option contract's symbol |
x.AskPrice, x.BidPrice |
Ask and bid prices |
x.Expiry |
Expiration date |
x.Strike |
Strike price |
x.ImpliedVolatility |
Implied volatility |
x.Greeks |
Greeks letter |
x.Right |
Right being purchased x.Right = OptionRight.Call (0) [right to buy] x.Right = OptionRight.Put (1) [right to sell] |
x.UnderlyingLastPrice |
Last price the underlying security traded at |
x.UnderlyingSymbol |
Underlying security's symbol |
We can print out the details of the contract after filtering with Python data frame to show these properties. Assume today is 01/03/2017. The stock price at 01/03/2017 09:31:00 is $776.01 per share. Here we use SetFilter(-1, +1, 0, 60)
to filter the contracts.
def OnData(self, slice: Slice) -> None:
chain = slice.OptionChains.get(self.symbol)
if chain:
self.Log(f"underlying price: {chain.Underlying.Price}")
df = pd.DataFrame([[x.Right,x.Strike,x.Expiry,x.BidPrice,x.AskPrice] for x in chain],
index=[x.Symbol.Value for x in chain],
columns=['type(call 0, put 1)', 'strike', 'expiry', 'ask price', 'bid price'])
self.Log(str(df))
Symbol | type(call 0, put 1) | Strike | Expiry | Ask Price | Bid Price |
---|---|---|---|---|---|
GOOG 170217C00780000 | 0 | 780.0 | 2017-02-17 | 26.4 | 27.9 |
GOOG 170120P00782500 | 1 | 782.5 | 2017-01-20 | 14.7 | 16.3 |
GOOG 170120C00782500 | 0 | 782.5 | 2017-01-20 | 9.4 | 10.2 |
GOOG 170120P00780000 | 1 | 780.0 | 2017-01-20 | 13.4 | 15.0 |
GOOG 170120C00780000 | 0 | 780.0 | 2017-01-20 | 10.6 | 11.5 |
GOOG 170217P00780000 | 1 | 780.0 | 2017-02-17 | 28.9 | 30.8 |
GOOG 170120P00777500 | 1 | 777.5 | 2017-01-20 | 12.2 | 13.7 |
GOOG 170120C00777500 | 0 | 777.5 | 2017-01-20 | 11.8 | 12.9 |
Here we give an example of how to find ATM, ITM OTM contracts for trading. First, we need to extract the OptionChain from OptionChains according to symbols we added in Initialize step. Secondly, we extract ATM, ITM and OTM contracts by using UnderlyingLastPrice and Strike properties. Note here the strikes of ATM options are not exactly the same as the market price of underlying stocks, thus here we first sort the contracts by the absolute values of the difference between the UnderlyingLastPrice and the Strike. Then we choose the contract with the minimum absolute value as the ATM contract.
chain = slice.OptionChains.get(self.symbol)
if chain:
# differentiate the call and put options
call = [x for x in chain if x.Right == OptionRight.Call]
put = [x for x in chain if x.Right == OptionRight.Put]
# choose ITM call contracts
contracts = [x for x in call if x.UnderlyingLastPrice - x.Strike > 0]
# or choose ATM contracts
contracts = sorted(chain, key = lambda x: abs(x.UnderlyingLastPrice - x.Strike))[0]
# or choose OTM call contracts
contracts = [x for x in call if x.UnderlyingLastPrice - x.Strike < 0]
# sort the contracts by their expiration dates
contracts = sorted(contracts, key = lambda x: x.Expiry, reverse = True)
Finally, we trade the options by using the contract's symbol.
if len(contracts) == 0: continue
# trade the contracts with the farthest expiration
symbol = contracts[0].Symbol
self.MarketOrder(symbol, 1)
self.MarketOnCloseOrder(symbol, -1)
Summary
After mastering the basic knowledge of options market, this tutorial we take a close at how to use Quantconnect to customize your own options trading. For example, how you can access an option chain, how to view the details of the contract as a Python data frame, and the most important how to trade the specific option contract.
Next chapter we will examine some important topics of options like the payoff, Put-Call parity, and the synthetic positions. By learning all those concepts, we will start some brief hedging strategies involving options.
Algorithm
This simple example demonstrates how you can inspect the option chain to pick a specific option contract to trade.