Requesting Data

Individual Contracts

Introduction

The AddOptionContractadd_option_contract method enables you to add an individual Option contract to your algorithm. To check which contracts are currently available to add to your algorithm, use the OptionChainProvider.GetOptionContractListoption_chain_provider.get_option_contract_list method. If you want to subscribe to a set of contracts instead of individual contracts one-by-one, see Universes.

Create Subscriptions

Before you can subscribe to an Index Option contract, you must configure the underlying Index and get the contract Symbolsymbol.

public class BasicIndexOptionAlgorithm : QCAlgorithm
{
    private Symbol _underlying;
    private Option _contract = null;

    public override void Initialize()
    {
        SetStartDate(2020, 1, 1);
        _underlying = AddIndex("SPX").Symbol;
    }

    public override void OnData(Slice data)
    {
        if (_contract == null)
        {
            var contractSymbols = OptionChainProvider.GetOptionContractList(_underlying, Time);
            var expiry = contractSymbols.Min(symbol => symbol.ID.Date);
            var filteredSymbols = contractSymbols
                .Where(symbol => symbol.ID.Date == expiry && symbol.ID.OptionRight == OptionRight.Call)
                .ToList();
            var symbol = filteredSymbols.OrderBy(symbol => symbol.ID.StrikePrice).First();
            _contract = AddIndexOptionContract(symbol);
        }
    }
}
class BasicIndexOptionAlgorithm(QCAlgorithm):
    def initialize(self):
        self.set_start_date(2020, 1, 1)
        self._underlying = self.add_index("SPX").symbol
        self._contract = None
    
    def on_data(self, data):
        if not self._contract:
            contract_symbols = self.option_chain_provider.get_option_contract_list(self._underlying, self.time)
            expiry = min([symbol.id.date for symbol in contract_symbols])
            filtered_symbols = [symbol for symbol in contract_symbols if symbol.id.date == expiry and symbol.id.option_right == OptionRight.CALL]
            symbol = sorted(filtered_symbols, key=lambda symbol: symbol.id.strike_price)[0]
            self._contract = self.add_index_option_contract(symbol)

Configure the Underlying Index

In most cases, you should subscribe to the underlying Index before you subscribe to an Index Option contract.

_symbol = AddIndex("SPX").Symbol;
self._symbol = self.add_index("SPX").symbol

Get Contract Symbols

To subscribe to an Option contract, you need the contract Symbol. The preferred method to getting Option contract Symbol objects is to use the OptionChainProvideroption_chain_provider. The GetOptionContractListget_option_contract_list method of OptionChainProvideroption_chain_provider returns a list of Symbol objects for a given date and underlying Index, which you can then sort and filter to find the specific contract(s) you want to trade.

// Standard contracts
_canonicalSymbol = QuantConnect.Symbol.CreateCanonicalOption(_symbol, Market.USA, "?SPX");
var contractSymbols = OptionChainProvider.GetOptionContractList(_canonicalSymbol, Time);
var expiry = contractSymbols.Select(symbol => symbol.ID.Date).Min();
var filteredSymbols = contractSymbols.Where(symbol => symbol.ID.Date == expiry && symbol.ID.OptionRight == OptionRight.Call);
_contractSymbol = filteredSymbols.OrderByDescending(symbol => symbol.ID.StrikePrice).Last();

// Weekly contracts
_weeklyCanonicalSymbol = QuantConnect.Symbol.CreateCanonicalOption(_symbol, "SPXW", Market.USA, "?SPXW");
var weeklyContractSymbols = OptionChainProvider.GetOptionContractList(_weeklyCanonicalSymbol, Time)
    .Where(symbol => OptionSymbol.IsWeekly(symbol));
var weeklyExpiry = weeklyContractSymbols.Select(symbol => symbol.ID.Date).Min();
filteredSymbols = contractSymbols.Where(symbol => symbol.ID.Date == weeklyExpiry && symbol.ID.OptionRight == OptionRight.Call);
_weeklyContractSymbol = filteredSymbols.OrderByDescending(symbol => symbol.ID.StrikePrice).Last();
# Standard contracts
self._canonical_symbol = Symbol.create_canonical_option(self._symbol, Market.USA, "?SPX")       
contract_symbols = self.option_chain_provider.get_option_contract_list(self._canonical_symbol, self.time)
expiry = min([symbol.id.date for symbol in contract_symbols])
filtered_symbols = [symbol for symbol in contract_symbols if symbol.id.date == expiry and symbol.id.option_right == OptionRight.CALL]
self._contract_symbol = sorted(filtered_symbols, key=lambda symbol: symbol.id.strike_price)[0]

