Skip to content

Interactive Brokers option chain request fails for non-US markets #2622

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
4 tasks done
surya opened this issue May 11, 2025 · 0 comments
Open
4 tasks done

Interactive Brokers option chain request fails for non-US markets #2622

surya opened this issue May 11, 2025 · 0 comments
Labels
bug Something isn't working help wanted Extra attention is needed

Comments

@surya
Copy link

surya commented May 11, 2025

Bug Report

Interactive Brokers option chain request fails for instruments from non-US markets.

Confirmation

Before opening a bug report, please confirm:

  • I’ve re-read the relevant sections of the documentation.
  • I’ve searched existing issues and discussions to avoid duplicates.
  • I’ve reviewed or skimmed the source code (or examples) to confirm the behavior isn’t by design.
  • I’ve confirmed the issue is reproducible with the latest version of nautilus_trader.

Expected Behavior

IBKR adapter API fetches all the instruments in the specified option chain.

Actual Behavior

IBKR adapter API fails with an error. The issue seems to be with the line exchange=exchange or "SMART" in the get_option_chain_details_by_expiry method. This fails to build option chains for non-US markets as exchange is not passed in the calling methods, and SMART is not a valid value. Please see #1704 where it's implemented.

async def get_option_chain_details_by_expiry(
        self,
        underlying: IBContract,
        last_trading_date: str,
        exchange: str | None = None,
    ) -> list[ContractDetails]:
        [option_details] = (
            await self._client.get_contract_details(
                IBContract(
                    secType=("FOP" if underlying.secType == "FUT" else "OPT"),
                    symbol=underlying.symbol,
                    lastTradeDateOrContractMonth=last_trading_date,
                    exchange=exchange or "SMART",
                ),
            ),
        )
        option_details = [d for d in option_details if d.underConId == underlying.conId]
        self._log.info(
            f"Received {len(option_details)} Option Contracts for "
            f"{underlying.symbol}.{underlying.primaryExchange or underlying.exchange} expiring on {last_trading_date}",
        )
        self._log.debug(f"Got {option_details=}")
        return option_details

Steps to Reproduce the Problem

  1. Run the code provided below.

Code Snippets or Logs

import asyncio

from nautilus_trader.adapters.interactive_brokers.common import IBContract
from nautilus_trader.adapters.interactive_brokers.config import (
    InteractiveBrokersInstrumentProviderConfig,
)
from nautilus_trader.adapters.interactive_brokers.config import SymbologyMethod
from nautilus_trader.adapters.interactive_brokers.historical import (
    HistoricInteractiveBrokersClient,
)


async def main() -> None:
    host: str = "192.168.1.13"
    port: int = 7497

    client = HistoricInteractiveBrokersClient(host=host, port=port, log_level="INFO")
    await client.connect()
    await asyncio.sleep(1)

    contracts = [
        IBContract(
            secType="IND",
            exchange="NSE",
            symbol="NIFTY50",
            lastTradeDateOrContractMonth="20250515",
            build_options_chain=True,
        )
    ]

    instrument_provider_config = InteractiveBrokersInstrumentProviderConfig(
        symbology_method=SymbologyMethod.IB_RAW,
        load_contracts=frozenset(contracts),
    )
    instruments = await client.request_instruments(
        instrument_provider_config=instrument_provider_config
    )
    print(instruments)


if __name__ == "__main__":
    asyncio.run(main())

This results in the following error:

2025-05-11T11:21:38.897092227Z [INFO] TRADER-000.InteractiveBrokersInstrumentProvider: Attempting to find instrument for contract=IBContract(secType='IND', exchange='NSE', symbol='NIFTY50', lastTradeDateOrContractMonth='20250515', build_options_chain=True)
2025-05-11T11:21:39.234546353Z [INFO] TRADER-000.InteractiveBrokersInstrumentProvider: Contract qualified for NIFTY50.NSE with ConId=51497778
Traceback (most recent call last):
  File "/nautilus/src/utils/test.py", line 42, in <module>
    asyncio.run(main())
    ~~~~~~~~~~~^^^^^^^^
  File "/home/surya/.local/share/uv/python/cpython-3.13.1-linux-x86_64-gnu/lib/python3.13/asyncio/runners.py", line 194, in run
    return runner.run(main)
           ~~~~~~~~~~^^^^^^
  File "/home/surya/.local/share/uv/python/cpython-3.13.1-linux-x86_64-gnu/lib/python3.13/asyncio/runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
  File "/home/surya/.local/share/uv/python/cpython-3.13.1-linux-x86_64-gnu/lib/python3.13/asyncio/base_events.py", line 720, in run_until_complete
    return future.result()
           ~~~~~~~~~~~~~^^
  File "/nautilus/src/utils/test.py", line 35, in main
    instruments = await client.request_instruments(
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        instrument_provider_config=instrument_provider_config
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/nautilus/.venv/lib/python3.13/site-packages/nautilus_trader/adapters/interactive_brokers/historical/client.py", line 134, in request_instruments
    await provider.load_all_async()
  File "/nautilus/.venv/lib/python3.13/site-packages/nautilus_trader/adapters/interactive_brokers/providers.py", line 89, in load_all_async
    await self.load_ids_async([])
  File "/nautilus/.venv/lib/python3.13/site-packages/nautilus_trader/adapters/interactive_brokers/providers.py", line 109, in load_ids_async
    await self.load_async(contract)
  File "/nautilus/.venv/lib/python3.13/site-packages/nautilus_trader/adapters/interactive_brokers/providers.py", line 301, in load_async
    contract_details = await self.get_contract_details(contract)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/nautilus/.venv/lib/python3.13/site-packages/nautilus_trader/adapters/interactive_brokers/providers.py", line 164, in get_contract_details
    option_contracts_detail = await self.get_option_chain_details_by_expiry(
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ...<5 lines>...
    )
    ^
  File "/nautilus/.venv/lib/python3.13/site-packages/nautilus_trader/adapters/interactive_brokers/providers.py", line 240, in get_option_chain_details_by_expiry
    option_details = [d for d in option_details if d.underConId == underlying.conId]
                                 ^^^^^^^^^^^^^^
TypeError: 'NoneType' object is not iterable

Specifications

  • OS platform: Ubuntu 24.04
  • Python version: 3.13.1
  • nautilus_trader version: 1.217.0
@surya surya added the bug Something isn't working label May 11, 2025
@cjdsellers cjdsellers added the help wanted Extra attention is needed label May 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

2 participants