Files
fxhnt/tests/unit/test_display_names.py
jgrusewski 8bd87e8ab1 fix(cockpit): honest FAIL verdict + plain UCITS holdings on the strategy detail
Two inconsistencies found verifying the IBKR /strategy/multistrat page:

1. A FAILED backtest with a high Sharpe (multistrat: Sharpe 1.7, gate FAIL)
   rendered a GREEN "Validated edge ... backtest passed" banner directly above
   the FAIL badge — the Sharpe>=1.0 fallback overrode the explicit FAIL. The
   verdict now respects the badge: an explicit FAIL is an amber "Backtest
   didn't clear the gate ... treat it as unproven", never a green "passed".
   The backtest Sharpe figure also goes neutral-grey (not alarming red) when
   unvalidated — the number itself (1.7) isn't bad, it just didn't validate.

2. The UCITS lines the EU DU account actually holds (CSPX/CBU0/IDTM/IGLN/ICOM)
   weren't in ASSET_DESCRIPTIONS, so the holdings "what it is" column read
   "other". Mapped each to its plain description (US stocks / US Treasuries /
   Gold / Commodities), same as its US cousin.

Regression tests: a passed=False + Sharpe 1.7 detail renders amber
"didn't clear the gate" and never "backtest passed"/"Validated edge"; UCITS
tickers resolve to plain descriptions, never "other". Verified desktop+mobile.

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

59 lines
3.0 KiB
Python

"""display_names.py — the SINGLE internal-id -> human-name map every cockpit view uses (the plain-language
global constraint). Covers every STRATEGY_REGISTRY entry (never a raw id falls through to the template) plus
the sim-book override pills absorbed from app.py's former local `_SIM_BOOK_LABELS`, and the asset->plain-
description map used by the holdings table."""
from __future__ import annotations
from fxhnt.application.display_names import ASSET_DESCRIPTIONS, asset_description, display_name
from fxhnt.registry import STRATEGY_REGISTRY
def test_every_registry_strategy_has_a_display_name() -> None:
for sid, entry in STRATEGY_REGISTRY.items():
# multistrat/vrp are deliberately overridden with a friendlier pill label (see the next test) — every
# other registry entry must resolve to its own registry `display_name`.
if sid not in ("multistrat", "vrp"):
assert display_name(sid) == entry["display_name"]
assert display_name(sid) != sid # never the bare raw id
def test_sim_book_overrides_are_plain_language() -> None:
# The interim local pill labels from app.py's `_SIM_BOOK_LABELS` (Task 1), absorbed here.
assert display_name("multistrat") == "ETF Portfolio"
assert display_name("vrp") == "Options income (put spreads)"
def test_unknown_id_falls_back_to_itself_never_raises() -> None:
assert display_name("not-a-real-strategy") == "not-a-real-strategy"
def test_asset_descriptions_cover_the_multistrat_instruments() -> None:
assert ASSET_DESCRIPTIONS == {
# US listings (modelled book / us-venue)
"SPY": "US stocks", "IEF": "US Treasuries", "GLD": "Gold",
"PDBC": "Commodities", "DBMF": "Managed futures",
# UCITS listings actually held in the EU DU account (the multi-strat leg trades these)
"CSPX": "US stocks", "CBU0": "US Treasuries", "IDTM": "US Treasuries",
"IGLN": "Gold", "ICOM": "Commodities",
}
for sym, desc in ASSET_DESCRIPTIONS.items():
assert asset_description(sym) == desc
def test_ucits_holdings_never_read_other() -> None:
# The EU DU account holds the UCITS remap (KID/PRIIPs blocks the US ETFs), so the holdings table sees
# CSPX/CBU0/IDTM/IGLN/ICOM — each MUST resolve to the same plain "what it is" as its US cousin, never the
# generic "other" (the bug found verifying the IBKR detail page: real holdings reading "other").
assert asset_description("CSPX") == "US stocks"
assert asset_description("IGLN") == "Gold"
assert asset_description("ICOM") == "Commodities"
assert asset_description("CBU0") == "US Treasuries"
assert asset_description("IDTM") == "US Treasuries"
def test_asset_description_unknown_symbol_falls_back_to_generic() -> None:
# An unmapped symbol (e.g. a future vrp option contract) gets a plain generic word, never a blank or an
# odd raw contract string — the symbol itself is still shown in its own column.
assert asset_description("XYZ") == "other"
assert asset_description("SPY 260130P00500000") == "other"