Files
fxhnt/tests/unit/test_no_binance_data.py
jgrusewski 649ea25000 feat(paper): drop vestigial venue column from the 7 paper_* tables (Phase 0c)
bybit is fxhnt's sole paper book (Task 7f hardcoded every PaperRepo read/write
to it); the prod purge left every paper_* table bybit-only. Remove the now-constant
`venue` column at the SCHEMA level so the paper book is single-venue by construction.

- cockpit_models.py: drop `venue` from all 7 paper models; re-key the 5 composite-PK
  tables on their remaining columns; paper_nav_summary swaps its sole `venue` PK for a
  singleton surrogate `id` (=1); paper_trades drops the non-PK venue index+column.
- paper_book_migration.py (new): Postgres-only, idempotent reverse migration invoked by
  PaperRepo.migrate(). Guarded on the live `venue` column so it is a pure no-op after the
  first run (heavy DDL on every cockpit startup would crashloop). Sole home of the
  column-drop DDL, keeping paper_repo free of any discriminator logic.
- paper_repo.py: delete `_BYBIT_VENUE`/`_VENUE_TABLES`; strip every venue kwarg/predicate;
  PK lookups use (1) / (key). `grep -n venue paper_repo.py` → 0.
- test_no_binance_data.py: 7f venue-default guard → schema guard (no paper model has a
  `venue` column).

Full suite green: `uv run pytest -q` → 1853 passed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 16:34:17 +02:00

124 lines
5.6 KiB
Python

