Files
fxhnt/tests/integration/test_compare_measured_precompute_cli.py
2026-07-14 00:30:20 +02:00

86 lines
3.6 KiB
Python

"""`fxhnt compare-measured-precompute` — the OWN-MEMORY writer of the Bybit single-book measured-cost
artifacts.
The per-coin measured-cost compute used to run LIVE in the cockpit web handler and OOM-killed the 4Gi
`fxhnt-dashboard` pod (exit 137). The compute now runs only here (own-memory Job), persisting
`compare_measured_cache`. This asserts the CLI computes + upserts the artifacts and is idempotent.
(Phase 0b venue consolidation, Task 7b: the Binance combined-book leg + the Binance-vs-Bybit COMPARE
artifact this CLI used to also persist are retired — Bybit is fxhnt's sole crypto venue; the CLI now
persists ONLY the `bybit_4edge` (+ `bybit_4edge_levered`) single-book curves.)
NO network: the operational DSN points at a temp sqlite db; the `bybit_features` warehouse + cockpit tables
are seeded on it directly.
"""
from __future__ import annotations
import datetime as dt
from typer.testing import CliRunner
from fxhnt.cli import app
_DAY = 86_400
_AT = dt.datetime(2026, 6, 24, tzinfo=dt.UTC)
def _seed(dsn: str) -> None:
"""Seed a wide-spread Bybit `xsfunding` carry universe + sleeve returns on `bybit_features` — the
inputs the precompute reads."""
from fxhnt.adapters.warehouse.timescale_feature_store import TimescaleFeatureStore
store = TimescaleFeatureStore(dsn, table="bybit_features")
start = dt.date(2021, 1, 1)
days = 60
funding = {"AAAUSDT": 0.001, "BBBUSDT": 0.001, "XXXUSDT": -0.001, "YYYUSDT": -0.001}
for idx, (sym, fund) in enumerate(funding.items()):
rows = []
for d in range(days):
f = fund + 0.0005 * ((d + idx) % 3 - 1)
half = 0.01 # 200bp wide spread
ts = (start + dt.timedelta(days=d)).toordinal() * _DAY
rows.append((ts, {"funding": f, "close": 100.0, "spot_close": 100.0,
"high": 100.0 * (1 + half), "low": 100.0 * (1 - half),
"turnover": 50_000_000.0}))
store.write_features(sym, rows)
store.close()
def _patch(monkeypatch, tmp_path) -> str:
dsn = f"sqlite:///{tmp_path / 'op.db'}"
monkeypatch.setenv("FXHNT_OPERATIONAL_DSN", dsn)
from fxhnt import cli as climod
climod.get_settings.cache_clear()
_seed(dsn)
return dsn
def test_cli_persists_single_book_cache(monkeypatch, tmp_path) -> None:
dsn = _patch(monkeypatch, tmp_path)
res = CliRunner().invoke(app, ["compare-measured-precompute", "--capital", "100000"])
assert res.exit_code == 0, res.output
assert "persisted" in res.output.lower()
from fxhnt.adapters.persistence.paper_repo import PaperRepo
from fxhnt.application.compare_measured_cost import read_cached_single_measured
payload = read_cached_single_measured(PaperRepo(dsn), "bybit_4edge")
assert payload is not None
assert payload["book"] == "bybit_4edge"
assert payload["dates_iso"] and payload["equity"]
def test_cli_is_idempotent(monkeypatch, tmp_path) -> None:
dsn = _patch(monkeypatch, tmp_path)
runner = CliRunner()
r1 = runner.invoke(app, ["compare-measured-precompute", "--capital", "100000"])
assert r1.exit_code == 0, r1.output
from fxhnt.adapters.persistence.paper_repo import PaperRepo
from fxhnt.application.compare_measured_cost import read_cached_single_measured
first = read_cached_single_measured(PaperRepo(dsn), "bybit_4edge")
r2 = runner.invoke(app, ["compare-measured-precompute", "--capital", "100000"])
assert r2.exit_code == 0, r2.output
second = read_cached_single_measured(PaperRepo(dsn), "bybit_4edge")
assert first is not None and second is not None
assert first["equity"] == second["equity"]