# Weekly contracts
self._weekly_canonical_symbol = Symbol.create_canonical_option(self._symbol, "SPXW", Market.USA, "?SPXW")
weekly_contract_symbols = self.option_chain_provider.get_option_contract_list(self._weekly_canonical_symbol, self.time)
weekly_contract_symbols = [symbol for symbol in weekly_contract_symbols if OptionSymbol.is_weekly(symbol)]
weekly_expiry = min([symbol.id.date for symbol in weekly_contract_symbols])
weekly_filtered_symbols = [symbol for symbol in weekly_contract_symbols if symbol.id.date == weekly_expiry and symbol.id.option_right == OptionRight.CALL]
self._weekly_contract_symbol = sorted(weekly_filtered_symbols, key=lambda symbol: symbol.id.strike_price)[0]

To filter and select contracts, you can use the following properties of each Symbol object:

PropertyDescription
ID.Dateid.dateThe expiration date of the contract.
ID.StrikePriceid.strike_priceThe strike price of the contract.
ID.OptionRightid.option_right The contract type, OptionRight.PutOptionRight.PUT or OptionRight.CallOptionRight.CALL.
ID.OptionStyleid.option_style The contract style, OptionStyle.AmericanOptionStyle.AMERICAN or OptionStyle.EuropeanOptionStyle.EUROPEAN. We currently only support European-style Options for Index Options.

Subscribe to Contracts

To create an Index Option contract subscription, pass the contract Symbol to the AddIndexOptionContractadd_index_option_contract method. Save a reference to the contract Symbol so you can easily access the contract in the OptionChain that LEAN passes to the OnDataon_data method. To override the default pricing model of the Option, set a pricing model.

var option = AddIndexOptionContract(_contractSymbol);
option.PriceModel = OptionPriceModels.BlackScholes();
option = self.add_index_option_contract(self._contract_symbol)
option.PriceModel = OptionPriceModels.black_scholes()

The AddIndexOptionContractadd_index_option_contract method creates a subscription for a single Index Option contract and adds it to your user-defined universe. To create a dynamic universe of Index Option contracts, add an Index Option universe.

Warm Up Contract Prices

If you subscribe to an Index Option contract with AddIndexOptionContractadd_index_option_contract, you'll need to wait until the next Slice to receive data and trade the contract. To trade the contract in the same time step you subscribe to the contract, set the current price of the contract in a security initializer.

var seeder = new FuncSecuritySeeder(GetLastKnownPrices);
SetSecurityInitializer(new BrokerageModelSecurityInitializer(BrokerageModel, seeder));
seeder = FuncSecuritySeeder(self.get_last_known_prices)
self.set_security_initializer(BrokerageModelSecurityInitializer(self.brokerage_model, seeder))

Supported Assets

To view the supported assets in the US Index Options dataset, see Supported Assets.

Resolutions

The following table shows the available resolutions and data formats for Index Option contract subscriptions:

ResolutionTradeBarQuoteBarTrade TickQuote Tick
TickTICK

SecondSECOND

MinuteMINUTEgreen checkgreen check
HourHOURgreen checkgreen check
DailyDAILYgreen checkgreen check

The default resolution for Index Option subscriptions is Resolution.MinuteResolution.MINUTE. To change the resolution, pass a resolution argument to the AddIndexOptionContractadd_index_option_contract method.

AddIndexOptionContract(_contractSymbol, Resolution.Hour);
self.add_index_option_contract(self._contract_symbol, Resolution.HOUR)

To create custom resolution periods, see Consolidating Data.

Supported Markets

The only market available for Index Options is Market.USA. You don't need to pass a Marketmarket argument to the AddIndexOptionContractadd_index_option_contract method because the contract Symbol already contains the market.

Fill Forward

Fill forward means if there is no data point for the current slice, LEAN uses the previous data point. Fill forward is the default data setting. If you disable fill forward, you may get stale fills or you may see trade volume as zero.

