From 09c6892c3a43875299d9a753b4873e02c145cfe3 Mon Sep 17 00:00:00 2001 From: Stefan Jansen Date: Sat, 17 Oct 2026 12:07:49 -0400 Subject: [PATCH 1/2] test: reproduce false crypto weekend warnings on complete Monday bars --- tests/test_crypto_assets.py | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/tests/test_crypto_assets.py b/tests/test_crypto_assets.py index aae92e66..7e1d20d8 100644 --- a/tests/test_crypto_assets.py +++ b/tests/test_crypto_assets.py @@ -3,6 +3,7 @@ from datetime import UTC, datetime, timedelta import polars as pl +import pytest from ml4t.data.assets import AssetClass, AssetInfo, AssetSchema, AssetValidator from ml4t.data.assets.asset_class import ( @@ -10,7 +11,40 @@ normalize_crypto_symbol, parse_crypto_symbol, ) -from ml4t.data.calendar.crypto import CryptoCalendar +from ml4t.data.calendar.crypto import CryptoCalendar, CryptoSessionValidator +from ml4t.data.providers import SyntheticProvider + + +@pytest.fixture(params=["direct", "asset"]) +def continuity_issues(request): + def validate(frame, frequency): + if request.param == "direct": + return CryptoSessionValidator().validate_continuity(frame, frequency) + info = AssetInfo(symbol="BTC/USD", asset_class=AssetClass.CRYPTO) + return AssetValidator(info).validate(frame, frequency).get("continuity", []) + + return validate + + +@pytest.mark.parametrize("frequency", ["minute", "hourly"]) +def test_seven_monday_bars_do_not_imply_a_missing_weekend(continuity_issues, frequency): + with SyntheticProvider(calendar_mode="continuous") as provider: + frame = provider.fetch_ohlcv("BTCUSD", "2025-07-07", "2025-07-07", frequency).head(7) + assert continuity_issues(frame, frequency) == [] + + +def test_real_missing_crypto_weekend_remains_a_continuity_gap(continuity_issues): + with SyntheticProvider(calendar_mode="continuous") as provider: + frame = provider.fetch_ohlcv("BTCUSD", "2025-07-07", "2025-07-15", "daily") + frame = frame.filter(pl.col("timestamp").dt.weekday() <= 5) + issues = continuity_issues(frame, "daily") + assert any("Gap detected: 2025-07-11" in issue and "72.0 hours" in issue for issue in issues) + + +def test_complete_crypto_week_has_no_continuity_gap(continuity_issues): + with SyntheticProvider(calendar_mode="continuous") as provider: + frame = provider.fetch_ohlcv("BTCUSD", "2025-07-07", "2025-07-13", "daily") + assert continuity_issues(frame, "daily") == [] class TestCryptoCalendar: From f3b7570608a87a69cae2ebb2f21a7ef1fa17c5f3 Mon Sep 17 00:00:00 2001 From: Stefan Jansen Date: Sat, 17 Oct 2026 12:08:10 -0400 Subject: [PATCH 2/2] fix: detect crypto continuity gaps without inferring weekends from row counts --- src/ml4t/data/calendar/crypto.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/ml4t/data/calendar/crypto.py b/src/ml4t/data/calendar/crypto.py index 95730908..c2ecce77 100644 --- a/src/ml4t/data/calendar/crypto.py +++ b/src/ml4t/data/calendar/crypto.py @@ -316,17 +316,6 @@ def validate_continuity( f"({duration.total_seconds() / 3600:.1f} hours)" ) - # Check for weekend data (crypto should have it) - timestamps = df["timestamp"].to_list() - weekend_dates = [ - ts - for ts in timestamps - if ts.weekday() in [5, 6] # Saturday, Sunday - ] - - if not weekend_dates and len(timestamps) >= 7: - issues.append("Missing weekend data (crypto trades 24/7)") - return issues def validate_volume_profile(