pandas-market-calendars 4.6.1__py3-none-any.whl → 5.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. pandas_market_calendars/calendar_utils.py +35 -84
  2. pandas_market_calendars/calendars/asx.py +6 -2
  3. pandas_market_calendars/calendars/bmf.py +4 -8
  4. pandas_market_calendars/calendars/bse.py +6 -2
  5. pandas_market_calendars/calendars/cboe.py +6 -2
  6. pandas_market_calendars/calendars/cme.py +4 -4
  7. pandas_market_calendars/calendars/cme_globex_base.py +2 -2
  8. pandas_market_calendars/calendars/cme_globex_crypto.py +12 -14
  9. pandas_market_calendars/calendars/cme_globex_energy_and_metals.py +4 -4
  10. pandas_market_calendars/calendars/cme_globex_equities.py +2 -2
  11. pandas_market_calendars/calendars/eurex.py +2 -2
  12. pandas_market_calendars/calendars/eurex_fixed_income.py +2 -2
  13. pandas_market_calendars/calendars/hkex.py +7 -5
  14. pandas_market_calendars/calendars/ice.py +2 -2
  15. pandas_market_calendars/calendars/iex.py +7 -3
  16. pandas_market_calendars/calendars/jpx.py +6 -2
  17. pandas_market_calendars/calendars/lse.py +6 -2
  18. pandas_market_calendars/calendars/mirror.py +6 -11
  19. pandas_market_calendars/calendars/nyse.py +41 -42
  20. pandas_market_calendars/calendars/ose.py +7 -5
  21. pandas_market_calendars/calendars/sifma.py +51 -22
  22. pandas_market_calendars/calendars/six.py +6 -2
  23. pandas_market_calendars/calendars/sse.py +6 -2
  24. pandas_market_calendars/calendars/tase.py +6 -2
  25. pandas_market_calendars/calendars/tsx.py +6 -2
  26. pandas_market_calendars/class_registry.py +1 -3
  27. pandas_market_calendars/holidays/sifma.py +31 -31
  28. pandas_market_calendars/market_calendar.py +33 -82
  29. {pandas_market_calendars-4.6.1.dist-info → pandas_market_calendars-5.1.0.dist-info}/METADATA +7 -4
  30. pandas_market_calendars-5.1.0.dist-info/RECORD +50 -0
  31. {pandas_market_calendars-4.6.1.dist-info → pandas_market_calendars-5.1.0.dist-info}/WHEEL +1 -1
  32. pandas_market_calendars-4.6.1.dist-info/RECORD +0 -50
  33. {pandas_market_calendars-4.6.1.dist-info → pandas_market_calendars-5.1.0.dist-info/licenses}/LICENSE +0 -0
  34. {pandas_market_calendars-4.6.1.dist-info → pandas_market_calendars-5.1.0.dist-info/licenses}/NOTICE +0 -0
  35. {pandas_market_calendars-4.6.1.dist-info → pandas_market_calendars-5.1.0.dist-info}/top_level.txt +0 -0
@@ -106,11 +106,15 @@ def mark_session(
106
106
  f"Schedule ends at: {schedule.iloc[-1, -1]}"
107
107
  )
108
108
 
109
+ lte_end = schedule.index <= end.normalize().tz_localize(None)
110
+ gte_start = schedule.index >= start.normalize().tz_localize(None)
111
+
112
+ # Shift both by 1 to keep an extra row on either end if available. Needed in some edge cases.
113
+ gte_start = np.append(gte_start, True)[1:] # Shifts gte_start by one to the left.
114
+ lte_end = np.insert(lte_end, 0, True)[:-1] # Shifts lte_end by one to the right.
115
+
109
116
  # Trim the schedule to match the timeframe covered by the given timeseries
110
- schedule = schedule[
111
- (schedule.index >= start.normalize().tz_localize(None))
112
- & (schedule.index <= end.normalize().tz_localize(None))
113
- ]
117
+ schedule = schedule[gte_start & lte_end]
114
118
 
115
119
  backfilled_map = DEFAULT_LABEL_MAP | label_map
116
120
  mapped_labels = [backfilled_map[label] for label in session_labels]
@@ -151,27 +155,17 @@ def merge_schedules(schedules, how="outer"):
151
155
  all_cols = [x.columns for x in schedules]
152
156
  all_cols = list(itertools.chain(*all_cols))
153
157
  if ("break_start" in all_cols) or ("break_end" in all_cols):
154
- warnings.warn(
155
- "Merge schedules will drop the break_start and break_end from result."
156
- )
158
+ warnings.warn("Merge schedules will drop the break_start and break_end from result.")
157
159
 
