fix(cockpit): IBKR paper card was falsely "not connected" — it is live via UCITS
The IBKR paper account (DU9600528) IS connected and trading: the armed fxhnt-ucits-rebalancer (0 9 * * 1-5) books the multi-strat ETF leg through UCITS lines (EU KID/PRIIPs blocks the US-ETF leg, whose rebalancer is correctly suspended). Prod already rendered it ($1,029,621, 5 holdings). Two self-inflicted "not connected" artifacts fixed: - scripts/dev_cockpit_local.py did NOT wire ibkr_account_repo, so the LOCAL dev cockpit always showed the IBKR card empty regardless of real data — a misleading local-verification artifact. Now wires every repo prod wires. - paper.html's new orientation banner hardcoded "not connected yet" (written from that stale local view). Made it data-aware (keyed on ibkr_view) so it states the live NLV + holdings when trading, "not connected yet" only when genuinely empty — it can't drift from the card beneath it. Adds a regression assertion: connected account => banner says "live now, trading through UCITS" and never "not connected yet". Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,7 @@ import os
|
||||
import uvicorn
|
||||
|
||||
from fxhnt.adapters.persistence.forward_nav import ForwardNavRepo
|
||||
from fxhnt.adapters.persistence.ibkr_account_repo import IbkrAccountRepo
|
||||
from fxhnt.adapters.persistence.paper_repo import PaperRepo
|
||||
from fxhnt.adapters.web.app import create_app
|
||||
|
||||
@@ -20,10 +21,14 @@ dsn = os.environ["FXHNT_OPERATIONAL_DSN"]
|
||||
port = int(os.environ.get("FXHNT_DEV_PORT", "8801"))
|
||||
|
||||
# READ-ONLY: no .migrate() — never write DDL to a prod DB we're only browsing.
|
||||
# Wire EVERY repo prod wires (create_app_from_settings), or a page silently degrades to an empty state that
|
||||
# does NOT match prod — the IBKR paper-account card in particular reads through ibkr_account_repo; without it
|
||||
# the /paper card renders "not connected yet" even when the real account is live (a misleading local artifact).
|
||||
app = create_app(
|
||||
ForwardNavRepo(dsn),
|
||||
paper_repo=PaperRepo(dsn),
|
||||
bybit_paper_repo=PaperRepo(dsn),
|
||||
ibkr_account_repo=IbkrAccountRepo(dsn),
|
||||
)
|
||||
print(f"cockpit (read-only) on http://127.0.0.1:{port}/paper/sim?book=bybit_4edge")
|
||||
uvicorn.run(app, host="127.0.0.1", port=port, log_level="warning")
|
||||
|
||||
@@ -15,8 +15,10 @@
|
||||
<div class="verdict" style="margin-bottom:18px">
|
||||
<b>Two paper accounts, one job: prove the edges before real money goes in.</b>
|
||||
The <b>Bybit crypto book</b> is the fund's live deploy venue — it trades the 4-edge book every day at real
|
||||
costs. The <b>IBKR account</b> is the US-equity leg, not connected yet. Open either card for its holdings,
|
||||
trades and forward record.
|
||||
costs. The <b>IBKR account</b> is the multi-strat ETF leg —
|
||||
{% if ibkr_view %}live now, trading through UCITS lines from the EU desk (${{ ibkr_rollup.total_nav|money }}
|
||||
across {{ ibkr_view.all_positions|length }} holding{{ 's' if ibkr_view.all_positions|length != 1 }}){% else %}not
|
||||
connected yet{% endif %}. Open either card for its holdings, trades and forward record.
|
||||
</div>
|
||||
<div class="cards" style="grid-template-columns:repeat(auto-fit,minmax(300px,1fr))">
|
||||
|
||||
|
||||
@@ -207,6 +207,11 @@ def test_paper_landing_shows_ibkr_holdings_count_and_behind_plan() -> None:
|
||||
text = _visible_text(c.get("/paper").text)
|
||||
assert "2 holdings" in text
|
||||
assert "0.7% behind plan" in text # exec 3.1% vs sim 3.8% -> 0.7% behind, same math as Task 4
|
||||
# The orientation banner must reflect the LIVE account state — never a stale hardcoded "not connected yet"
|
||||
# while the real account is trading (the exact regression this guards: the banner is data-aware, keyed on
|
||||
# ibkr_view, so it can't drift from the card it sits above).
|
||||
assert "live now, trading through ucits" in text
|
||||
assert "not connected yet" not in text
|
||||
|
||||
|
||||
def test_paper_landing_empty_ibkr_account_shows_clean_empty_state_no_500() -> None:
|
||||
|
||||
Reference in New Issue
Block a user