test(vrp): port archived-hide coverage to synthetic entry + honest test narratives (review fixes)
This commit is contained in:
@@ -9,7 +9,7 @@ module does NO new compute, only comparison, plus a read of the strategy-tagged
|
||||
cost-to-trade inputs (median slippage, total fees).
|
||||
|
||||
`has_exec` is the gate: when the `<sid>_exec` track has no persisted forward_summary row yet (a real account
|
||||
that hasn't run, or an exec twin that's suspended — e.g. `vrp_exec` today), there is nothing to reconcile
|
||||
that hasn't run, or an exec twin that's suspended pending a fund decision), there is nothing to reconcile
|
||||
against — every other field defaults to zero rather than computing on unavailable data."""
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ from fxhnt.application.dashboard_service import DashboardService
|
||||
from fxhnt.application.forward_models import BacktestSummary
|
||||
from fxhnt.application.forward_models import ForwardNavRow as Row
|
||||
from fxhnt.application.forward_models import ForwardSummary
|
||||
from fxhnt.registry import STRATEGY_REGISTRY
|
||||
|
||||
|
||||
def _seeded() -> ForwardNavRepo:
|
||||
@@ -92,3 +93,31 @@ def test_fleet_no_backtest_renders_blank() -> None:
|
||||
assert unlock.bt_as_of == ""
|
||||
|
||||
|
||||
def test_archived_sleeves_hidden_everywhere(monkeypatch) -> None:
|
||||
# A SYNTHETIC archived registry entry (`STRATEGY_REGISTRY` `archived: True` — the pattern
|
||||
# `test_paper_books_web.py` uses for its synthetic `archivedstrat`/`newstrat`, in place of the deleted
|
||||
# vrp-based test) must be excluded from every cockpit surface `ForwardNavRepo.registry()` feeds:
|
||||
# `DashboardService.fleet()` and `.detail()`. Monkeypatch BEFORE `migrate()` so `_seed_registry()` (which
|
||||
# seeds the DB registry table FROM `STRATEGY_REGISTRY` and prunes anything not present there) picks up
|
||||
# the synthetic entry.
|
||||
monkeypatch.setitem(STRATEGY_REGISTRY, "archivedstrat",
|
||||
{"archived": True, "display_name": "Options income (put spreads)",
|
||||
"sleeve": "equity-vol", "gate_spec": {}})
|
||||
repo = _seeded() # seeds registry AFTER the monkeypatch above, so archivedstrat lands in the DB row set
|
||||
svc = DashboardService(repo)
|
||||
|
||||
# registry() itself: the archived row never comes back, a non-archived row (unlock) does.
|
||||
reg_ids = {r.strategy_id for r in repo.registry()}
|
||||
assert "archivedstrat" not in reg_ids
|
||||
assert "unlock" in reg_ids
|
||||
|
||||
# fleet(): archived sleeve excluded; a live sleeve still shows.
|
||||
fleet = svc.fleet()
|
||||
assert all(f.strategy_id != "archivedstrat" for f in fleet)
|
||||
assert any(f.strategy_id == "unlock" for f in fleet)
|
||||
|
||||
# detail(): direct hit on the archived id returns None (404 at the route layer); a live id still resolves.
|
||||
assert svc.detail("archivedstrat") is None
|
||||
assert svc.detail("unlock") is not None
|
||||
|
||||
|
||||
|
||||
@@ -4,20 +4,24 @@ from the PRECOMPUTED `sim_curve_ret` table (never recomputed on the request path
|
||||
measured-cost path's shape (curve + metrics + Live forward), with plain-language pill labels (never the raw
|
||||
registry id "multistrat").
|
||||
|
||||
NOTE: vrp was ARCHIVED/FALSIFIED (shelved 2026-07-15) — its `STRATEGY_REGISTRY` entry carries
|
||||
`archived: True`, so it is EXCLUDED from the Backtest book pills (`_SIM_BOOKS`) and a `?book=vrp` request
|
||||
falls back to the default book. The recompute-replay machinery itself still supports vrp (code + OPRA data
|
||||
kept); this test file now asserts vrp is HIDDEN from the sim UI (see the archived-book tests below).
|
||||
NOTE: vrp has been FULLY REMOVED from the codebase (shelved 2026-07-15, code+data deleted in the
|
||||
`chore/remove-vrp-code` cleanup) — there is no longer a `STRATEGY_REGISTRY` entry, `_SIM_BOOKS` pill, or
|
||||
recompute-replay support for it at all. This test file uses a synthetic non-existent book id
|
||||
(`no-such-book`) as an honest stand-in to probe the SAME fallback behavior an archived/unknown book must
|
||||
still get: never selectable, never rendered, always a graceful fallback to the default book (see the
|
||||
archived-book tests below).
|
||||
|
||||
Asserts:
|
||||
* /paper/sim shows the multistrat pill labelled in plain language, not the raw id, and NO vrp pill;
|
||||
* /paper/sim shows the multistrat pill labelled in plain language, not the raw id, and NO stray pill for an
|
||||
unknown book;
|
||||
* /paper/sim/run?book=multistrat renders a real curve + metrics from the precomputed sim_curve_ret series;
|
||||
* capital scales the curve linearly (same contract as the Bybit measured path);
|
||||
* the request path never recomputes the replay (a tripwire on ForwardStrategy.advance would fire if it did —
|
||||
proven implicitly: the seeded repo carries no build_strategy at all, so a recompute is structurally
|
||||
impossible; the curve still renders from sim_curve_ret alone);
|
||||
* the Live forward section renders for multistrat (its own forward track), not just bybit_4edge;
|
||||
* an archived book (vrp) is not selectable — the request falls back to the default book, never rendering vrp;
|
||||
* an unknown/archived book id is not selectable — the request falls back to the default book, never
|
||||
rendering that id;
|
||||
* absent precompute -> the graceful 'precomputing' note (never a 500, never the Bybit-specific caption).
|
||||
NO network — in-memory SQLite repos, seeded directly.
|
||||
"""
|
||||
@@ -47,7 +51,7 @@ def _seed_ibkr_curve(repo: PaperRepo, book: str, *, start: dt.date = dt.date(202
|
||||
repo.upsert_sim_curve_ret(book, d, ret, at=at)
|
||||
|
||||
|
||||
def _seeded_paper_repo(*, books: tuple[str, ...] = ("multistrat", "vrp")) -> PaperRepo:
|
||||
def _seeded_paper_repo(*, books: tuple[str, ...] = ("multistrat", "no-such-book")) -> PaperRepo:
|
||||
repo = PaperRepo("sqlite://")
|
||||
repo.migrate()
|
||||
for b in books:
|
||||
@@ -89,16 +93,14 @@ def _sim_data(html: str) -> dict:
|
||||
return json.loads(m.group(1))
|
||||
|
||||
|
||||
def test_sim_page_shows_multistrat_pill_and_hides_archived_vrp() -> None:
|
||||
def test_sim_page_shows_multistrat_pill_and_hides_unknown_book() -> None:
|
||||
c = _client(_seeded_paper_repo())
|
||||
html = c.get("/paper/sim").text
|
||||
assert "ETF Portfolio" in html
|
||||
# vrp is ARCHIVED — neither its pill label nor its raw id may appear anywhere on the Backtest page.
|
||||
assert "Options income (put spreads)" not in html
|
||||
assert "Equity VRP" not in html
|
||||
assert 'data-book="vrp"' not in html and "book=vrp" not in html
|
||||
# a book id absent from `_SIM_BOOKS` (unknown or archived) must never get a pill on the Backtest page.
|
||||
assert 'data-book="no-such-book"' not in html and "book=no-such-book" not in html
|
||||
# the raw registry id must never leak as pill TEXT (it is fine as a query-string value).
|
||||
assert ">multistrat<" not in html and ">vrp<" not in html
|
||||
assert ">multistrat<" not in html and ">no-such-book<" not in html
|
||||
|
||||
|
||||
def test_multistrat_book_is_selectable_and_lazy_loads() -> None:
|
||||
@@ -121,14 +123,14 @@ def test_multistrat_run_renders_curve_and_metrics_from_precomputed_sim_curve_ret
|
||||
assert 80000 < data["equity"][0] < 120000 # curve starts near the configured capital
|
||||
|
||||
|
||||
def test_archived_vrp_run_falls_back_and_never_renders_vrp() -> None:
|
||||
"""An archived book (vrp) is not in `_SIM_BOOKS`, so `/paper/sim/run?book=vrp` falls back to the default
|
||||
book (bybit_4edge) — it renders that book's view (or the graceful pending note), never vrp's curve/label."""
|
||||
def test_unknown_book_run_falls_back_and_never_renders_it() -> None:
|
||||
"""A book id absent from `_SIM_BOOKS` (unknown or archived) makes `/paper/sim/run?book=no-such-book`
|
||||
fall back to the default book (bybit_4edge) — it renders that book's view (or the graceful pending
|
||||
note), never the unknown id's curve/label."""
|
||||
c = _client(_seeded_paper_repo())
|
||||
r = c.get("/paper/sim/run", params={"book": "vrp", "capital": 100000})
|
||||
r = c.get("/paper/sim/run", params={"book": "no-such-book", "capital": 100000})
|
||||
assert r.status_code == 200 # graceful fallback, never a 500
|
||||
assert "Options income (put spreads)" not in r.text and "Equity VRP" not in r.text
|
||||
assert 'data-book="vrp"' not in r.text # the effective book is the default, not vrp
|
||||
assert 'data-book="no-such-book"' not in r.text # the effective book is the default, not the unknown id
|
||||
|
||||
|
||||
def test_ibkr_capital_scales_the_curve_linearly() -> None:
|
||||
@@ -180,12 +182,12 @@ def test_ibkr_missing_precompute_shows_pending_note_not_bybit_caption() -> None:
|
||||
assert 'data-cost-mode="flat"' not in r.text
|
||||
|
||||
|
||||
def test_sim_book_switch_covers_visible_books_and_excludes_archived_vrp() -> None:
|
||||
def test_sim_book_switch_covers_visible_books_and_excludes_unknown_book() -> None:
|
||||
c = _client(_seeded_paper_repo())
|
||||
# the three VISIBLE books each render their own page...
|
||||
for book in ("bybit_4edge", "bybit_4edge_levered", "multistrat"):
|
||||
r = c.get("/paper/sim", params={"book": book})
|
||||
assert r.status_code == 200 and f'data-book="{book}"' in r.text
|
||||
# ...but the ARCHIVED vrp is not selectable: it falls back to the default book (200, never vrp).
|
||||
r = c.get("/paper/sim", params={"book": "vrp"})
|
||||
assert r.status_code == 200 and 'data-book="vrp"' not in r.text
|
||||
# ...but an unknown/archived book id is not selectable: it falls back to the default book (200, never it).
|
||||
r = c.get("/paper/sim", params={"book": "no-such-book"})
|
||||
assert r.status_code == 200 and 'data-book="no-such-book"' not in r.text
|
||||
|
||||
Reference in New Issue
Block a user