"""Guard (Phase 0b Task 7e): Binance is LITERALLY 0% in src/fxhnt — CODE AND COMMENTS. SUBSTRING guard
(mirrors test_no_alpaca.py): `grep -rin binance src/fxhnt` must return 0 hits.
This SUPERSEDES the narrower Task 7b/7c guard that used to live here (which explicitly scoped itself to
CODE, not comments, and intentionally kept historical/documentary Binance mentions). Task 7e closes that
gap: it retires the crypto_pit Binance fetch/panel/ingest/research surface (`crypto_fetch`,
`crypto_pit_panel`, `ts_trend_strategy`, `silver_crypto`, `silver_ingest_helpers`, the `crypto_pit_dir`
setting, the `backtest-funding` CLI, the `--crypto`/`--source crypto` legs of `fetch`/`warehouse-ingest`)
and rewords every remaining Binance-naming comment. No knowledge is lost — the mirage/venue-history
findings are preserved (just not attributed by name) and are banked in memory under
`project_fxhnt_measured_cost_mirage_and_postgres`.
The module/asset/CLI existence checks below are a SECOND, code-level guard against reintroduction —
complementary to the substring grep, not a replacement for it."""
from __future__ import annotations
import importlib
import pathlib
import subprocess
import pytest
def test_no_binance_symbols_in_src() -> None:
root = pathlib.Path(__file__).parents[2] / "src" / "fxhnt"
hits = subprocess.run(["grep", "-rin", "binance", str(root)], capture_output=True, text=True).stdout
assert not hits.strip(), f"residual Binance references:\n{hits}"
_DELETED_MODULES = [
"fxhnt.adapters.data.binance_funding",
"fxhnt.adapters.data.binance_xsfunding_live",
"fxhnt.adapters.exchange.binance_funding_history",
"fxhnt.adapters.exchange.ccxt_live_price",
"fxhnt.adapters.data.unlock_calendar_live",
"fxhnt.application.funding_dispersion_eval",
"fxhnt.application.bybit_xvenue_funding_eval",
"fxhnt.application.funding_ingest",
"fxhnt.application.spot_ingest",
"fxhnt.application.basis_panel_builder",
"fxhnt.domain.strategies.funding_carry",
# Task 7c — the Binance worst_basis Parquet side-store + the dormant vendored VRP PoC.
"fxhnt.adapters.data.binance_vision_klines",
"fxhnt.adapters.data.binance_spot_history",
"fxhnt.application.worst_basis_ingest",
"fxhnt.adapters.persistence.worst_basis_store",
"fxhnt.vendor.surfer.surfer_poc",
# Task 7e — the crypto_pit Binance fetch/panel/ingest/research surface.
"fxhnt.adapters.data.crypto_fetch",
"fxhnt.adapters.data.crypto_pit_panel",
"fxhnt.application.ts_trend_strategy",
"fxhnt.adapters.warehouse.silver_crypto",
"fxhnt.application.silver_ingest_helpers",
]
# Binance DATA ingest assets (fed only the retired `features` warehouse funding/spot columns).
_DELETED_INGEST_ASSETS = ("crypto_bars", "crypto_funding", "crypto_spot")
# Binance-specific CLIs (ingest / cross-venue-dispersion / manual delta-neutral-order bridge / crypto_pit
# research).
_DELETED_CLIS = {
"crypto-funding-ingest", "crypto-spot-ingest", "build-basis-panel", "xsfunding-orders",
"funding-dispersion-test", "bybit-xvenue-funding-eval",
"ingest-worst-basis", # Task 7c — the Binance worst_basis Parquet side-store CLI.
"backtest-funding", # Task 7e — its only data source was the crypto_pit panel.
}
# CLIs that MUST survive (Bybit/IBKR paths, Bybit-only measured cost, or the futures-only fetch/ingest).
_KEPT_CLIS = {
"bybit-carry-revalidate", "bybit-edges-eval", "cross-venue-carry-eval",
"compare-measured-precompute", "bybit-ingest-worst-basis", "fetch", "warehouse-ingest",
}
@pytest.mark.parametrize("mod", _DELETED_MODULES)
def test_binance_module_gone(mod: str) -> None:
with pytest.raises(ModuleNotFoundError):
importlib.import_module(mod)
def test_binance_ingest_assets_gone() -> None:
from fxhnt.adapters.orchestration import assets
for a in _DELETED_INGEST_ASSETS:
assert not hasattr(assets, a), f"{a} Binance ingest asset still present"
def test_binance_clis_gone() -> None:
from fxhnt.cli import app
names = {c.name or (c.callback.__name__ if c.callback else None) for c in app.registered_commands}
assert not (_DELETED_CLIS & names), f"binance CLIs still registered: {_DELETED_CLIS & names}"
def test_bybit_ibkr_and_futures_clis_survive() -> None:
"""No over-deletion: the decoupled Bybit/IBKR commands + the futures-only fetch/warehouse-ingest legs
are still wired."""
from fxhnt.cli import app
names = {c.name or (c.callback.__name__ if c.callback else None) for c in app.registered_commands}
missing = _KEPT_CLIS - names
assert not missing, f"a KEPT command was wrongly removed: {missing}"
def test_paper_models_have_no_venue_column() -> None:
"""Guard (Phase 0c): the paper book is single-venue at the SCHEMA level — bybit is fxhnt's sole paper book,
so the vestigial `venue` discriminator column has been dropped from every paper_* table (Task 7f had already
hardcoded every `PaperRepo` read/write to bybit; this removes the now-constant column). The invariant is no
longer 'writes use one hardcoded venue value' but 'the models carry NO venue column at all'."""
from fxhnt.adapters.persistence.cockpit_models import (
PaperBookStateRow,
PaperNavRow,
PaperNavSummaryRow,
PaperPositionRow,
PaperShadowPositionRow,
PaperSleeveRetRow,
PaperTradeRow,
)
paper_models = (
PaperPositionRow, PaperTradeRow, PaperNavRow, PaperNavSummaryRow,
PaperBookStateRow, PaperSleeveRetRow, PaperShadowPositionRow,
)
for model in paper_models:
cols = {c.name for c in model.__table__.columns}
assert "venue" not in cols, f"{model.__name__} still has a venue column: {sorted(cols)}"