Download tests/test_data_layer.py from 3VVM/Lk: direct link, hf CLI and curl.
- Browser
- Download file 4.51 kB
-
https://huggingface.co/spaces/3VVM/Lk/resolve/main/tests/test_data_layer.py
- Command line
-
hf download hf://spaces/3VVM/Lk/tests/test_data_layer.py
-
curl -L -o test_data_layer.py https://huggingface.co/spaces/3VVM/Lk/resolve/main/tests/test_data_layer.py
4.51 kB
| """ | |
| Covers the parts of data_layer.py that don't require network access: | |
| validate_ohlcv (duplicate/invalid/NaN handling), LocalCache round-trip, | |
| and data_freshness classification. fetch_ohlcv() itself needs a real | |
| network call and is exercised only by the smoke-test script (expected | |
| to fail here with DataSourceError -- see README). | |
| """ | |
| import numpy as np | |
| import pandas as pd | |
| from data_layer import LocalCache, data_freshness, validate_ohlcv | |
| def test_validate_removes_duplicates_and_keeps_first(): | |
| idx = pd.to_datetime(["2024-01-01", "2024-01-01", "2024-01-02"], utc=True) | |
| df = pd.DataFrame({ | |
| "open": [1, 999, 2], "high": [1, 999, 2], "low": [1, 999, 2], | |
| "close": [1, 999, 2], "volume": [10, 10, 10], | |
| }, index=idx) | |
| out, report = validate_ohlcv(df) | |
| assert report.duplicates_removed == 1 | |
| assert len(out) == 2 | |
| assert out.iloc[0]["close"] == 1 # first occurrence kept, not the duplicate | |
| def test_validate_removes_invalid_ohlc_relationship(): | |
| idx = pd.date_range("2024-01-01", periods=3, freq="1h", tz="UTC") | |
| df = pd.DataFrame({ | |
| "open": [10, 10, 10], | |
| "high": [11, 9, 11], # row 1: high < open -> invalid | |
| "low": [9, 9, 9], | |
| "close": [10.5, 10.5, 10.5], | |
| "volume": [5, 5, 5], | |
| }, index=idx) | |
| out, report = validate_ohlcv(df) | |
| assert report.invalid_ohlc_removed == 1 | |
| assert len(out) == 2 | |
| def test_validate_removes_nan_rows_and_reports_them(): | |
| idx = pd.date_range("2024-01-01", periods=3, freq="1h", tz="UTC") | |
| df = pd.DataFrame({ | |
| "open": [10, np.nan, 10], "high": [11, 11, 11], "low": [9, 9, 9], | |
| "close": [10.5, 10.5, 10.5], "volume": [5, 5, 5], | |
| }, index=idx) | |
| out, report = validate_ohlcv(df) | |
| assert report.nan_rows_removed == 1 | |
| assert len(out) == 2 | |
| def test_validate_marks_negative_volume_unavailable_not_invented(): | |
| idx = pd.date_range("2024-01-01", periods=2, freq="1h", tz="UTC") | |
| df = pd.DataFrame({ | |
| "open": [10, 10], "high": [11, 11], "low": [9, 9], | |
| "close": [10.5, 10.5], "volume": [5, -3], | |
| }, index=idx) | |
| out, _ = validate_ohlcv(df) | |
| assert np.isnan(out.iloc[1]["volume"]), "negative volume must become NaN, never a guessed positive number" | |
| def test_local_cache_round_trip(tmp_path_str="/tmp/_moirai_test_cache.sqlite3"): | |
| import os | |
| if os.path.exists(tmp_path_str): | |
| os.remove(tmp_path_str) | |
| cache = LocalCache(tmp_path_str) | |
| idx = pd.date_range("2024-01-01", periods=3, freq="1h", tz="UTC") | |
| df = pd.DataFrame({"open": [1, 2, 3], "high": [1, 2, 3], "low": [1, 2, 3], | |
| "close": [1, 2, 3], "volume": [1, 2, 3]}, index=idx) | |
| assert cache.get("EURUSD=X", "1h", "yfinance", "2024-01-01", "2024-01-02") is None | |
| cache.set("EURUSD=X", "1h", "yfinance", "2024-01-01", "2024-01-02", df) | |
| round_tripped = cache.get("EURUSD=X", "1h", "yfinance", "2024-01-01", "2024-01-02") | |
| assert round_tripped is not None | |
| assert len(round_tripped) == 3 | |
| assert list(round_tripped["close"]) == [1, 2, 3] | |
| os.remove(tmp_path_str) | |
| def test_data_freshness_classification(): | |
| now = pd.Timestamp.now(tz="UTC") | |
| fresh = data_freshness(now - pd.Timedelta(minutes=1), "1h") | |
| stale = data_freshness(now - pd.Timedelta(hours=10), "1h") | |
| assert fresh["status"] == "fresh" | |
| assert stale["status"] == "stale" | |
| def test_resolve_history_window_days(): | |
| from data_layer import resolve_history_window | |
| kind, value = resolve_history_window("1 day") | |
| assert kind == "start" | |
| kind, value = resolve_history_window("30 days") | |
| assert kind == "start" | |
| def test_resolve_history_window_months_uses_calendar_months(): | |
| from data_layer import resolve_history_window | |
| from datetime import datetime, timezone | |
| kind, value = resolve_history_window("3 months") | |
| assert kind == "start" | |
| now = datetime.now(timezone.utc) | |
| delta_days = (now - value).days | |
| # 3 calendar months is 89-92 days depending on which months are spanned -- | |
| # NOT exactly 90 (which a naive 30*3 approximation would assume). | |
| assert 88 <= delta_days <= 93, f"expected ~3 calendar months, got {delta_days} days" | |
| def test_resolve_history_window_max(): | |
| from data_layer import resolve_history_window | |
| kind, value = resolve_history_window("max") | |
| assert kind == "period" | |
| assert value == "max" | |
| def test_resolve_history_window_years(): | |
| from data_layer import resolve_history_window | |
| kind, value = resolve_history_window("2 years") | |
| assert kind == "start" | |