158
160
  result = schedules[0]
159
161
  for schedule in schedules[1:]:
160
162
  result = result.merge(schedule, how=how, right_index=True, left_index=True)
161
163
  if how == "outer":
162
- result["market_open"] = result.apply(
163
- lambda x: min(x.market_open_x, x.market_open_y), axis=1
164
- )
165
- result["market_close"] = result.apply(
166
- lambda x: max(x.market_close_x, x.market_close_y), axis=1
167
- )
164
+ result["market_open"] = result.apply(lambda x: min(x.market_open_x, x.market_open_y), axis=1)
165
+ result["market_close"] = result.apply(lambda x: max(x.market_close_x, x.market_close_y), axis=1)
168
166
  elif how == "inner":
169
- result["market_open"] = result.apply(
170
- lambda x: max(x.market_open_x, x.market_open_y), axis=1
171
- )
172
- result["market_close"] = result.apply(
173
- lambda x: min(x.market_close_x, x.market_close_y), axis=1
174
- )
167
+ result["market_open"] = result.apply(lambda x: max(x.market_open_x, x.market_open_y), axis=1)
168
+ result["market_close"] = result.apply(lambda x: min(x.market_close_x, x.market_close_y), axis=1)
175
169
  else:
176
170
  raise ValueError('how argument must be "inner" or "outer"')
177
171
  result = result[["market_open", "market_close"]]
@@ -211,9 +205,7 @@ SESSIONS = Literal[
211
205
  "closed",
212
206
  "closed_masked",
213
207
  ]
214
- MKT_TIMES = Literal[
215
- "pre", "post", "market_open", "market_close", "break_start", "break_end"
216
- ]
208
+ MKT_TIMES = Literal["pre", "post", "market_open", "market_close", "break_start", "break_end"]
217
209
 
218
210
 
219
211
  # region ---- ---- ---- Date Range Warning Types ---- ---- ----
@@ -279,9 +271,7 @@ class InsufficientScheduleWarning(DateRangeWarning):
279
271
 
280
272
  def filter_date_range_warnings(
281
273
  action: Literal["error", "ignore", "always", "default", "once"],
282
- source: Union[
283
- Iterable[type[DateRangeWarning]], type[DateRangeWarning]
284
- ] = DateRangeWarning,
274
+ source: Union[Iterable[type[DateRangeWarning]], type[DateRangeWarning]] = DateRangeWarning,
285
275
  ):
