Review fixes (2 Major + 2 Minor): - MAJOR: reword the pre-existing "Executed reality" card (same detail page) off the forbidden "divergence"/"vs sim"/"decay"/"bp" jargon -> "Real trades vs the plan", "N% behind/ahead of plan", "cost to trade %". Extend the web forbidden-strings test to assert (on VISIBLE text, stripping CSS/markup) no divergence/decay/vs-sim/bare-sim/bps anywhere on the rendered page. - MAJOR: %-of-book uses GROSS exposure (sum of abs) as the denominator, so a mixed-sign defined-risk book (vrp put-credit-spread: short put + long wing) no longer renders >100%/negative/non-summing shares. Each row keeps its signed value. New mixed-sign holdings test. - MINOR: holdings value is marked at each holding's last trade price (no live mark) — surface a per-row "as of <date>" and a column caveat so a stale mark isn't over-trusted. - MINOR: an unmapped asset symbol (future vrp option contracts) falls back to a plain generic "other" instead of echoing a raw contract string. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
80 lines
3.6 KiB
Python
80 lines
3.6 KiB
Python
import datetime as dt
|
|
|
|
from fastapi.testclient import TestClient
|
|
from sqlalchemy.orm import Session
|
|
|
|
from fxhnt.adapters.persistence.cockpit_models import (
|
|
ForwardSummaryRow,
|
|
StrategyAllocationRow,
|
|
StrategyFundingRow,
|
|
)
|
|
from fxhnt.adapters.persistence.forward_nav import ForwardNavRepo
|
|
from fxhnt.adapters.web.app import create_app
|
|
|
|
|
|
def _client(tmp_path):
|
|
repo = ForwardNavRepo(f"sqlite:///{tmp_path/'c.db'}")
|
|
repo.migrate()
|
|
return TestClient(create_app(repo))
|
|
|
|
|
|
def test_overview_renders_bottom_tab_nav(tmp_path):
|
|
r = _client(tmp_path).get("/")
|
|
assert r.status_code == 200
|
|
assert 'class="tabs"' in r.text # the mobile bottom-tab nav is present
|
|
assert ">Overview<" in r.text
|
|
|
|
|
|
def test_overview_shows_allocation_and_funding(tmp_path):
|
|
repo = ForwardNavRepo(f"sqlite:///{tmp_path/'c.db'}")
|
|
repo.migrate()
|
|
at = dt.datetime(2026, 7, 10)
|
|
repo.replace_allocations([StrategyAllocationRow(strategy_id="bybit_4edge", target_weight=0.54,
|
|
target_dollars=18900.0, policy_version=1, policy_hash="h", as_of="2026-07-10", computed_at=at)], at)
|
|
repo.replace_funding([StrategyFundingRow(strategy_id="bybit_4edge", state="funded", label="funded",
|
|
decay_run=0, recovery_run=0, first_pass_date=None, policy_version=2, policy_hash="h",
|
|
as_of="2026-07-10", computed_at=at)], at)
|
|
r = TestClient(create_app(repo)).get("/")
|
|
assert r.status_code == 200
|
|
assert "18,900" in r.text or "18900" in r.text # allocation dollars surfaced
|
|
assert "funded" in r.text # funding badge surfaced
|
|
assert "1,000,000" in r.text # capital meter ceiling = resolved paper ceiling (allow_live False)
|
|
|
|
|
|
def test_detail_shows_lifecycle_blocks(tmp_path):
|
|
repo = ForwardNavRepo(f"sqlite:///{tmp_path/'c.db'}")
|
|
repo.migrate()
|
|
at = dt.datetime(2026, 7, 10)
|
|
repo.replace_funding([StrategyFundingRow(strategy_id="bybit_4edge", state="funded", label="funded",
|
|
decay_run=0, recovery_run=0, first_pass_date=None, policy_version=2, policy_hash="h",
|
|
as_of="2026-07-10", computed_at=at)], at)
|
|
repo.replace_allocations([StrategyAllocationRow(strategy_id="bybit_4edge", target_weight=0.54,
|
|
target_dollars=18900.0, policy_version=1, policy_hash="h", as_of="2026-07-10", computed_at=at)], at)
|
|
r = TestClient(create_app(repo)).get("/strategy/bybit_4edge")
|
|
assert r.status_code == 200
|
|
assert "Funding health" in r.text
|
|
assert "Allocation" in r.text
|
|
assert "18,900" in r.text or "18900" in r.text
|
|
|
|
|
|
def test_detail_shows_executed_reality_block(tmp_path):
|
|
repo = ForwardNavRepo(f"sqlite:///{tmp_path/'c.db'}")
|
|
repo.migrate()
|
|
at = dt.datetime(2026, 7, 10)
|
|
with Session(repo._engine) as s:
|
|
s.add_all([
|
|
ForwardSummaryRow(strategy_id="bybit_4edge", as_of="2026-07-10", days=5, nav=1.0031,
|
|
total_return=0.0031, sharpe=1.0, maxdd=-0.05, gate_status="WAIT",
|
|
gate_reason="", src_updated_at=at),
|
|
ForwardSummaryRow(strategy_id="bybit_4edge_exec", as_of="2026-07-10", days=5, nav=1.0019,
|
|
total_return=0.0019, sharpe=1.0, maxdd=-0.05, gate_status="WAIT",
|
|
gate_reason="", src_updated_at=at),
|
|
])
|
|
s.commit()
|
|
r = TestClient(create_app(repo)).get("/strategy/bybit_4edge")
|
|
assert r.status_code == 200
|
|
# the executed-vs-plan card (renamed from "Executed reality" to plain language in Task 3).
|
|
assert "Real trades vs the plan" in r.text
|
|
assert "+0.19%" in r.text # real trades made
|
|
assert "+0.31%" in r.text # the plan expected
|