From 433db3a14015a2765f64b48bb9ece50c09c1ec16 Mon Sep 17 00:00:00 2001 From: goodboy Date: Tue, 3 Mar 2026 16:19:59 -0500 Subject: [PATCH] Handle ambiguous futes contracts in `get_fute()` Use (the only available in `ib_async`) `returnAll=True` in `qualifyContractsAsync()` calls within `get_fute()` and handle the case where IB returns a list of ambiguous contract matches instead of a single result. Deats, - add `returnAll=True` to both `ContFuture` and `Future` qualification calls. - add `isinstance(con, list)` check after unpacking first result to detect ambiguous contract sets. - log warning with input params and matched contracts when ambiguous. - update return type annot to `Contract|list[Contract]`. Also, - handle list-of-contracts case in `find_contracts()` by unpacking `*contracts` into the `qualifyContractsAsync()` call. - reformat `qualifyContractsAsync()` calls to multiline style. (this commit msg was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code --- piker/brokers/ib/api.py | 56 ++++++++++++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/piker/brokers/ib/api.py b/piker/brokers/ib/api.py index b359ab06..b467ee0e 100644 --- a/piker/brokers/ib/api.py +++ b/piker/brokers/ib/api.py @@ -768,26 +768,48 @@ class Client: expiry: str = '', front: bool = False, - ) -> Contract: + ) -> Contract|list[Contract]: ''' Get an unqualifed contract for the current "continous" future. + When input params result in a so called "ambiguous contract" + situation, we return the list of all matches provided by, + + `IB.qualifyContractsAsync(..., returnAll=True)` + ''' # it's the "front" contract returned here if front: - con = (await self.ib.qualifyContractsAsync( - ContFuture(symbol, exchange=exchange) - ))[0] - else: - cons = (await self.ib.qualifyContractsAsync( - Future( - symbol, - exchange=exchange, - lastTradeDateOrContractMonth=expiry, + cons = ( + await self.ib.qualifyContractsAsync( + ContFuture(symbol, exchange=exchange), + returnAll=True, ) - )) - con = cons[0] + ) + else: + cons = ( + await self.ib.qualifyContractsAsync( + Future( + symbol, + exchange=exchange, + lastTradeDateOrContractMonth=expiry, + ), + returnAll=True, + ) + ) + + con = cons[0] + if isinstance(con, list): + log.warning( + f'{len(con)!r} futes cons matched for input params,\n' + f'symbol={symbol!r}\n' + f'exchange={exchange!r}\n' + f'expiry={expiry!r}\n' + f'\n' + f'cons:\n' + f'{con!r}\n' + ) return con @@ -912,11 +934,17 @@ class Client: ) exch = 'SMART' if not exch else exch - contracts: list[Contract] = [con] + if isinstance(con, list): + contracts: list[Contract] = con + else: + contracts: list[Contract] = [con] + if qualify: try: contracts: list[Contract] = ( - await self.ib.qualifyContractsAsync(con) + await self.ib.qualifyContractsAsync( + *contracts + ) ) except RequestError as err: msg = err.message