Files
fxhnt/tests/unit/test_crypto_fetch.py
jgrusewski eaa4e699b2 fix(fetch): clean missing-key error + day-aligned funding array
Fix A: `fxhnt fetch --futures` with no DATABENTO_API_KEY now exits 1
with a clear message instead of surfacing a RuntimeError traceback;
guard runs before any network call so crypto still proceeds normally.

Fix B: `fetch_crypto_pit` writes `funding` as a per-day array aligned
to `kd` (same length as `day`/`close`), dropping the separate
`funding_day` key that was unaligned with the price axis.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 12:30:07 +02:00

51 lines
2.3 KiB
Python

"""Crypto fetch: offline (injected GET) — universe selection, npz keys/shape, <200-bar skip, refresh."""
from __future__ import annotations
import numpy as np
from fxhnt.adapters.data.crypto_fetch import fetch_crypto_pit
def _fake_get_factory(n_bars: int):
"""Return a `get(url)` that serves canned Binance fapi JSON for any symbol."""
def get(url: str):
if "exchangeInfo" in url:
return {"symbols": [
{"symbol": "AAAUSDT", "contractType": "PERPETUAL", "quoteAsset": "USDT", "status": "TRADING"},
{"symbol": "BBBUSDT", "contractType": "PERPETUAL", "quoteAsset": "USDT", "status": "TRADING"},
]}
if "ticker/24hr" in url:
return [{"symbol": "AAAUSDT", "quoteVolume": "9999"},
{"symbol": "BBBUSDT", "quoteVolume": "10"}]
if "klines" in url:
# n_bars daily klines: [openTime, o,h,l, close, vol, closeTime, quoteVol, ...]
day_ms = 86_400_000
return [[i * day_ms, "1", "1", "1", str(100 + i), "1", i * day_ms + 1, "1000", 0, 0, 0, 0]
for i in range(n_bars)]
if "fundingRate" in url:
return []
return None
return get
def test_writes_npz_with_day_and_close(tmp_path) -> None:
n, total = fetch_crypto_pit(str(tmp_path), top_n=2, get=_fake_get_factory(300), sleep=lambda s: None, dead=[])
assert n == 2 and total == 2
z = np.load(tmp_path / "crypto_pit" / "AAAUSDT.npz")
assert "day" in z and "close" in z
assert len(z["day"]) == 300 and z["close"][0] == 100.0
assert len(z["funding"]) == len(z["day"]) # funding is day-aligned
def test_skips_symbols_with_too_few_bars(tmp_path) -> None:
n, total = fetch_crypto_pit(str(tmp_path), top_n=2, get=_fake_get_factory(50), sleep=lambda s: None, dead=[])
assert n == 0 and total == 2 # <200 bars -> skipped
def test_refresh_overwrites_existing(tmp_path) -> None:
fetch_crypto_pit(str(tmp_path), top_n=1, get=_fake_get_factory(300), sleep=lambda s: None, dead=[])
# second run with longer series must overwrite (refresh), not skip
fetch_crypto_pit(str(tmp_path), top_n=1, get=_fake_get_factory(400), sleep=lambda s: None, dead=[])
z = np.load(tmp_path / "crypto_pit" / "AAAUSDT.npz")
assert len(z["day"]) == 400