Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions src/ml4t/data/calendar/crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
36 changes: 35 additions & 1 deletion tests/test_crypto_assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,48 @@
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 (
get_asset_class_from_symbol,
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:
Expand Down
Loading