diff --git a/src/market_prices/data.py b/src/market_prices/data.py index 7dd26a2..61783a1 100644 --- a/src/market_prices/data.py +++ b/src/market_prices/data.py @@ -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. diff --git a/src/market_prices/prices/base.py b/src/market_prices/prices/base.py index 490a346..ac7d584 100644 --- a/src/market_prices/prices/base.py +++ b/src/market_prices/prices/base.py @@ -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: @@ -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, diff --git a/tests/test_data.py b/tests/test_data.py index 538f3a3..d9f1c66 100644 --- a/tests/test_data.py +++ b/tests/test_data.py @@ -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) diff --git a/tests/test_daterange.py b/tests/test_daterange.py index 0659a29..1bd7275 100644 --- a/tests/test_daterange.py +++ b/tests/test_daterange.py @@ -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( @@ -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(