Skip to content

Commit

Permalink
Accommodate dependency updates
Browse files Browse the repository at this point in the history
Fixes `FutureWarning` raised when indexing a `pandas.Series` directly
with an `int` rather than via `Series.iloc`.

Surpresses new hypothesis HealthCheck concerned with multiple executors.
  • Loading branch information
maread99 committed Sep 5, 2023
1 parent ee41859 commit 85f7ed7
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 51 deletions.
12 changes: 6 additions & 6 deletions exchange_calendars/exchange_calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -926,12 +926,12 @@ def last_session(self) -> pd.Timestamp:
@property
def first_session_open(self) -> pd.Timestamp:
"""Open time of calendar's first session."""
return self.opens[0]
return self.opens.iloc[0]

@property
def last_session_close(self) -> pd.Timestamp:
"""Close time of calendar's last session."""
return self.closes[-1]
return self.closes.iloc[-1]

@property
def first_minute(self) -> pd.Timestamp:
Expand Down Expand Up @@ -1522,7 +1522,7 @@ def next_open(self, minute: Minute, _parse: bool = True) -> pd.Timestamp:
try:
idx = next_divider_idx(self.opens_nanos, minute.value)
except IndexError:
if minute >= self.opens[-1]:
if minute >= self.opens.iloc[-1]:
raise ValueError(
"Minute cannot be the last open or later (received `minute`"
f" parsed as '{minute}'.)"
Expand Down Expand Up @@ -1553,7 +1553,7 @@ def next_close(self, minute: Minute, _parse: bool = True) -> pd.Timestamp:
try:
idx = next_divider_idx(self.closes_nanos, minute.value)
except IndexError:
if minute == self.closes[-1]:
if minute == self.closes.iloc[-1]:
raise ValueError(
"Minute cannot be the last close (received `minute` parsed as"
f" '{minute}'.)"
Expand Down Expand Up @@ -1583,7 +1583,7 @@ def previous_open(self, minute: Minute, _parse: bool = True) -> pd.Timestamp:
try:
idx = previous_divider_idx(self.opens_nanos, minute.value)
except ValueError:
if minute == self.opens[0]:
if minute == self.opens.iloc[0]:
raise ValueError(
"Minute cannot be the first open (received `minute` parsed as"
f" '{minute}'.)"
Expand Down Expand Up @@ -1614,7 +1614,7 @@ def previous_close(self, minute: Minute, _parse: bool = True) -> pd.Timestamp:
try:
idx = previous_divider_idx(self.closes_nanos, minute.value)
except ValueError:
if minute <= self.closes[0]:
if minute <= self.closes.iloc[0]:
raise ValueError(
"Minute cannot be the first close or earlier (received"
f" `minute` parsed as '{minute}'.)"
Expand Down
4 changes: 2 additions & 2 deletions exchange_calendars/exchange_calendar_xbud.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def bridge_mon(dt: datetime.datetime) -> datetime.datetime | None:
"""Define Monday as holiday if Tuesday is a holiday.
If a holiday falls on a Tuesday an extra holiday is observed on Monday
to create a three day weekend.
to bridge the weekend and the official holiday.
"""
return dt - ONE_DAY if dt.weekday() == TUESDAY else None

Expand All @@ -49,7 +49,7 @@ def bridge_fri(dt: datetime.datetime) -> datetime.datetime | None:
"""Define Friday as holiday if Thrusday is a holiday.
If a holiday falls on a Thursday an extra holiday is observed on Friday
to create a three day weekend.
to bridge the weekend and the official holiday.
"""
return dt + ONE_DAY if dt.weekday() == THURSDAY else None

Expand Down
4 changes: 2 additions & 2 deletions exchange_calendars/exchange_calendar_xtai.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def bridge_mon(dt: datetime.datetime) -> datetime.datetime | None:
Notes
-----
If a holiday falls on a Tuesday an extra holiday is observed on Monday
to create a three day weekend.
to bridge the weekend and the official holiday.
"""
dt -= ONE_DAY
return dt if (dt.weekday() == MONDAY and dt.year > 2013) else None
Expand All @@ -135,7 +135,7 @@ def bridge_fri(dt: datetime.datetime) -> datetime.datetime | None:
Notes
-----
If a holiday falls on a Thursday an extra holiday is observed on Friday
to create a three day weekend.
to bridge the weekend and the official holiday.
"""
dt += ONE_DAY
return dt if (dt.weekday() == FRIDAY and dt.year > 2013) else None
Expand Down
12 changes: 6 additions & 6 deletions tests/test_calendar_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import pandas as pd
import pytest
import pytz
from hypothesis import assume, given, settings
from hypothesis import assume, given, settings, HealthCheck
from hypothesis import strategies as st
from pandas.testing import assert_index_equal

Expand Down Expand Up @@ -640,7 +640,7 @@ def bounds(start: pd.Series, end: pd.Series, force: bool, align: pd.Timedelta):
if curtail and not (force_close and force_break_close):
indices = lower_bounds.argsort()
lower_bounds = lower_bounds.sort_values()
upper_bounds = upper_bounds[indices]
upper_bounds = upper_bounds.iloc[indices]
curtail_mask = upper_bounds > lower_bounds.shift(-1)
if curtail_mask.any():
upper_bounds[curtail_mask] = lower_bounds.shift(-1)[curtail_mask]
Expand All @@ -656,7 +656,7 @@ def bounds(start: pd.Series, end: pd.Series, force: bool, align: pd.Timedelta):
align=st_align(),
align_pm=st_align(),
)
@settings(deadline=None)
@settings(deadline=None, suppress_health_check=[HealthCheck.differing_executors])
def test_indices_fuzz(
self,
data,
Expand Down Expand Up @@ -779,7 +779,7 @@ def test_indices_fuzz(
align=st_align(),
align_pm=st_align(),
)
@settings(deadline=None)
@settings(deadline=None, suppress_health_check=[HealthCheck.differing_executors])
def test_intervals_fuzz(
self,
data,
Expand Down Expand Up @@ -919,7 +919,7 @@ def test_for_empty_with_neither_fuzz(
force_break_close=st.booleans(),
curtail_overlaps=st.booleans(),
)
@settings(deadline=None)
@settings(deadline=None, suppress_health_check=[HealthCheck.differing_executors])
def test_daily_fuzz(
self,
data,
Expand Down Expand Up @@ -953,7 +953,7 @@ def test_daily_fuzz(

@pytest.mark.parametrize("name", ["XHKG", "24/7", "CMES"])
@given(data=st.data(), closed=st.sampled_from(["right", "both"]))
@settings(deadline=None)
@settings(deadline=None, suppress_health_check=[HealthCheck.differing_executors])
def test_overlap_error_fuzz(self, data, name, calendars, answers, closed, one_min):
"""Fuzz for expected IndicesOverlapError.
Expand Down
96 changes: 61 additions & 35 deletions tests/test_exchange_calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -468,12 +468,12 @@ def sessions_range(self) -> tuple[pd.Timestamp, pd.Timestamp]:
@property
def first_session_open(self) -> pd.Timestamp:
"""Open time of first session covered by answers."""
return self.opens[0]
return self.opens.iloc[0]

@property
def last_session_close(self) -> pd.Timestamp:
"""Close time of last session covered by answers."""
return self.closes[-1]
return self.closes.iloc[-1]

@property
def first_minute(self) -> pd.Timestamp:
Expand Down Expand Up @@ -737,7 +737,7 @@ def _get_sessions_with_times_different_to_next_session(
if is_break_col:
if column_.isna().all():
return [pd.DatetimeIndex([])] * 2
column_ = column_.fillna(method="ffill").fillna(method="bfill")
column_ = column_.ffill().bfill()

diff = (column_.shift(-1) - column_)[:-1]
remainder = diff % pd.Timedelta(hours=24)
Expand Down Expand Up @@ -1493,25 +1493,31 @@ def prev_next_open_close_minutes(
"""
close_is_next_open_bv = self.closes == self.opens.shift(-1)
open_was_prev_close_bv = self.opens == self.closes.shift(+1)
close_is_next_open = close_is_next_open_bv[0]
close_is_next_open = close_is_next_open_bv.iloc[0]

# minutes for session 0
minute = self.opens[0]
yield (minute, (None, None, self.opens[1], self.closes[0]))
minute = self.opens.iloc[0]
yield (minute, (None, None, self.opens.iloc[1], self.closes.iloc[0]))

minute = minute + self.ONE_MIN
yield (minute, (self.opens[0], None, self.opens[1], self.closes[0]))
yield (
minute,
(self.opens.iloc[0], None, self.opens.iloc[1], self.closes.iloc[0]),
)

minute = self.closes[0]
next_open = self.opens[2] if close_is_next_open else self.opens[1]
yield (minute, (self.opens[0], None, next_open, self.closes[1]))
minute = self.closes.iloc[0]
next_open = self.opens.iloc[2] if close_is_next_open else self.opens.iloc[1]
yield (minute, (self.opens.iloc[0], None, next_open, self.closes.iloc[1]))

minute += self.ONE_MIN
prev_open = self.opens[1] if close_is_next_open else self.opens[0]
yield (minute, (prev_open, self.closes[0], next_open, self.closes[1]))
prev_open = self.opens.iloc[1] if close_is_next_open else self.opens.iloc[0]
yield (minute, (prev_open, self.closes.iloc[0], next_open, self.closes.iloc[1]))

minute = self.closes[0] - self.ONE_MIN
yield (minute, (self.opens[0], None, self.opens[1], self.closes[0]))
minute = self.closes.iloc[0] - self.ONE_MIN
yield (
minute,
(self.opens.iloc[0], None, self.opens.iloc[1], self.closes.iloc[0]),
)

# minutes for sessions over [1:-1] except for -1 close and 'close + one_min'
opens = self.opens[1:-1]
Expand All @@ -1528,7 +1534,7 @@ def prev_next_open_close_minutes(
pd.Series(pd.Timestamp("2200-01-01", tz=UTC)),
]
)
stop = closes[-1]
stop = closes.iloc[-1]

for (
open_,
Expand Down Expand Up @@ -1568,31 +1574,51 @@ def prev_next_open_close_minutes(
yield (close + self.ONE_MIN, (open_, close, next_open_, next_close))

# close and 'close + one_min' for session -2
minute = self.closes[-2]
next_open = None if close_is_next_open_bv[-2] else self.opens[-1]
yield (minute, (self.opens[-2], self.closes[-3], next_open, self.closes[-1]))
minute = self.closes.iloc[-2]
next_open = None if close_is_next_open_bv.iloc[-2] else self.opens.iloc[-1]
yield (
minute,
(
self.opens.iloc[-2],
self.closes.iloc[-3],
next_open,
self.closes.iloc[-1],
),
)

minute += self.ONE_MIN
prev_open = self.opens[-1] if close_is_next_open_bv[-2] else self.opens[-2]
yield (minute, (prev_open, self.closes[-2], next_open, self.closes[-1]))
prev_open = (
self.opens.iloc[-1]
if close_is_next_open_bv.iloc[-2]
else self.opens.iloc[-2]
)
yield (
minute,
(prev_open, self.closes.iloc[-2], next_open, self.closes.iloc[-1]),
)

# minutes for session -1
if not open_was_prev_close_bv[-1]:
open_ = self.opens[-1]
prev_open = self.opens[-2]
prev_close = self.closes[-2]
if not open_was_prev_close_bv.iloc[-1]:
open_ = self.opens.iloc[-1]
prev_open = self.opens.iloc[-2]
prev_close = self.closes.iloc[-2]
next_open = None
close = self.closes[-1]
close = self.closes.iloc[-1]
yield (open_, (prev_open, prev_close, next_open, close))
yield (open_ - self.ONE_MIN, (prev_open, prev_close, open_, close))
yield (open_ + self.ONE_MIN, (open_, prev_close, next_open, close))

minute = self.closes[-1]
next_open = self.opens[2] if close_is_next_open_bv[-1] else self.opens[1]
yield (minute, (self.opens[-1], self.closes[-2], None, None))
minute = self.closes.iloc[-1]
next_open = (
self.opens.iloc[2] if close_is_next_open_bv.iloc[-1] else self.opens.iloc[1]
)
yield (minute, (self.opens.iloc[-1], self.closes.iloc[-2], None, None))

minute -= self.ONE_MIN
yield (minute, (self.opens[-1], self.closes[-2], None, self.closes[-1]))
yield (
minute,
(self.opens.iloc[-1], self.closes.iloc[-2], None, self.closes.iloc[-1]),
)

# dunder

Expand Down Expand Up @@ -3054,12 +3080,12 @@ def test_prev_next_minute(self, all_calendars_with_answers, one_minute):
f_prev = no_parsing(cal.previous_minute)

# minutes of first session
first_min = ans.first_minutes[0]
first_min_plus_one = ans.first_minutes_plus_one[0]
first_min_less_one = ans.first_minutes_less_one[0]
last_min = ans.last_minutes[0]
last_min_plus_one = ans.last_minutes_plus_one[0]
last_min_less_one = ans.last_minutes_less_one[0]
first_min = ans.first_minutes.iloc[0]
first_min_plus_one = ans.first_minutes_plus_one.iloc[0]
first_min_less_one = ans.first_minutes_less_one.iloc[0]
last_min = ans.last_minutes.iloc[0]
last_min_plus_one = ans.last_minutes_plus_one.iloc[0]
last_min_less_one = ans.last_minutes_less_one.iloc[0]

match = "Requested minute would fall before the calendar's first trading minute"
with pytest.raises(errors.RequestedMinuteOutOfBounds, match=match):
Expand Down

0 comments on commit 85f7ed7

Please sign in to comment.