Closed dmoklaf closed 1 year ago
qualifyContracts is a higher-level convenience method that sits atop of reqContractDetails. The latter can be used to get a list of matching contract details, which also includes the fully qualified contracts.
That's exactly what I thought initially. But it doesn't work because of step (c) above: qualifyContracts also performs fixes in the data fields of the contract, fixes which have to be duplicated (and thus unmaintained) if operating separately:
An alternative solution would be to refactor these fixes into a util package function that "fixes" a Contract object received from the IB server. The API user would be free to call it if he wants to process raw contract objects.
OK, so it's strictly about the contract 'fixup' part. Which is this:
c = detailsList[0].contract
expiry = c.lastTradeDateOrContractMonth
if expiry:
# remove time and timezone part as it will cause problems
expiry = expiry.split()[0]
c.lastTradeDateOrContractMonth = expiry
if contract.exchange == 'SMART':
# overwriting 'SMART' exchange can create invalid contract
c.exchange = contract.exchange
util.dataclassUpdate(contract, c)
The expiry fixup has become part of the Decoder and can be removed here (will do that later). The exchange fixup doesn't apply here as one does want all the different exchanges returned.
If IB sends contracts that they then reject when sent back as-is, then that is not something that ib_insync can generally fix.
It answers perfectly my point. Indeed the fixup generated all this and is now not an issue. Thanks for the explanations. Created a follow-up tracking issue for the move to the decoder (to allow tracking by my development environment as I have currently some workaround code there)
qualifyContractsAsync works in 4 steps: (a) Retrieve qualified contracts from IB database based on a contract pattern argument (b) Ensure that there is only one qualified contract per pattern (c) Fix some faulty data fields of the qualified contract (d) Copy its data into the initial argument's fields
However, there are legitimate cases where (b) may be problematic. There could be several matches for a given contract pattern, that only some posterior client logic can eliminate. E.g., when looking for a US stock by ISIN and currency, without knowing its primary exchange, 2 contracts are returned in some cases. E.g., MSFT (ISIN US594918104) has 2 contracts in USD in the database:
The solution could be to: (1) Create an additional method searchContractsAsync that take as argument a single contract pattern and return a list of candidate contracts - that would be steps (a) and (c) above (2) Factor out from qualifyContractsAsync into a separate private method called by both public methods the contract data repair logic (c) (https://github.com/erdewit/ib_insync/blob/7337c9a3dd93b3b0eb9b129f77956401aad70a05/ib_insync/ib.py#L1860)
I can submit a PR if this is useful