Skip to content

Commit 1af722b

Browse files
authored
Fix bar timestamps for IB (#2380)
1 parent 8fe0640 commit 1af722b

File tree

1 file changed

+48
-3
lines changed

1 file changed

+48
-3
lines changed

nautilus_trader/adapters/interactive_brokers/client/market_data.py

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,7 @@ async def _convert_ib_bar_date_to_unix_nanos(self, bar: BarData, bar_type: BarTy
527527
"""
528528
Convert the date from BarData to unix nanoseconds.
529529
530-
If the bar type's aggregation is 14, the bar date is always returned in the
530+
If the bar type's aggregation is 14 - 16, the bar date is always returned in the
531531
YYYYMMDD format from IB. For all other aggregations, the bar date is returned
532532
in system time.
533533
@@ -551,6 +551,42 @@ async def _convert_ib_bar_date_to_unix_nanos(self, bar: BarData, bar_type: BarTy
551551

552552
return ts.value
553553

554+
async def _ib_bar_to_ts_event(self, bar: BarData, bar_type: BarType) -> int:
555+
"""
556+
Calculate the ts_event timestamp for a bar.
557+
558+
This method computes the timestamp at which data event occurred, by adjusting
559+
the provided bar's timestamp based on the bar type's duration. ts_event is set
560+
to the start of the bar period.
561+
562+
Week/Month bars's date returned from IB represents ending date,
563+
the start of bar period should be start of the week and month respectively
564+
565+
Parameters
566+
----------
567+
bar : BarData
568+
The bar data to be used for the calculation.
569+
bar_type : BarType
570+
The type of the bar, which includes information about the bar's duration.
571+
572+
Returns
573+
-------
574+
int
575+
576+
"""
577+
ts_event = 0
578+
if bar_type.spec.aggregation in [15, 16]:
579+
date_obj = pd.to_datetime(bar.date, format="%Y%m%d", utc=True)
580+
if bar_type.spec.aggregation == 15:
581+
first_day_of_week = date_obj - pd.Timedelta(days=date_obj.weekday())
582+
ts_event = first_day_of_week.value
583+
else:
584+
first_day_of_month = date_obj.replace(day=1)
585+
ts_event = first_day_of_month.value
586+
else:
587+
ts_event = await self._convert_ib_bar_date_to_unix_nanos(bar, bar_type)
588+
return ts_event
589+
554590
async def _ib_bar_to_ts_init(self, bar: BarData, bar_type: BarType) -> int:
555591
"""
556592
Calculate the initialization timestamp for a bar.
@@ -572,7 +608,14 @@ async def _ib_bar_to_ts_init(self, bar: BarData, bar_type: BarType) -> int:
572608
573609
"""
574610
ts = await self._convert_ib_bar_date_to_unix_nanos(bar, bar_type)
575-
return ts + pd.Timedelta(bar_type.spec.timedelta).value
611+
if bar_type.spec.aggregation in [15, 16]:
612+
# Week/Month bars's date represents ending date
613+
return ts
614+
elif bar_type.spec.aggregation == 14:
615+
# -1 to make day's bar ts_event and ts_init on the same day
616+
return ts + pd.Timedelta(bar_type.spec.timedelta).value - 1
617+
else:
618+
return ts + pd.Timedelta(bar_type.spec.timedelta).value
576619

577620
async def _ib_bar_to_nautilus_bar(
578621
self,
@@ -604,7 +647,9 @@ async def _ib_bar_to_nautilus_bar(
604647
if not instrument:
605648
raise ValueError(f"No cached instrument for {bar_type.instrument_id}")
606649

607-
ts_event = await self._convert_ib_bar_date_to_unix_nanos(bar, bar_type)
650+
ts_event = await self._ib_bar_to_ts_event(bar, bar_type)
651+
# used to be _convert_ib_bar_date_to_unix_nanos
652+
608653
return Bar(
609654
bar_type=bar_type,
610655
open=instrument.make_price(bar.open),

0 commit comments

Comments
 (0)