feat: Enhance dashboard with live overview panel and control features
This commit is contained in:
+102
-2
@@ -84,11 +84,46 @@ def _seed_metrics_data(app) -> None:
|
||||
started,
|
||||
],
|
||||
)
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT INTO portfolio_snapshots (
|
||||
snapshot_at,
|
||||
balances,
|
||||
total_value_usd
|
||||
) VALUES (?, ?, ?)
|
||||
""",
|
||||
[started, '{"USD": 1000.0, "BTC": 0.25}', 1250.0],
|
||||
)
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT INTO trades (
|
||||
trade_ref,
|
||||
started_at,
|
||||
finished_at,
|
||||
status,
|
||||
realized_pnl,
|
||||
estimated_pnl,
|
||||
capital_used,
|
||||
cycle,
|
||||
leg_count
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
[
|
||||
"trade-open",
|
||||
started,
|
||||
None,
|
||||
"open",
|
||||
None,
|
||||
5.0,
|
||||
50.0,
|
||||
"USD->BTC->ETH->USD",
|
||||
3,
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
async def test_dashboard_page_and_fragment_and_sse(tmp_path) -> None:
|
||||
app = create_app(
|
||||
Settings(_env_file=None, DUCKDB_PATH=tmp_path / "dash.duckdb"))
|
||||
app = create_app(Settings(DUCKDB_PATH=tmp_path / "dash.duckdb"))
|
||||
_seed_metrics_data(app)
|
||||
|
||||
transport = httpx.ASGITransport(app=app)
|
||||
@@ -96,10 +131,14 @@ async def test_dashboard_page_and_fragment_and_sse(tmp_path) -> None:
|
||||
page = await client.get("/")
|
||||
fragment = await client.get("/dashboard/fragment/metrics")
|
||||
stream = await client.get("/dashboard/stream/metrics")
|
||||
overview = await client.get("/dashboard/fragment/overview")
|
||||
overview_stream = await client.get("/dashboard/stream/overview")
|
||||
controls = await client.get("/dashboard/fragment/controls")
|
||||
|
||||
assert page.status_code == 200
|
||||
assert "EventSource" in page.text
|
||||
assert 'hx-get="/dashboard/fragment/metrics"' in page.text
|
||||
assert 'hx-get="/dashboard/fragment/controls"' in page.text
|
||||
|
||||
assert fragment.status_code == 200
|
||||
assert "Realized P&L" in fragment.text
|
||||
@@ -110,3 +149,64 @@ async def test_dashboard_page_and_fragment_and_sse(tmp_path) -> None:
|
||||
assert stream.headers["content-type"].startswith("text/event-stream")
|
||||
assert "event: metrics" in stream.text
|
||||
assert "Realized P&L" in stream.text
|
||||
|
||||
assert overview.status_code == 200
|
||||
assert "live" in overview.text
|
||||
assert "Balances Snapshot" in overview.text
|
||||
assert "Open Trades" in overview.text
|
||||
assert "Opportunity Feed" in overview.text
|
||||
assert "1250.00 USD" in overview.text
|
||||
assert "trade-open" in overview.text
|
||||
|
||||
assert overview_stream.status_code == 200
|
||||
assert overview_stream.headers["content-type"].startswith(
|
||||
"text/event-stream")
|
||||
assert "event: overview" in overview_stream.text
|
||||
assert "trade-open" in overview_stream.text
|
||||
|
||||
assert controls.status_code == 200
|
||||
assert "Runtime Status" in controls.text
|
||||
assert ">running<" in controls.text
|
||||
assert "Paper trading mode" in controls.text
|
||||
assert "Trade capital USD" in controls.text
|
||||
|
||||
|
||||
async def test_dashboard_controls_update_runtime_state_and_config(tmp_path) -> None:
|
||||
app = create_app(Settings(DUCKDB_PATH=tmp_path / "controls.duckdb"))
|
||||
|
||||
transport = httpx.ASGITransport(app=app)
|
||||
async with httpx.AsyncClient(transport=transport, base_url="http://test") as client:
|
||||
stop_response = await client.post("/dashboard/control/stop")
|
||||
start_response = await client.post("/dashboard/control/start")
|
||||
kill_response = await client.post(
|
||||
"/dashboard/control/kill-switch",
|
||||
data={"reason": "manual"},
|
||||
)
|
||||
config_response = await client.post(
|
||||
"/dashboard/control/config",
|
||||
data={
|
||||
"trade_capital_usd": "250.50",
|
||||
"max_trade_capital_usd": "300.00",
|
||||
"max_concurrent_trades": "4",
|
||||
"paper_trading_mode": "on",
|
||||
},
|
||||
)
|
||||
|
||||
assert stop_response.status_code == 200
|
||||
assert ">stopped<" in stop_response.text
|
||||
|
||||
assert start_response.status_code == 200
|
||||
assert ">running<" in start_response.text
|
||||
|
||||
assert kill_response.status_code == 200
|
||||
assert ">active<" in kill_response.text
|
||||
assert "manual" in kill_response.text
|
||||
|
||||
assert config_response.status_code == 200
|
||||
assert "250.50 USD" in config_response.text
|
||||
assert "300.00 USD" in config_response.text
|
||||
assert "4" in config_response.text
|
||||
assert app.state.settings.trade_capital_usd == 250.5
|
||||
assert app.state.settings.max_trade_capital_usd == 300.0
|
||||
assert app.state.settings.max_concurrent_trades == 4
|
||||
assert app.state.settings.paper_trading_mode is True
|
||||
|
||||
Reference in New Issue
Block a user