To disable fill forward for a security, set the fillForwardfill_forward argument to false when you create the security subscription.

AddIndexOptionContract(_contractSymbol, fillForward: false);
self.add_index_option_contract(self._contract_symbol, fill_forward=False)

Margin and Leverage

LEAN models buying power and margin calls to ensure your algorithm stays within the margin requirements. Options are already leveraged products, so you can't change their leverage.

Data Normalization

The data normalization mode doesn't affect the data that LEAN passes to OnDataon_data or the data from history request. If you change the data normalization mode, it won't change the outcome.

Remove Subscriptions

To remove a contract subscription that you created with AddIndexOptionContractadd_index_option_contract, call the RemoveOptionContractremove_option_contract method. This method is an alias for RemoveSecurityremove_security.

RemoveOptionContract(_contractSymbol);
self.remove_option_contract(self._contract_symbol)

The RemoveOptionContractremove_option_contract method cancels your open orders for the contract and liquidates your holdings.

Properties

The AddIndexOptionContractadd_index_option_contract method returns an Option object, which have the following properties:

Helper Methods

The Option object provides methods you can use for basic calculations. These methods require the underlying price. To get the Option object and the Security object for its underlying in any function, use the Option Symbolsymbol to access the value in the Securitiessecurities object.

var option = Securities[_contractSymbol];
var underlying = Securities[_contractSymbol.Underlying];
var underlyingPrice = underlying.Price;
option = self.securities[self._contract_symbol]
underlying = self.securities[self._contract_symbol.underlying]
underlying_price = underlying.price

To get the Option payoff, call the GetPayOffget_pay_off method.

var payOff = option.GetPayOff(underlyingPrice);
pay_off = option.get_pay_off(underlying_price)

To get the Option intrinsic value, call the GetIntrinsicValueget_intrinsic_value method.

var intrinsicValue = option.GetIntrinsicValue(underlyingPrice);
intrinsic_value = option.get_intrinsic_value(underlying_price)

To get the Option out-of-the-money amount, call the OutOfTheMoneyAmountout_of_the_money_amount method.

var otmAmount = option.OutOfTheMoneyAmount(underlyingPrice);
otm_amount = option.out_of_the_money_amount(underlying_price)

To check whether the Option can be automatic exercised, call the IsAutoExercisedis_auto_exercised method.

var isAutoExercised = option.IsAutoExercised(underlyingPrice);
is_auto_exercised = option.is_auto_exercised(underlying_price)

Exceptions and Edge Cases

The following sections explain exceptions and edge cases with subscribing to individual Option contracts.

Default Underlying Subscription Settings

If you subscribe to an Index Option contract but don't have a subscription to the underlying Index, LEAN automatically subscribes to the underlying Index and sets its fill forward property to match that of the Index Option contract. In this case, you still need the Index Symbolsymbol to subscribe to Index Option contracts. If you don't have access to it, create it.

_symbol = QuantConnect.Symbol.Create("SPX", SecurityType.Index, Market.USA);
self._symbol = Symbol.create("SPX", SecurityType.INDEX, Market.USA)

Manually Creating Option Symbol Objects

To subscribe to an Option contract, you need the contract Symbol. To get Index Option contract Symbol objects, call the CreateOptioncreate_option method or use the OptionChainProvideroption_chain_provider. If you use the CreateOptioncreate_option method, you need to know the specific contract details.

// Standard contracts
_contractSymbol = QuantConnect.Symbol.CreateOption(_symbol, Market.USA,
    OptionStyle.European, OptionRight.Call, 3650, new DateTime(2022, 6, 17));

// Weekly contracts
_weeklyContractSymbol = QuantConnect.Symbol.CreateOption(_symbol, "SPXW", Market.USA,
    OptionStyle.European, OptionRight.Call, 3650, new DateTime(2022, 6, 17));
# Standard contracts
self._contract_symbol = Symbol.create_option(self._symbol, Market.USA,
    OptionStyle.EUROPEAN, OptionRight.CALL, 3650, datetime(2022, 6, 17))

# Weekly contracts
self._weekly_contract_symbol = Symbol.create_option(self._symbol, "SPXW", Market.USA,
    OptionStyle.EUROPEAN, OptionRight.CALL, 3650, datetime(2022, 6, 17))