286
276
  """
287
277
  Adjust the behavior of the date_range() warnings to the desired action.
@@ -471,13 +461,9 @@ def date_range(
471
461
  if frequency <= pd.Timedelta("0s"):
472
462
  raise ValueError("Market Calendar Date_Range Frequency must be Positive.")
473
463
  if frequency > pd.Timedelta("1D"):
474
- raise ValueError(
475
- "Market Calendar Date_Range Frequency Cannot Be longer than '1D'."
476
- )
464
+ raise ValueError("Market Calendar Date_Range Frequency Cannot Be longer than '1D'.")
477
465
 
478
- session_list, mask = _make_session_list(
479
- set(schedule.columns), session, merge_adjacent
480
- )
466
+ session_list, mask = _make_session_list(set(schedule.columns), session, merge_adjacent)
481
467
  if len(session_list) == 0:
482
468
  return pd.DatetimeIndex([], dtype="datetime64[ns, UTC]")
483
469
 
@@ -490,9 +476,7 @@ def date_range(
490
476
  dtype = schedule[session_list[0][0]].dtype # copy dtype info from schedule
491
477
  start, end, periods = _standardize_times(schedule, start, end, periods, tz)
492
478
 
493
- time_series = _calc_time_series(
494
- session_times, frequency, closed, force_close, start, end, periods
495
- )
479
+ time_series = _calc_time_series(session_times, frequency, closed, force_close, start, end, periods)
496
480
  time_series.name = None
497
481
 
498
482
  return pd.DatetimeIndex(time_series, tz=tz, dtype=dtype)
@@ -501,9 +485,7 @@ def date_range(
501
485
  # region ------------------ Date Range LTF Subroutines ------------------
502
486
 
503
487
 
504
- def _make_session_list(
505
- columns: set, sessions: Union[str, Iterable], merge_adjacent: bool
506
- ) -> Tuple[list, bool]:
488
+ def _make_session_list(columns: set, sessions: Union[str, Iterable], merge_adjacent: bool) -> Tuple[list, bool]:
507
489
  "Create a list of (Session Start, Session End) Tuples"
508
490
  session_times = []
509
491
  missing_cols = set()
@@ -571,9 +553,7 @@ def _make_session_list(
571
553
  return session_pairs, "closed_masked" in sessions
572
554
 
573
555
 
574
- def _standardize_times(
575
- schedule, start, end, periods, tz
576
- ) -> Tuple[pd.Timestamp, pd.Timestamp, Union[int, None]]:
556
+ def _standardize_times(schedule, start, end, periods, tz) -> Tuple[pd.Timestamp, pd.Timestamp, Union[int, None]]:
577
557
  "Standardize start and end into a timestamp of the relevant timezone"
578
558
  if all((start, end, periods)):
579
559
  periods = None # Ignore Periods if all 3 params are given.
@@ -617,8 +597,7 @@ def _standardize_times(
617
597
 
618
598
  if start is not None and end is not None and start > end:
619
599
  raise ValueError(
620
- "Date_range() given a start-date that occurs after the given end-date. "
621
- f"{start = }, {end = }"
600
+ "Date_range() given a start-date that occurs after the given end-date. " f"{start = }, {end = }"
622
601
  )
623
602
 
624
603
  return start, end, periods
@@ -633,9 +612,7 @@ def _reconfigure_schedule(schedule, session_list, mask_close) -> pd.DataFrame:
633
612
  if not end.endswith("_wrap"):
634
613
  # Simple Session where 'start' occurs before 'end'
635
614
  sessions.append(
636
- schedule[[start, end]]
637
- .rename(columns={start: "start", end: "end"})
638
- .set_index("start", drop=False)
615
+ schedule[[start, end]].rename(columns={start: "start", end: "end"}).set_index("start", drop=False)
639
616
  )
640
617
  continue
641
618
 
@@ -779,9 +756,7 @@ def _course_trim_to_period_count(num_bars, periods, reverse) -> pd.Series:
779
756
  return sessions_to_keep
780
757
 
781
758
 
782
- def _calc_time_series(
783
- session_times, timestep, closed, force_close, start, end, periods
784
- ) -> pd.Series:
759
+ def _calc_time_series(session_times, timestep, closed, force_close, start, end, periods) -> pd.Series:
785
760
  "Interpolate each session into a datetime series at the desired frequency."
786
761
  # region ---- ---- ---- Trim the Sessions ---- ---- ----
787
762
  # Compare 'start' to the session end times so that if 'start' is in the middle of a session
@@ -798,23 +773,17 @@ def _calc_time_series(
798
773
  # Align the start to a multiple of the timestep after the session's beginning.
799
774
  # This is to make the returned DTIndex consistent across all start/end/period settings.
800
775
  session_start = session_times.loc[session_times.index[0], "start"]
801
- start_aligned = session_start + (
802
- ceil((start - session_start) / timestep) * timestep
803
- )
776
+ start_aligned = session_start + (ceil((start - session_start) / timestep) * timestep)
804
777
  session_times.loc[session_times.index[0], "start"] = start_aligned
805
778
  if end is not None and end < session_times.loc[session_times.index[-1], "end"]:
806
779
  session_start = session_times.loc[session_times.index[0], "start"]
807
- end_aligned = session_start + (
808
- floor((end - session_start) / timestep) * timestep
809
- )
780
+ end_aligned = session_start + (floor((end - session_start) / timestep) * timestep)
810
781
  session_times.loc[session_times.index[-1], "end"] = end_aligned
811
782
 
812
783
  num_bars = _num_bars_ltf(session_times, timestep, closed)
813
784
 
814
785
  if periods is not None:
815
- sessions_to_keep = _course_trim_to_period_count(
816
- num_bars, periods, end is not None
817
- )
786
+ sessions_to_keep = _course_trim_to_period_count(num_bars, periods, end is not None)
818
787
  num_bars = num_bars[sessions_to_keep]
819
788
  session_times = session_times[sessions_to_keep]
820
789
 
@@ -853,9 +822,7 @@ def _calc_time_series(
853
822
 
854
823
  PeriodCode = Literal["D", "W", "M", "Q", "Y"]
855
824
  Day_Anchor = Literal["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"]
856
- Month_Anchor = Literal[
857
- "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"
858
- ]
825
+ Month_Anchor = Literal["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"]
859
826
 
860
827
  # These needed because the pandas Period Object is stupid and not consistant w/ date_range.
861
828
  # pd.date_range(s,e, freq = 'W-SUN') == [DatetimeIndex of all sundays] (as Expected)
@@ -970,9 +937,7 @@ def _error_check_htf_range(
970
937
  "Standardize and Error Check Start, End, and period params"
971
938
  if periods is not None:
972
939
  if not isinstance(periods, int):
973
- raise ValueError(
974
- f"Date_Range_HTF Must be either an int or None. Given {type(periods)}"
975
- )
940
+ raise ValueError(f"Date_Range_HTF Must be either an int or None. Given {type(periods)}")
976
941
  if periods < 0:
977
942
  raise ValueError("Date_range_HTF Periods must be Positive.")
978
943
 
@@ -989,9 +954,7 @@ def _error_check_htf_range(
989
954
  if all((start, end, periods)):
990
955
  periods = None # Ignore Periods if passed too many params
991
956
  if len([param for param in (start, end, periods) if param is not None]) < 2:
992
- raise ValueError(
993
- "Date_Range_HTF must be given two of the three following params: (start, end, periods)"
994
- )
957
+ raise ValueError("Date_Range_HTF must be given two of the three following params: (start, end, periods)")
995
958
 
996
959
  if start is not None and end is not None and end < start:
997
960
  raise ValueError("Date_Range_HTF() Start-Date must be before the End-Date")
@@ -999,9 +962,7 @@ def _error_check_htf_range(
999
962
  return start, end, periods
1000
963
 
1001
964
 
1002
- def _standardize_htf_freq(
1003
- frequency: Union[str, pd.Timedelta, int, float]
1004
- ) -> Tuple[int, PeriodCode]:
965
+ def _standardize_htf_freq(frequency: Union[str, pd.Timedelta, int, float]) -> Tuple[int, PeriodCode]:
1005
966
  "Standardize the frequency multiplier and Code, throwing errors as needed."
1006
967
  if isinstance(frequency, str):
1007
968
  if len(frequency) == 0:
@@ -1027,9 +988,7 @@ def _standardize_htf_freq(
1027
988
  if frequency < pd.Timedelta("1D"):
1028
989
  raise ValueError("Date_Range_HTF() Frequency must be '1D' or Higher.")
1029
990
  if frequency % pd.Timedelta("1D") != pd.Timedelta(0):
1030
- raise ValueError(
1031
- "Date_Range_HTF() Week and Day frequency must be an integer multiple of Days"
1032
- )
991
+ raise ValueError("Date_Range_HTF() Week and Day frequency must be an integer multiple of Days")
1033
992
 
1034
993
  return frequency.days, "D"
1035
994
 
@@ -1052,9 +1011,7 @@ def _days_per_week(weekmask: Union[Iterable, str]) -> int:
1052
1011
  return len([day for day in weekmask if bool(day)])
1053
1012
 
1054
1013
 
1055
- def _cal_day_range(
1056
- cb_day: "CustomBusinessDay", start, end, periods, mult
1057
- ) -> pd.DatetimeIndex:
1014
+ def _cal_day_range(cb_day: "CustomBusinessDay", start, end, periods, mult) -> pd.DatetimeIndex:
1058
1015
  """
