Skip to content

Commit

Permalink
Request live prices for delayed daily data
Browse files Browse the repository at this point in the history
Previously would not re-request live indices for daily data with
a delay > one day. This PR corrects `data.Data` to evaluate start
date so as to account for any delay, thereby including 'live
indices' in subsequent request.

Also fixes flaky test
`test_daterange_duration_cal_end_minute_oolb`
  • Loading branch information
maread99 committed Nov 8, 2024
1 parent d9778d9 commit 19f1290
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 7 deletions.
7 changes: 6 additions & 1 deletion src/market_prices/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,12 @@ def _requested_dates_adjust(self, dr: DateRangeReq) -> DateRangeReq | None:
"""
end = dr[1]
if self.bi.is_one_day and end == helpers.now(self.bi):
end -= helpers.ONE_DAY
if self._delay is None:
margin = 1
else:
delay_in_days = self._delay.total_seconds() / (24 * 60 * 60)
margin = max(1, math.ceil(delay_in_days))
end -= helpers.ONE_DAY * margin
elif self.bi.is_intraday:
assert self._delay is not None
# ten minutes to cover provider delays in publishing data.
Expand Down
3 changes: 1 addition & 2 deletions src/market_prices/prices/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1226,7 +1226,6 @@ def _set_pdata(self):
d = {}
max_delay = self.max_delay
for bi in self.bis:
delay = max_delay if bi.is_intraday else None
ll = self.base_limits[bi]
if bi.is_daily:
if ll is not None:
Expand All @@ -1240,7 +1239,7 @@ def _set_pdata(self):
request=self._request_data,
cc=self.cc,
bi=bi,
delay=delay,
delay=max_delay,
left_limit=ll,
right_limit=self.base_limits_right[bi],
source_live=self.SOURCE_LIVE,
Expand Down
46 changes: 46 additions & 0 deletions tests/test_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -1253,3 +1253,49 @@ def test_raises_PricesUnavailableFromSourceError(
session_open = xlon.session_open(session)
with pytest.raises(errors.PricesUnavailableFromSourceError):
data.get_table((session_open, session_open + pd.Timedelta(30, "min")))


def test_delay_daily(xlon_calendar_extended, today, one_day, monkeypatch):
cal = xlon_calendar_extended
cc = calutils.CompositeCalendar([cal])
bi = TDInterval.D1

end = helpers.to_tz_naive(cal.date_to_session(today, "previous"))
start = helpers.to_tz_naive(cal.session_offset(end, -7))
dr = start, end

now = cal.session_open(end) + pd.Timedelta(minutes=90)
monkeypatch.setattr(
"pandas.Timestamp.now", lambda *_, tz=None, **__: now.tz_convert(tz)
)

delay = pd.Timedelta(minutes=7200)

admin = MockRequestAdmin(cal)
request_data = admin.mock_request_data()
data = m.Data(request_data, cc, bi, delay, source_live=True)
table = data.get_table(dr)
assert (table.index[0], table.index[-1]) == (start, end)
assert admin.last_request == (start, end)
# repeat call, should still be requesting most recent 5 days of data
table = data.get_table(dr)
assert admin.last_request == (end - (one_day * 5), end)

admin.reset()
data = m.Data(request_data, cc, bi, delay, source_live=False)
table = data.get_table(dr)
assert (table.index[0], table.index[-1]) == (start, end)
assert admin.last_request == (start, end)
# repeat call, should now not be requesting live dates
table = data.get_table(dr)
assert admin.last_request == (end, end)

delay = None
admin.reset()
data = m.Data(request_data, cc, bi, delay, source_live=True)
table = data.get_table(dr)
assert (table.index[0], table.index[-1]) == (start, end)
assert admin.last_request == (start, end)
# repeat call, as no delay should assume one day live index
table = data.get_table(dr)
assert admin.last_request == (end - one_day, end)
23 changes: 19 additions & 4 deletions tests/test_daterange.py
Original file line number Diff line number Diff line change
Expand Up @@ -2655,7 +2655,7 @@ def test_daterange_duration_cal_end_minute_oolb(

start = end - pd.DateOffset(weeks=2)
idx = ans.first_minutes.searchsorted(start)
start_evaluted = ans.first_minutes.iloc[idx]
start_evaluated = ans.first_minutes.iloc[idx]
drg = self.get_drg(cal, pp, interval=bi, limit=limit, strict=True)
if not limit_idx:
error_msg = re.escape(
Expand All @@ -2669,13 +2669,28 @@ def test_daterange_duration_cal_end_minute_oolb(
_ = drg.daterange
else:
error_msg = re.escape(
f"Prices unavailable as start evaluates to {helpers.fts(start_evaluted)}"
f"Prices unavailable as start evaluates to {helpers.fts(start_evaluated)}"
" which is earlier than the earliest minute for which price data is"
" available. The earliest minute for which prices are available is"
f" {helpers.fts(limit)}."
)
with pytest.raises(errors.StartTooEarlyError, match=error_msg):
_ = drg.daterange
try:
with pytest.raises(errors.StartTooEarlyError, match=error_msg):
_ = drg.daterange
except AssertionError:
if not ((ans.opens < start) & (ans.closes > start)).any():
raise
# the evaluated start will be as start when start falls on a trading
# minute, rather than the close. This can happen if the close time
# as UTC) was later two weeks prior, for example with DST in spring.
error_msg = re.escape(
f"Prices unavailable as start evaluates to {helpers.fts(start)}"
" which is earlier than the earliest minute for which price data is"
" available. The earliest minute for which prices are available is"
f" {helpers.fts(limit)}."
)
with pytest.raises(errors.StartTooEarlyError, match=error_msg):
_ = drg.daterange

@hyp.given(data=sthyp.data())
@hyp.settings(
Expand Down

0 comments on commit 19f1290

Please sign in to comment.