Correctly Identifying Futures Options in Interactive Brokers API Requests
Summary
The document describes an Interactive Brokers API issue in which reported call option bids, asks, and deltas appeared inconsistent with the quoted price of an S&P futures contract. The initial request used a regular equity index option contract, even though the intended instruments were options on futures. The mismatch in contract definitions made the returned option data appear incorrect; the questioner suspected delayed market data but the response identifies the contract type as the problem.
The correction is to request futures options tied to the underlying futures symbol, with the relevant expiration, strike, exchange, multiplier, currency, and trading class. The author reports that the data then worked as expected. This is a practical reminder to verify instrument identity and contract fields when API values conflict with expectations. The document does not investigate subscription entitlements or give a general diagnosis of delayed data, so its finding is specific to the described contract mismatch.
Key ideas
- The requested option contract must match the intended underlying instrument.
- Options on futures should be represented as futures options rather than regular index options.
- Exchange, multiplier, currency, expiration, and trading class help identify the correct contract.
- A contract-definition error can make valid API responses appear to be incorrect or delayed market data.
Tags
Full text
# Getting incorrect options data with IB API. Missing real time market data subscription?
# Getting incorrect options data with IB API. Missing real time market data subscription?
I'm having a problem getting options data with IB's API. The data seems not to be correct. In my code I'm getting some 0DTE call options for the Mini SP500 March Futures contract and printing their bid, ask and delta. For this I'm using the ib_sync which simplifies development greatly. This is the code:
```
from datetime import datetime
from ib_insync import *
import pandas as pd
import math
from datetime import datetime, time
def week_of_month(dt):
""" Returns the week of the month for the specified date.
"""
first_day = dt.replace(day=1)
dom = dt.day
adjusted_dom = dom + first_day.weekday()
return int(math.ceil(adjusted_dom/7.0))
def get_trading_class(dt: datetime):
week = week_of_month(dt)
day = dt.weekday()
res = 'E' + str(week)
if day == 4:
return 'EW' + str(week)
else:
return 'E' + str(week) + chr(60+day)
ib = IB().connect('winhost', 7496, clientId=123, timeout=15)
es = Future('ES', '202303', 'CME')
print(ib.qualifyContracts(es))
ib.reqMarketDataType(1)
[ticker] = ib.reqTickers(es)
# spx current price
spxValue = ticker.marketPrice()
print('es futue value: ', spxValue)
chains = ib.reqSecDefOptParams(es.symbol, 'CME', es.secType, es.conId)
# chainsDf = util.df(chains)
# print(chainsDf.to_string(max_colwidth=10))
# chainsDf = chainsDf[(chains.exchange == 'SMART') & (chains.tradingClass == 'SPXW')]
print('week of the month:', week_of_month(datetime.now()))
trading_cls = get_trading_class(datetime.now())
print('trading class:', trading_cls)
chain = next(c for c in chains if c.tradingClass == trading_cls and c.exchange == 'CME')
expiration = chain.expirations[0]
print('expiration:',expiration)
call_strikes = [strike for strike in chain.strikes
if strike % 5 == 0
and spxValue - 10 < strike < spxValue + 30]
put_strikes = [strike for strike in chain.strikes
if strike % 5 == 0
and spxValue - 30 < strike < spxValue]
rights = ['P', 'C']
contracts = [Option('SPX', expiration, strike, 'C', 'SMART', tradingClass='SPXW') for strike in call_strikes]
contracts = ib.qualifyContracts(*contracts)
tickers = ib.reqTickers(*contracts)
for ticker in tickers:
print('call strike', ticker.contract.strike, 'bid:', ticker.bid, 'ask:', ticker.ask, 'delta:', ticker.lastGreeks.delta)
ib.disconnect()
```
And this is the output:
```
[Future(conId=495512572, symbol='ES', lastTradeDateOrContractMonth='20230317', multiplier='50', exchange='CME', currency='USD', localSymbol='ESH3', tradingClass='ES')]
es futue value: 4061.75
week of the month: 3
trading class: EW3
expiration: 20230217
call strike 4055.0 bid: 10.1 ask: 10.3 delta: 0.472330335007325
call strike 4060.0 bid: 7.9 ask: 8.1 delta: 0.3769870767527141
call strike 4065.0 bid: 6.1 ask: 6.3 delta: 0.29022751622672416
call strike 4070.0 bid: 4.6 ask: 4.8 delta: 0.212861073049887
call strike 4075.0 bid: 3.4 ask: 3.6 delta: 0.1491749774504159
call strike 4080.0 bid: 2.55 ask: 2.6 delta: 0.09797399088652259
call strike 4085.0 bid: 1.85 ask: 1.95 delta: 0.06485900831271653
call strike 4090.0 bid: 1.35 ask: 1.4 delta: 0.04255032736417017
```
I've double checked the price of the contract, and it's correct: 4061.75.
Now, looking at the TWS at the time I make this request, the bid asks and deltas of the options are incorrect. Look at the attached image.
I think I'm getting delayed data, because if the price of the futures contract is 4061.75, the first ITM call option with a delta over 50 should be the 4060, but in my output, the delta for that option is 0.37. Not even the 4055 call is ITM.
So my question is. How is it possible that I'm seeing real time data on the TWS but I'm getting delayed data through the API? Am I missing a Real Time data subscription for the API? In that case, which one?
These are my Market Data Subscriptions:
Thanks
## Answer by rugobal (score 2)
https://quant.stackexchange.com/a/74714
My mistake. The definition of the Option contract was wrong. So after changing the line:
```
contracts = [Option('SPX', expiration, strike, 'C', 'SMART', tradingClass='SPXW') for strike in call_strikes]
```
by:
```
contracts = [FuturesOption(es.symbol, expiration, strike, 'C', 'CME', '50', 'USD', tradingClass=trading_cls) for strike in call_strikes]
```
It worked as expectedShown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.