1059
1016
  Returns a Normalized DateTimeIndex of Open Buisness Days.
1060
1017
  Exactly two of the (start, end, periods) arguments must be given.
@@ -1080,9 +1037,7 @@ def _cal_day_range(
1080
1037
  if isinstance(start, pd.Timestamp) and isinstance(end, pd.Timestamp):
1081
1038
  num_days = (end - start) / mult
1082
1039
  # Get a better estimate of the number of open days since date_range calc is slow
1083
- est_open_days = (
1084
- (num_days // 7) * _days_per_week(cb_day.weekmask)
1085
- ) + num_days % pd.Timedelta("1W")
1040
+ est_open_days = ((num_days // 7) * _days_per_week(cb_day.weekmask)) + num_days % pd.Timedelta("1W")
1086
1041
 
1087
1042
  # Should always produce a small overestimate since Holidays aren't accounted for.
1088
1043
  est_open_days = ceil(est_open_days / pd.Timedelta("1D"))
@@ -1149,9 +1104,7 @@ def _cal_WMQY_range(
1149
1104
  roll_func = cb_day.rollforward
1150
1105
  if start is not None:
1151
1106
  normalized_start = start.to_period(grouping_period).start_time
1152
- _dr_start = (
1153
- normalized_start if start <= roll_func(normalized_start) else start
1154
- )
1107
+ _dr_start = normalized_start if start <= roll_func(normalized_start) else start
1155
1108
 
1156
1109
  if end is not None:
1157
1110
  if periods is not None:
@@ -1181,9 +1134,7 @@ def _cal_WMQY_range(
1181
1134
  normalized_end = end.to_period(grouping_period).end_time.normalize()
1182
1135
  _dr_end = normalized_end if end >= roll_func(normalized_end) else end
1183
1136
 
1184
- _range = (
1185
- pd.date_range(_dr_start, _dr_end, periods, freq).to_series().apply(roll_func)
1186
- )
1137
+ _range = pd.date_range(_dr_start, _dr_end, periods, freq).to_series().apply(roll_func)
1187
1138
 
1188
1139
  # Ensure that Rolled Timestamps are in the desired range When given both Start and End
1189
1140
  if start is not None and end is not None:
@@ -1,7 +1,7 @@
1
1
  from datetime import time
2
2
 
3
3
  from pandas.tseries.holiday import AbstractHolidayCalendar, GoodFriday, EasterMonday
4
- from pytz import timezone
4
+ from zoneinfo import ZoneInfo
5
5
 
6
6
  from pandas_market_calendars.holidays.oz import *
7
7
  from pandas_market_calendars.market_calendar import MarketCalendar
@@ -42,9 +42,13 @@ class ASXExchangeCalendar(MarketCalendar):
42
42
  def name(self):
43
43
  return "ASX"
44
44
 
45
+ @property
46
+ def full_name(self):
47
+ return "Australian Securities Exchange"
48
+
45
49
  @property
46
50
  def tz(self):
47
- return timezone("Australia/Sydney")
51
+ return ZoneInfo("Australia/Sydney")
48
52
 
49
53
  @property
50
54
  def regular_holidays(self):
@@ -23,7 +23,7 @@ from pandas.tseries.holiday import (
23
23
  GoodFriday,
24
24
  Holiday,
25
25
  )
26
- from pytz import timezone
26
+ from zoneinfo import ZoneInfo
27
27
 
28
28
  from pandas_market_calendars.market_calendar import FRIDAY, MarketCalendar
29
29
 
@@ -38,9 +38,7 @@ AniversarioSaoPaulo = Holiday(
38
38
  "Aniversario de Sao Paulo", month=1, day=25, end_date="2021-12-31"
39
39
  )
40
40
  # Carnival Monday
41
- CarnavalSegunda = Holiday(
42
- "Carnaval Segunda", month=1, day=1, offset=[Easter(), Day(-48)]
43
- )
41
+ CarnavalSegunda = Holiday("Carnaval Segunda", month=1, day=1, offset=[Easter(), Day(-48)])
44
42
  # Carnival Tuesday
45
43
  CarnavalTerca = Holiday("Carnaval Terca", month=1, day=1, offset=[Easter(), Day(-47)])
46
44
  # Ash Wednesday (short day)
@@ -62,9 +60,7 @@ DiaTrabalho = Holiday(
62
60
  day=1,
63
61
  )
64
62
  # Constitutionalist Revolution
65
- Constitucionalista = Holiday(
66
- "Constitucionalista", month=7, day=9, start_date="1997-01-01", end_date="2019-12-31"
67
- )
63
+ Constitucionalista = Holiday("Constitucionalista", month=7, day=9, start_date="1997-01-01", end_date="2019-12-31")
68
64
  # Independence Day
69
65
  Independencia = Holiday(
70
66
  "Independencia",
@@ -185,7 +181,7 @@ class BMFExchangeCalendar(MarketCalendar):
185
181
 
186
182
  @property
187
183
  def tz(self):
188
- return timezone("America/Sao_Paulo")
184
+ return ZoneInfo("America/Sao_Paulo")
189
185
 
190
186
  @property
191
187
  def regular_holidays(self):
@@ -5,7 +5,7 @@ Bombay Stock Exchnage
5
5
  from datetime import time
6
6
 
7
7
  from pandas import Timestamp
8
- from pytz import timezone
8
+ from zoneinfo import ZoneInfo
9
9
 
10
10
  from pandas_market_calendars.market_calendar import MarketCalendar
11
11
 
@@ -412,9 +412,13 @@ class BSEExchangeCalendar(MarketCalendar):
412
412
  def name(self):
413
413
  return "BSE"
414
414
 
415
+ @property
416
+ def full_name(self):
417
+ return "Bombay Stock Exchange"
418
+
415
419
  @property
416
420
  def tz(self):
417
- return timezone("Asia/Calcutta")
421
+ return ZoneInfo("Asia/Calcutta")
418
422
 
419
423
  @property
420
424
  def adhoc_holidays(self):
@@ -10,7 +10,7 @@ from pandas.tseries.holiday import (
10
10
  USThanksgivingDay,
11
11
  Holiday,
12
12
  )
13
- from pytz import timezone
13
+ from zoneinfo import ZoneInfo
14
14
 
15
15
  from pandas_market_calendars.holidays.us import (
16
16
  Christmas,
@@ -83,9 +83,13 @@ class CFEExchangeCalendar(MarketCalendar):
83
83
  def name(self):
84
84
  return "CFE"
85
85
 
86
+ @property
87
+ def full_name(self):
88
+ return "CBOE Futures Exchange"
89
+
86
90
  @property
87
91
  def tz(self):
88
- return timezone("America/Chicago")
92
+ return ZoneInfo("America/Chicago")
89
93
 
90
94
  @property
91
95
  def regular_holidays(self):
@@ -24,7 +24,7 @@ from pandas.tseries.holiday import (
24
24
  USPresidentsDay,
25
25
  USThanksgivingDay,
26
26
  )
27
- from pytz import timezone
27
+ from zoneinfo import ZoneInfo
28
28
 
29
29
  from pandas_market_calendars.holidays.us import (
30
30
  Christmas,
@@ -71,7 +71,7 @@ class CMEEquityExchangeCalendar(MarketCalendar):
71
71
 
72
72
  @property
73
73
  def tz(self):
74
- return timezone("America/Chicago")
74
+ return ZoneInfo("America/Chicago")
75
75
 
76
76
  @property
77
77
  def regular_holidays(self):
@@ -141,7 +141,7 @@ class CMEAgricultureExchangeCalendar(MarketCalendar):
141
141
 
142
142
  @property
143
143
  def tz(self):
144
- return timezone("America/Chicago")
144
+ return ZoneInfo("America/Chicago")
145
145
 
146
146
  @property
147
147
  def regular_holidays(self):
@@ -357,7 +357,7 @@ class CMEBondExchangeCalendar(MarketCalendar):
357
357
 
358
358
  @property
359
359
  def tz(self):
360
- return timezone("America/Chicago")
360
+ return ZoneInfo("America/Chicago")
361
361
 
362
362
  @property
363
363
  def regular_holidays(self):
@@ -22,7 +22,7 @@ from pandas.tseries.holiday import (
22
22
  USPresidentsDay,
23
23
  USThanksgivingDay,
24
24
  )
25
- from pytz import timezone
25
+ from zoneinfo import ZoneInfo
26
26
 
27
27
  from pandas_market_calendars.holidays.us import (
28
28
  Christmas,
@@ -79,7 +79,7 @@ class CMEGlobexBaseExchangeCalendar(MarketCalendar, ABC):
79
79
 
80
80
  @property
81
81
  def tz(self):
82
- return timezone("America/Chicago")
82
+ return ZoneInfo("America/Chicago")
83
83
 
84
84
  @property
85
85
  def regular_holidays(self):
@@ -1,6 +1,6 @@
1
1
  import datetime as dt
2
2
 
3
- import pytz
3
+ from zoneinfo import ZoneInfo
4
4
  from pandas.tseries.holiday import AbstractHolidayCalendar
5
5
 
6
6
  from pandas_market_calendars.holidays.cme import (
@@ -45,25 +45,23 @@ class CMEGlobexCryptoExchangeCalendar(CMEGlobexBaseExchangeCalendar):
45
45
  # Tuple[Tuple[first date used, time, offset], ...]
46
46
  # -1 offset indicates that the open is on the previous day
47
47
  # None for first date used marks the start, subsequent market times must have an actual timestamp
48
- "market_open": (
49
- (None, dt.time(17, tzinfo=pytz.timezone("America/Chicago")), -1),
50
- ),
48
+ "market_open": ((None, dt.time(17, tzinfo=ZoneInfo("America/Chicago")), -1),),
51
49
  "market_close": (
52
50
  (
53
51
  None,
54
- dt.time(16, tzinfo=pytz.timezone("America/Chicago")),
52
+ dt.time(16, tzinfo=ZoneInfo("America/Chicago")),
55
53
  ),
56
54
  ),
57
55
  "break_start": (
58
56
  (
59
57
  None,
60
- dt.time(16, tzinfo=pytz.timezone("America/Chicago")),
58
+ dt.time(16, tzinfo=ZoneInfo("America/Chicago")),
61
59
  ),
62
60
  ),
63
61
  "break_end": (
64
62
  (
65
63
  None,
66
- dt.time(17, tzinfo=pytz.timezone("America/Chicago")),
64
+ dt.time(17, tzinfo=ZoneInfo("America/Chicago")),
67
65
  ),
68
66
  ),
69
67
  }
@@ -71,7 +69,7 @@ class CMEGlobexCryptoExchangeCalendar(CMEGlobexBaseExchangeCalendar):
71
69
  @property
72
70
  def tz(self):
73
71
  # Central Time
74
- return pytz.timezone("America/Chicago")
72
+ return ZoneInfo("America/Chicago")
75
73
 
76
74
  @property
77
75
  def name(self):
@@ -100,7 +98,7 @@ class CMEGlobexCryptoExchangeCalendar(CMEGlobexBaseExchangeCalendar):
100
98
  # list[Tuple[time, AbstractHolidayCalendar]]
101
99
  return [
102
100
  (
103
- dt.time(8, 15, tzinfo=pytz.timezone("America/Chicago")),
101
+ dt.time(8, 15, tzinfo=ZoneInfo("America/Chicago")),
104
102
  AbstractHolidayCalendar(
105
103
  rules=[
106
104
  GoodFriday2021,
@@ -108,7 +106,7 @@ class CMEGlobexCryptoExchangeCalendar(CMEGlobexBaseExchangeCalendar):
108
106
  ),
109
107
  ),
110
108
  (
111
- dt.time(10, 15, tzinfo=pytz.timezone("America/Chicago")),
109
+ dt.time(10, 15, tzinfo=ZoneInfo("America/Chicago")),
112
110
  AbstractHolidayCalendar(
113
111
  rules=[
114
112
  GoodFridayAfter2022,
@@ -116,7 +114,7 @@ class CMEGlobexCryptoExchangeCalendar(CMEGlobexBaseExchangeCalendar):
116
114
  ),
117
115
  ),
118
116
  (
119
- dt.time(12, tzinfo=pytz.timezone("America/Chicago")),
117
+ dt.time(12, tzinfo=ZoneInfo("America/Chicago")),
120
118
  AbstractHolidayCalendar(
121
119
  rules=[
122
120
  USMartinLutherKingJrPre2022,
@@ -129,7 +127,7 @@ class CMEGlobexCryptoExchangeCalendar(CMEGlobexBaseExchangeCalendar):
129
127
  ),
130
128
  ),
131
129
  (
132
- dt.time(12, 15, tzinfo=pytz.timezone("America/Chicago")),
130
+ dt.time(12, 15, tzinfo=ZoneInfo("America/Chicago")),
133
131
  AbstractHolidayCalendar(
134
132
  rules=[
135
133
  ChristmasEveInOrAfter1993,
@@ -139,12 +137,12 @@ class CMEGlobexCryptoExchangeCalendar(CMEGlobexBaseExchangeCalendar):
139
137
  ),
140
138
  ),
141
139
  (
142
- dt.time(12, 45, tzinfo=pytz.timezone("America/Chicago")),
140
+ dt.time(12, 45, tzinfo=ZoneInfo("America/Chicago")),
143
141
  AbstractHolidayCalendar(rules=[USThanksgivingFridayFrom2021]),
144
142
  ),
145
143
  # TODO: this market already closes at 1600 normally, do we need these holidays?
146
144
  (
147
- dt.time(16, tzinfo=pytz.timezone("America/Chicago")),
145
+ dt.time(16, tzinfo=ZoneInfo("America/Chicago")),
148
146
  AbstractHolidayCalendar(
149
147
  rules=[
150
148
  USMartinLutherKingJrFrom2022,
@@ -18,7 +18,7 @@ from datetime import time
18
18
  from pandas.tseries.holiday import (
19
19
  AbstractHolidayCalendar,
20
20
  ) # , GoodFriday, USLaborDay, USPresidentsDay, USThanksgivingDay
21
- from pytz import timezone
21
+ from zoneinfo import ZoneInfo
22
22
 
23
23
  from pandas_market_calendars.holidays.cme_globex import (
24
24
  USMartinLutherKingJrFrom2022,
@@ -180,7 +180,7 @@ class CMEGlobexEnergyAndMetalsExchangeCalendar(CMEGlobexBaseExchangeCalendar):
180
180
  def special_closes(self):
181
181
  return [
182
182
  (
183
- time(12, tzinfo=timezone("America/Chicago")),
183
+ time(12, tzinfo=ZoneInfo("America/Chicago")),
184
184
  AbstractHolidayCalendar(
185
185
  rules=[
186
186
  USMartinLutherKingJrPre2022,
@@ -193,7 +193,7 @@ class CMEGlobexEnergyAndMetalsExchangeCalendar(CMEGlobexBaseExchangeCalendar):
193
193
  ),
194
194
  ),
195
195
  (
196
- time(12, 45, tzinfo=timezone("America/Chicago")),
196
+ time(12, 45, tzinfo=ZoneInfo("America/Chicago")),
197
197
  AbstractHolidayCalendar(
198
198
  rules=[
199
199
  FridayAfterThanksgiving,
@@ -201,7 +201,7 @@ class CMEGlobexEnergyAndMetalsExchangeCalendar(CMEGlobexBaseExchangeCalendar):
201
201
  ),
202
202
  ),
203
203
  (
204
- time(13, 30, tzinfo=timezone("America/Chicago")),
204
+ time(13, 30, tzinfo=ZoneInfo("America/Chicago")),
205
205
  AbstractHolidayCalendar(
206
206
  rules=[
207
207
  USMartinLutherKingJrFrom2022,
@@ -1,7 +1,7 @@
1
1
  from datetime import time
2
2
 
3
3
  from pandas.tseries.holiday import AbstractHolidayCalendar
4
- from pytz import timezone
4
+ from zoneinfo import ZoneInfo
5
5
 
6
6
  from pandas_market_calendars.holidays.cme import (
7
7
  USMartinLutherKingJrAfter1998Before2015,
@@ -45,7 +45,7 @@ class CMEGlobexEquitiesExchangeCalendar(CMEGlobexBaseExchangeCalendar):
45
45
 
46
46
  @property
47
47
  def tz(self):
48
- return timezone("America/Chicago")
48
+ return ZoneInfo("America/Chicago")
49
49
 
50
50
  @property
51
51
  def name(self):
@@ -11,7 +11,7 @@ from pandas.tseries.holiday import (
11
11
  Holiday,
12
12
  previous_friday,
13
13
  )
14
- from pytz import timezone
14
+ from zoneinfo import ZoneInfo
15
15
 
16
16
  from pandas_market_calendars.market_calendar import (
17
17
  FRIDAY,
@@ -99,7 +99,7 @@ class EUREXExchangeCalendar(MarketCalendar):
99
99
 
100
100
  @property
101
101
  def tz(self):
102
- return timezone("Europe/Berlin")
102
+ return ZoneInfo("Europe/Berlin")
103
103
 
104
104
  @property
105
105
  def regular_holidays(self):
@@ -6,7 +6,7 @@ from pandas.tseries.holiday import (
6
6
  GoodFriday,
7
7
  Holiday,
8
8
  )
9
- from pytz import timezone
9
+ from zoneinfo import ZoneInfo
10
10
 
11
11
  from pandas_market_calendars.market_calendar import (
12
12
  FRIDAY,
@@ -80,7 +80,7 @@ class EUREXFixedIncomeCalendar(MarketCalendar):
80
80
 
81
81
  @property
82
82
  def tz(self):
83
- return timezone("Europe/Berlin")
83
+ return ZoneInfo("Europe/Berlin")
84
84
 
85
85
  @property
86
86
  def regular_holidays(self):
@@ -10,7 +10,7 @@ from pandas.tseries.holiday import (
10
10
  sunday_to_monday,
11
11
  )
12
12
  from pandas.tseries.offsets import LastWeekOfMonth, WeekOfMonth
13
- from pytz import timezone
13
+ from zoneinfo import ZoneInfo
14
14
 
15
15
  from pandas_market_calendars.holidays.cn import (
16
16
  bsd_mapping,
@@ -198,9 +198,7 @@ MidAutumnFestivalDay = Holiday(
198
198
  name="Mid-autumn Festival", # 中秋节翌日
199
199
  month=9,
200
200
  day=7,
201
- observance=partial(
202
- process_date, mapping=maf_mapping, delta=1, func=sunday_to_monday
203
- ),
201
+ observance=partial(process_date, mapping=maf_mapping, delta=1, func=sunday_to_monday),
204
202
  start_date=Timestamp("2011-01-01"),
205
203
  )
206
204
 
@@ -381,9 +379,13 @@ class HKEXExchangeCalendar(MarketCalendar):
381
379
  def name(self):
382
380
  return "HKEX"
383
381
 
382
+ @property
383
+ def full_name(self):
384
+ return "Hong Kong Stock Exchange"
385
+
384
386
  @property
385
387
  def tz(self):
386
- return timezone("Asia/Shanghai")
388
+ return ZoneInfo("Asia/Shanghai")
387
389
 
388
390
  @property
389
391
  def regular_holidays(self):