Files
fxhnt/tests/integration/test_warehouse_read_panel.py
jgrusewski 2e50b04eff perf(cockpit): live mark reads LATEST close per symbol, not the full history
Root cause of slow /paper + /paper/live (profiled, not guessed): WarehouseLivePrice.get_prices called
store.read_panel(book_symbols, 'close'), which reads the ENTIRE multi-year close history of every open-book
symbol (EXPLAIN ANALYZE on prod: 21,359 rows / 502ms server) and then takes max() per symbol in Python —
to produce ~26 numbers. New store.read_latest() returns one row per symbol via a per-symbol index seek
(LATERAL ... ORDER BY ts DESC LIMIT 1): EXPLAIN ANALYZE 26 rows / 1.3ms, ~400x less server work and no
21k-row transfer/parse. NOT a cache — a correct query. (Postgres uses LATERAL; duckdb/sqlite fall back to
panel+max for tests.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 14:30:54 +02:00

47 lines
2.7 KiB
Python

"""read_panel returns per-symbol {epoch_day: value} dicts for one feature — the crypto-panel read path."""
from __future__ import annotations
from fxhnt.adapters.warehouse.duckdb_feature_store import DuckDbFeatureStore
from fxhnt.application.warehouse_ingest import WarehouseIngest
from fxhnt.domain.warehouse.bar import BarRecord
def test_read_panel_groups_by_symbol_and_day(tmp_path) -> None:
store = DuckDbFeatureStore(str(tmp_path / "wh.duckdb"))
WarehouseIngest(store).ingest_bars("BTCUSDT", [BarRecord("BTCUSDT", 19000 * 86400, 100.0, 100.0, 100.0),
BarRecord("BTCUSDT", 19001 * 86400, 110.0, 110.0, 110.0)])
WarehouseIngest(store).ingest_bars("ETHUSDT", [BarRecord("ETHUSDT", 19001 * 86400, 50.0, 50.0, 50.0)])
panel = store.read_panel(["BTCUSDT", "ETHUSDT"], "close")
assert panel["BTCUSDT"] == {19000: 100.0, 19001: 110.0}
assert panel["ETHUSDT"] == {19001: 50.0}
store.close()
def test_read_panel_empty_symbols(tmp_path) -> None:
store = DuckDbFeatureStore(str(tmp_path / "wh.duckdb"))
assert store.read_panel([], "close") == {}
store.close()
def test_read_latest_returns_last_value_per_symbol(tmp_path) -> None:
"""read_latest returns ONLY the last value per symbol — what the live cockpit mark needs — instead of
the whole multi-year panel (the /paper hotspot: read_panel read ~21k rows for ~26 numbers)."""
store = DuckDbFeatureStore(str(tmp_path / "wh.duckdb"))
WarehouseIngest(store).ingest_bars("BTCUSDT", [BarRecord("BTCUSDT", 19000 * 86400, 100.0, 100.0, 100.0),
BarRecord("BTCUSDT", 19001 * 86400, 110.0, 110.0, 110.0)])
WarehouseIngest(store).ingest_bars("ETHUSDT", [BarRecord("ETHUSDT", 19001 * 86400, 50.0, 50.0, 50.0)])
assert store.read_latest(["BTCUSDT", "ETHUSDT"], "close") == {"BTCUSDT": 110.0, "ETHUSDT": 50.0}
assert store.read_latest([], "close") == {}
store.close()
def test_warehouse_live_price_uses_read_latest(tmp_path) -> None:
"""WarehouseLivePrice.get_prices marks at the LATEST close per requested symbol (via read_latest), and
omits symbols with no warehouse rows (the view then marks those at entry)."""
from fxhnt.adapters.exchange.warehouse_live_price import WarehouseLivePrice
store = DuckDbFeatureStore(str(tmp_path / "wh.duckdb"))
WarehouseIngest(store).ingest_bars("BTCUSDT", [BarRecord("BTCUSDT", 19000 * 86400, 100.0, 100.0, 100.0),
BarRecord("BTCUSDT", 19005 * 86400, 123.0, 123.0, 123.0)])
assert WarehouseLivePrice(store).get_prices(["BTCUSDT", "UNSEENUSDT"]) == {"BTCUSDT": 123.0}
store.close()