erdewit / ib_insync

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

decoder.execDetails mishandling timezone ? #671

Closed nealeyoung closed 6 months ago

nealeyoung commented 6 months ago

Times that ib_insync reports for my trade executions seem to be off by 5 hours, which happens to be the difference between my timezone and UTC.

My TWS (Active Trader Pro) and ib_insync are running on the same machine, with US/Eastern timezone. I have left the ib_insync IB timezone empty, which according to the documentation should cause ib_insync to assume TWS is running with the local timezone (as it is).

I believe the code that sets the execution time is in decoder.py, starting at line L452 (see [1]):

       time = cast(datetime, parseIBDatetime(timeStr))
        if not time.tzinfo:
            tz = self.wrapper.ib.TimezoneTWS
            if tz:
                time = time.replace(tzinfo=ZoneInfo(str(tz)))
        ex.time = time.astimezone(timezone.utc)

I printed out the values being used there. Here they are:

time=datetime.datetime(2023, 12, 14, 20, 57, 37)
time.tzinfo=None
tz=''
timeStr='20231214-20:57:37'
ex.time=datetime.datetime(2023, 12, 15, 1, 57, 37, tzinfo=datetime.timezone.utc)

The trade in question executed today at the end of US/Eastern trading hours, at 15:57:37 Eastern = 20:57:37 UTC.

It looks to me like the timeStr (presumably from TWS?) is already in UTC. Then cast(..) produces a time with no timezone info. Then time.astimezone assumes (incorrectly) that that time is in local time, and adds 5 hours to try to put it into UTC time.

Documentation for time.astimezone says: "Return a datetime object with new tzinfo tz, adjusting the date and time data so the result is the same UTC time as self, but in tz’s local time." This does not specify the behavior when the given object has no timezone. A quick experiment shows that, at least on my system, it does indeed interpret the time without timezone as local time.

>>> time = datetime.now()
>>> time
datetime.datetime(2023, 12, 14, 21, 4, 23, 507139)
>>> time.tzinfo is None
True
>>> time.astimezone(timezone.utc)
datetime.datetime(2023, 12, 15, 2, 4, 23, 507139, tzinfo=datetime.timezone.utc)

[1] https://github.com/erdewit/ib_insync/blob/90670606931e145bbced000f3bc460a7792a6d21/ib_insync/decoder.py#L452

erdewit commented 6 months ago

Could you please see what this TWS setting is set to:

https://ibkrcampus.com/ibkr-api-page/trader-workstation-api/#modify-return-date

image

nealeyoung commented 6 months ago

It's set to "UTC format".

erdewit commented 6 months ago

It's set to "UTC format".

Okay that explains it then. Your options are:

  1. Set it to "operator timezone" (default). OR (exclusive or)
  2. Set IB.TimezoneTWS to 'UTC'.
nealeyoung commented 6 months ago

Thank you. I set it to "operator timezone" and it seems to be working.