Overriding the Initial Implied Volatility Guess

To override the initial guess of implied volatility, set and warm up the underlying volatility model.

Example

The following example shows how to update the Option chain every five minutes. The OptionChainManager class implements the selection logic and manages the contract subscriptions.

namespace QuantConnect.Algorithm.CSharp
{
    public class OptionChainProviderFullExample : QCAlgorithm
    {
        private Dictionary<Symbol, OptionChainManager> _chainManager = new();
        public override void Initialize()
        {
            SetStartDate(2023, 1, 2);
            SetEndDate(2023, 1, 30);
            SetCash(100000);
            UniverseSettings.Asynchronous = true;
            UniverseSettings.MinimumTimeInUniverse = TimeSpan.Zero;
            SetSecurityInitializer(new BrokerageModelSecurityInitializer(BrokerageModel, new FuncSecuritySeeder(GetLastKnownPrices)));
            var spx = AddIndex("SPX").Symbol;
            _chainManager[QuantConnect.Symbol.CreateCanonicalOption(spx, "SPXW", Market.USA, "?SPXW")] = new(-10, 10, 0, 1);
            PopulateOptionChain();
            Schedule.On(DateRules.EveryDay(spx), TimeRules.AfterMarketOpen(spx, 1), PopulateOptionChain);
            Schedule.On(DateRules.EveryDay(spx), TimeRules.Every(TimeSpan.FromMinutes(5)), Filter);
        }
        
        private void PopulateOptionChain()
        {
            // The contract list is updated daily, so we can get it and apply
            // the expiration filter as soon as the market open
            foreach (var (symbol, manager) in _chainManager)
            {
                manager.SetChain(OptionChainProvider.GetOptionContractList(symbol, Time), Time);
            }
    
            Filter();
        }
        
        private void Filter()
        {
            foreach (var (symbol, manager) in _chainManager)
            {
                manager.Select(this, symbol);
            }
        }
        
        public override void OnData(Slice slice)
        {
            foreach (var (symbol, manager) in _chainManager)
            {
                if (!slice.OptionChains.TryGetValue(symbol, out var chain))
                    continue;
                var expiry = chain.Min(x => x.Expiry);
                var atmCall = chain
                    .Where(x => x.Expiry == expiry && x.Right == OptionRight.Call && Securities[x.Symbol].IsTradable)
                    .OrderBy(x => Math.Abs(chain.Underlying.Price - x.Strike))
                    .FirstOrDefault();

                if (atmCall != null && !Portfolio[atmCall.Symbol].Invested)
                    MarketOrder(atmCall.Symbol, 1);
            }
        }
    }

    internal class OptionChainManager
    {
        private readonly int _minStrike;
        private readonly int _maxStrike;
        private readonly int _minExpiry;
        private readonly int _maxExpiry;
        private List<Symbol> _chain = new();
        private readonly List<Symbol> _symbols = new();

        public OptionChainManager(int minStrike, int maxStrike, int minExpiry, int maxExpiry)
        {
            _minStrike = minStrike;
            _maxStrike = maxStrike;
            _minExpiry = minExpiry;
            _maxExpiry = maxExpiry;
        }

        public void SetChain(IEnumerable<Symbol> symbols, DateTime time)
        {
            _chain = symbols.Where(x =>
            {
                var totalDays = (x.ID.Date - time).TotalDays;
                return _minExpiry <= totalDays && totalDays <= _maxExpiry;
            }).ToList();
        }
        
