Hello,
I have been trying to get options contracts using OptionChainProvider.GetOptionContractList rather than subscribing to option chains via AddOption, as it seems to be much more efficient in backtesting. I was having trouble getting the actual OptionContract, however I think I am most of the way there. I want to share my progress with this community (at Alex's suggestion) so it can be a learning resource for other users trying to accomplish similar tasks.
Here is the code that I call in the OnData() method for each time step to get a 5% OTM call contract for a given security:
def GetCallContract(self, data, symbol):
# Last price of the underlying
px_last = data[symbol].Close
# Get contracts
contracts = self.OptionChainProvider.GetOptionContractList(symbol, data.Time)
# Get the call options
calls = [x for x in contracts if x.ID.OptionRight == 0]
# Choose OTM contracts
otm = [x for x in calls if px_last - x.ID.StrikePrice < 0]
# Get 3-6 month contracts
otm_3_month = [x for x in otm if x.ID.Date >= (self.Time + relativedelta(months=+3))
and x.ID.Date < (self.Time + relativedelta(months=+6))]
# Go 5% OTM
otm_5_to_7 = [x for x in otm_3_month if x.ID.StrikePrice >= px_last * 1.05]
# Sort by strike price then by soonest expiry
sorted_contracts = list(sorted(otm_5_to_7, key = lambda x: (x.ID.StrikePrice, x.ID.Date)))
# Exit if there are no contracts
if len(sorted_contracts) == 0:
return None
# Use AddOptionContract() to subscribe to this contract
self.AddOptionContract(sorted_contracts[0], Resolution.Minute)
return sorted_contracts[0]
I was having difficulty getting things like AskPrice etc. from the Symbol returned from this call, but it turns out I can get the SecurityIdentifier from self.Securities[contract_id]:
contract_id = self.GetCallContract(data, symbol)
if contract_id is None: continue
# Get the contract
contract = self.Securities[contract_id]
With the above lookup by option contract symbol, I can now access things contract.AskPrice, contract.StrikePrice, contract.Expiry, etc.
The only remaining issue is that I often end up with the following error:
Backtest Handled Error: The security with symbol 'SBUX 180420C00062500' is marked as non-tradable.
I have seen in other forum posts (that I cannot seem to locate again) that I need to wait one time-step between subscribing to an option contract with self.AddOptionContract(contract_id) and actually attempting to trade it. If this is the case, how should I go about fixing my pattern? As it stands, I call GetCallContract() in my OnData method, once for each security in my universe. I need to then immediately access the current market pricing of that contract and trade it if necessary.
Thank you, and I hope this discussion will be useful to others in similar situations!