@@ -527,7 +527,7 @@ async def _convert_ib_bar_date_to_unix_nanos(self, bar: BarData, bar_type: BarTy
527
527
"""
528
528
Convert the date from BarData to unix nanoseconds.
529
529
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
531
531
YYYYMMDD format from IB. For all other aggregations, the bar date is returned
532
532
in system time.
533
533
@@ -551,6 +551,42 @@ async def _convert_ib_bar_date_to_unix_nanos(self, bar: BarData, bar_type: BarTy
551
551
552
552
return ts .value
553
553
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
+
554
590
async def _ib_bar_to_ts_init (self , bar : BarData , bar_type : BarType ) -> int :
555
591
"""
556
592
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:
572
608
573
609
"""
574
610
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
576
619
577
620
async def _ib_bar_to_nautilus_bar (
578
621
self ,
@@ -604,7 +647,9 @@ async def _ib_bar_to_nautilus_bar(
604
647
if not instrument :
605
648
raise ValueError (f"No cached instrument for { bar_type .instrument_id } " )
606
649
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
+
608
653
return Bar (
609
654
bar_type = bar_type ,
610
655
open = instrument .make_price (bar .open ),
0 commit comments