        public void Select(QCAlgorithm algorithm, Symbol underlyingSymbol)
        {
            if (_chain.IsNullOrEmpty())
                return;
            if (underlyingSymbol.IsCanonical())
                underlyingSymbol = underlyingSymbol.Underlying;

            var strikes = _chain.Select(x => x.ID.StrikePrice).OrderBy(x => x).Distinct().ToList();
            var spot = algorithm.Securities[underlyingSymbol].Price;
            var atm = strikes.OrderBy(x => Math.Abs(spot - x)).FirstOrDefault();
            var index = strikes.IndexOf(atm);
            var minStrike = strikes[Math.Max(0, index + _minStrike)];
            var maxStrike = strikes[Math.Min(strikes.Count - 1, index + _maxStrike)];
            var symbols = _chain.Where(x => minStrike <= x.ID.StrikePrice && x.ID.StrikePrice <= maxStrike).ToList();

            var toRemove = _symbols.Except(symbols).ToList();
            foreach (var symbol in toRemove)
            {
                if (algorithm.RemoveOptionContract(symbol))
                    _symbols.Remove(symbol);
            }
            var toAdd = symbols.Except(_symbols).ToList();
            foreach (var symbol in toAdd)
            {
                _symbols.Add(symbol);
                algorithm.AddOptionContract(symbol);
            }
        }
    }
}
class OptionChainProviderFullExample(QCAlgorithm):

    def initialize(self):
        self.set_start_date(2023, 1, 2)
        self.set_end_date(2023, 1, 30)
        self.set_cash(100000)
        self.universe_settings.asynchronous = True
        self.universe_settings.minimum_time_in_universe = timedelta(minutes=0)
        self.set_security_initializer(BrokerageModelSecurityInitializer(self.brokerage_model, FuncSecuritySeeder(self.get_last_known_prices)))
        spx = self.add_index("SPX").symbol
        self._chain_manager = {
            Symbol.create_canonical_option(spx, "SPXW", Market.USA, "?SPXW"): OptionChainManager(-10, 10, 0, 1)
        }
        self._populate_option_chain()
        self.schedule.on(self.date_rules.every_day(spx), self.time_rules.after_market_open(spx, 1), self._populate_option_chain)
        self.schedule.on(self.date_rules.every_day(spx), self.time_rules.every(timedelta(minutes=5)), self._filter)

    def _populate_option_chain(self):
        # The contract list is updated daily, so we can get it and apply
        # the expiration filter as soon as the market open
        for symbol, manager in self._chain_manager.items():
            manager.set_chain(self.option_chain_provider.get_option_contract_list(symbol, self.time), self.time)
        self.filter()

    def _filter(self):
        for symbol, manager in self._chain_manager.items():
            manager.select(self, symbol)

    def on_data(self, slice: Slice) -> None:
        for symbol, _ in self._chain_manager.items():
            chain = slice.option_chains.get(symbol)
            if not chain: continue
            if self.portfolio[symbol.underlying].invested:
                self.liquidate(symbol.underlying)

            expiry = min([x.expiry for x in chain])
            contracts = [x for x in chain if x.expiry == expiry and x.right == OptionRight.CALL and self.securities[x.symbol].is_tradable]
            if not contracts: continue
            atm_call = sorted(contracts, key=lambda x: abs(chain.underlying.price-x.strike))[0]

            if not self.portfolio[atm_call.symbol].invested:
                self.market_order(atm_call.symbol, 1)

class OptionChainManager:
    _chain = []
    _symbols = []
        
    def __init__(self, min_strike, max_strike, min_expiry, max_expiry):
        self._min_strike = min_strike
        self._max_strike = max_strike
        self._min_expiry = min_expiry
        self._max_expiry = max_expiry
        
    def set_chain(self, symbols: List[Symbol], time: datetime) -> None:
        self.chain = [x for x in symbols if self._min_expiry <= (x.id.date - time).days <= self._max_expiry]
        
    def select(self, algorithm: QCAlgorithm, symbol: Symbol) -> None:
        if not self._chain:
            return
        if symbol.is_canonical():
            symbol = symbol.underlying
        strikes = sorted(set(x.id.strike_price for x in self._chain))
        spot = algorithm.securities[symbol].price
        atm = sorted(strikes, key=lambda x: abs(spot-x))[0]
        index = strikes.index(atm)
        min_strike = strikes[max(0, index + self._min_strike)]
        max_strike = strikes[min(len(strikes) - 1, index + self._max_strike)]
        symbols = set(x for x in self.chain if min_strike <= x.id.strike_price <= max_strike)
        to_remove = set(self._symbols).difference(symbols)
        for symbol in to_remove:
            if algorithm.remove_option_contract(symbol):
                self._symbols.remove(symbol)
        to_add = symbols.difference(self._symbols)
        for symbol in to_add:
            self._symbols.append(symbol)
            algorithm.add_option_contract(symbol)

You can also see our Videos. You can also get in touch with us via Discord.

Did you find this page helpful?

Contribute to the documentation: