SPB Git

Repository Public

test_hl_import.py 3.6 KB 68 lines python

1"""spq.data.hl_import: the API's pre-fetched bundle becomes the same local store the downloader writes, offline."""2import json34import pandas as pd5import pytest67from spq.data import DataError, load_candles8from spq.data import hl_import9from spq.data.hyperliquid import load_funding, load_meta, load_perp_universe1011H = 3_600_00012T0 = 1_780_000_000_000 // H * H131415def bundle(n=48, symbols=("BTC", "ETH")):16    candles, funding = {}, {}17    for k, s in enumerate(symbols):18        px = 100.0 * (k + 1)19        candles[s] = [[T0 + i * H, str(px + i), str(px + i + 2), str(px + i - 1), str(px + i + 1), "12.5"] for i in range(n)]20        funding[s] = [[T0 + i * H + 37, "0.0000125", None if i % 2 else "0.0001"] for i in range(n)]21    return {"interval": "1h", "as_of_ms": T0 + n * H - 1, "meta": {s: {"max_leverage": 40, "size_decimals": 5} for s in symbols},22            "candles": candles, "funding": funding}232425def test_bundle_becomes_the_downloader_layout_and_drops_the_bar_in_progress(tmp_path):26    summary = hl_import.import_bundle(bundle(), tmp_path)27    # as_of is one millisecond before the last bar closes: that bar is in progress and is not stored28    assert summary == {"BTC": {"bars": 47, "funding": 48}, "ETH": {"bars": 47, "funding": 48}}29    df, q = load_candles("BTC", "1h", root=tmp_path, source="hyperliquid", as_of=pd.Timestamp(T0 + 48 * H, unit="ms", tz="UTC"))30    assert len(df) == 47 and q.missing_bars == 0 and df["close"].iloc[0] == 101.031    f, fq = load_funding("BTC", root=tmp_path, as_of=pd.Timestamp(T0 + 48 * H, unit="ms", tz="UTC"))32    assert fq.n == 48 and fq.off_grid == 0 and fq.max_offset_ms == 37 and f["rate"].iloc[0] == 0.000012533    assert load_meta(tmp_path)["universe"]["ETH"] == {"max_leverage": 40.0, "size_decimals": 5, "delisted": False}34    data, _, _, _ = load_perp_universe(["BTC", "ETH"], "1h", root=tmp_path, as_of=pd.Timestamp(T0 + 48 * H, unit="ms", tz="UTC"))35    assert list(data) == ["BTC", "ETH"] and data["ETH"]["funding"].notna().all()363738def test_spot_candles_are_written_for_the_benchmark(tmp_path):39    b = bundle()40    b["spot"] = {"source": "coinbase", "candles": {"BTC": b["candles"]["BTC"], "ETH": b["candles"]["ETH"]}}41    assert hl_import.import_bundle(b, tmp_path)["BTC"]["spot_bars"] == 4742    assert (tmp_path / "coinbase" / "BTC_1h.parquet").exists()434445@pytest.mark.parametrize("mutate", [46    lambda b: b["funding"].pop("ETH"),                                   # funding must cover the same symbols47    lambda b: b["candles"]["BTC"].append(b["candles"]["BTC"][3]),         # duplicated bar48    lambda b: b["funding"]["BTC"].append([T0 + 5 * H + 900, "0.5", None]),  # two settlements for one hour49    lambda b: b["candles"].__setitem__("btc", b["candles"].pop("BTC")),   # not an upper-case ticker50    lambda b: b.pop("interval"),51])52def test_a_bundle_that_is_not_clean_is_refused(tmp_path, mutate):53    b = bundle()54    mutate(b)55    with pytest.raises((DataError, ValueError)):56        hl_import.import_bundle(b, tmp_path)575859def test_cli_exit_codes_and_no_network_import(tmp_path, capsys):60    path = tmp_path / "bundle.json"61    path.write_text(json.dumps(bundle()))62    assert hl_import.main(["--input", str(path), "--out", str(tmp_path / "data")]) == 063    assert json.loads(capsys.readouterr().out)["BTC"]["bars"] == 4764    path.write_text("{}")65    assert hl_import.main(["--input", str(path), "--out", str(tmp_path / "data2")]) == 266    # the module itself opens no connection: it has no reference to the downloader's request function67    assert not any(name in vars(hl_import) for name in ("post_info", "urllib", "fetch_candles", "fetch_funding", "download"))68
Files main
Menu

Theme