Files
fxhnt/tests/integration/test_dashboard_surfacing.py
jgrusewski 31bc726053 fix(cockpit): overview ceiling reads resolved policy (paper/live), not hardcoded $35k; ruff import-sort
Whole-branch Important: dashboard showed the stale hardcoded ceiling while the engine sizes against
the resolved (paper $1M / live $35k) ceiling. Task-5 Minor: ruff I001 import-sort.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 20:51:15 +02:00

101 lines
4.5 KiB
Python

import datetime as dt
from fxhnt.adapters.persistence.cockpit_models import (
ForwardSummaryRow,
StrategyAllocationRow,
StrategyFundingRow,
)
from fxhnt.adapters.persistence.forward_nav import ForwardNavRepo
from fxhnt.application.dashboard_service import DashboardService
def _seed(tmp_path):
repo = ForwardNavRepo(f"sqlite:///{tmp_path/'c.db'}")
repo.migrate()
at = dt.datetime(2026, 7, 10)
def _sum(sid, tr):
return ForwardSummaryRow(strategy_id=sid, as_of="2026-07-10", days=5, nav=1.0 + tr, total_return=tr,
sharpe=1.0, maxdd=-0.05, gate_status="WAIT", gate_reason="", src_updated_at=at)
from sqlalchemy.orm import Session
with Session(repo._engine) as s:
s.add_all([_sum("bybit_4edge", 0.0031), _sum("bybit_4edge_exec", 0.0019)])
s.commit()
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),
StrategyFundingRow(strategy_id="multistrat", state="de-funded", label="de-funded", decay_run=0,
recovery_run=0, first_pass_date="2026-06-20", policy_version=2, policy_hash="h",
as_of="2026-07-10", computed_at=at),
], at)
return repo
def test_fleet_carries_allocation_funding_and_exec_pairing(tmp_path):
svc = DashboardService(_seed(tmp_path))
rows = {f.strategy_id: f for f in svc.fleet()}
b = rows["bybit_4edge"]
assert b.alloc_dollars == 18900.0 and abs(b.alloc_weight - 0.54) < 1e-9
assert b.funding_label == "funded"
# exec pairing: bybit_4edge modeled paired with bybit_4edge_exec (return 0.19% vs 0.31% -> Δ -0.12%)
assert b.exec_twin_sid == "bybit_4edge_exec"
assert abs(b.exec_return_pct - 0.19) < 1e-6
assert abs(b.exec_divergence_pct - (-0.12)) < 1e-6
# a de-funded edge
assert rows["multistrat"].funding_label == "de-funded"
# the executed twin is flagged for hiding at top level
assert "bybit_4edge_exec" in svc.exec_twin_sids()
def test_overview_capital_meter(tmp_path):
ov = DashboardService(_seed(tmp_path)).overview()
assert ov.gross_deployed == 18900.0
# ceiling reflects the RESOLVED allocation policy (default settings -> allow_live False -> paper $1M),
# NOT the old hardcoded $35k — so the meter matches the ceiling the engine actually sizes against.
assert ov.ceiling == 1_000_000.0
assert ov.n_funded == 1 and ov.n_defunded == 1
def test_graceful_when_no_alloc_funding(tmp_path):
# a repo with summaries but NO allocation/funding rows -> fields default, no crash.
repo = ForwardNavRepo(f"sqlite:///{tmp_path/'e.db'}")
repo.migrate()
rows = {f.strategy_id: f for f in DashboardService(repo).fleet()}
any_row = next(iter(rows.values()))
assert any_row.alloc_dollars is None and any_row.funding_label == ""
def _seed_multistrat_pair(tmp_path, db_name):
repo = ForwardNavRepo(f"sqlite:///{tmp_path/db_name}")
repo.migrate()
at = dt.datetime(2026, 7, 10)
def _sum(sid, tr):
return ForwardSummaryRow(strategy_id=sid, as_of="2026-07-10", days=5, nav=1.0 + tr, total_return=tr,
sharpe=1.0, maxdd=-0.05, gate_status="WAIT", gate_reason="", src_updated_at=at)
from sqlalchemy.orm import Session
with Session(repo._engine) as s:
s.add_all([_sum("multistrat", 0.0031), _sum("multistrat_exec", 0.0019)])
s.commit()
return repo, at
def test_detail_exec_venue_from_book_state(tmp_path):
repo, at = _seed_multistrat_pair(tmp_path, "f.db")
repo.set_book_state("multistrat_exec_pos", {"positions": {}, "prices": {}, "envelope": 1e6,
"venue": "ibkr-paper"}, at)
svc = DashboardService(repo)
d = svc.detail("multistrat")
assert d.exec_twin_sid == "multistrat_exec"
assert d.exec_venue == "ibkr-paper"
def test_detail_exec_venue_none_when_unrecorded(tmp_path):
repo, at = _seed_multistrat_pair(tmp_path, "g.db")
repo.set_book_state("multistrat_exec_pos", {"positions": {}, "prices": {}, "envelope": 1e6}, at)
svc = DashboardService(repo)
d = svc.detail("multistrat")
assert d.exec_twin_sid == "multistrat_exec"
assert d.exec_venue is None