erdewit / ib_insync

Python sync/async framework for Interactive Brokers API
BSD 2-Clause "Simplified" License
2.85k stars 772 forks source link

cancelMktDepth causes errors #665

Closed nealeyoung closed 11 months ago

nealeyoung commented 11 months ago

I'm getting errors calling cancelMktDepth. Looks like it clears domBids and domAsks right away [1], but IB server keeps sending the level2 updates for a while, and when the decoder decodes them it tries to access domBids and domAsks, which have been cleared, causing an error. Below is a small example demonstrating the issue. It looks like there is a second issue as well with "can't find the subscribed market depth..",

BTW my version.py shows __version_info__ = (0, 9, 86), but for some reason the line number in wrapper.py in the error message I get is line 921, but in the source here it looks like line 975 (?).

[1] https://github.com/erdewit/ib_insync/blob/d31241f2fcb16f5a61dc075d6f458721cb95eebd/ib_insync/ib.py#L1352 [2] https://github.com/erdewit/ib_insync/blob/d31241f2fcb16f5a61dc075d6f458721cb95eebd/ib_insync/wrapper.py#L975

-Neal

--- code demonstrating the issue:

import ib_insync as ibs

ib = ibs.IB()

ib.connect()

ib.sleep(1)

contract = ibs.Stock("SPY", "SMART", "USD")
ib.qualifyContracts(contract)

ib.sleep(1)

ticker = ib.reqMktDepth(contract, numRows=50, isSmartDepth=True)

ib.sleep(2)

ib.cancelMktDepth(contract)

ib.sleep(2)

# now we get the following errors:
#
# Error 310, reqId 15049: Can't find the subscribed market depth with tickerId:15049
# Error for updateMktDepthL2:
# Traceback (most recent call last):
#   File "/Users/neal/Desktop/IB/curses_app/ib_insync/decoder.py", line 187, in handler
#     method(*args)
#   File "/Users/neal/Desktop/IB/curses_app/ib_insync/wrapper.py", line 921, in updateMktDepthL2
#     dom[position] = DOMLevel(price, size, marketMaker)
#     ~~~^^^^^^^^^^
# IndexError: list assignment index out of range

ib.disconnect()
nealeyoung commented 11 months ago

p.s. it's conceivable that this error will show up only when the connection to IB has relatively high latency (e.g. 40ms). It's possible that it may occur only because of packets that are sent by IB after the cancel request is sent to IB but before the request is received there (after which IB presumably stops sending update packets).

mattsta commented 11 months ago

I've never seen this happen before, but also note cancelMktDepth() has an extra parameter for isSmartDepth too and it must match the original request.

erdewit commented 11 months ago

Looks like it clears domBids and domAsks right away [1], but IB server keeps sending the level2 updates for a while, and when the decoder decodes them it tries to access domBids and domAsks, which have been cleared, causing an error.

Yes, that is absolutely right. The error is harmless (just some logging) but I'll fix it by moving the clearing of domBids and domAsks from cancelMktDepth to reqMktDepth. They have to be cleared somewhere in case an instrument gets subscribed again (after having been canceled).

nealeyoung commented 11 months ago

Thanks Matt. FYI if I change the cancellation call to

ib.cancelMktDepth(contract, isSmartDepth=True)

I don't get the errors any more.