Files
momentum-system/dashboard_api.py
T
momentum devandClaude Fable 5 21687c97ff operator-plane: wire research reader into standalone dashboard (sparklines)
DashboardState.bars() short-circuits to [] unless a research_con is attached; the
embedded orchestrator passed one but the standalone dashboard_api.py never built
it, so production sparklines were permanently dead. The missing wire was purely
the entrypoint (DATABASE_URL already reaches the market schema) — now it opens
open_research_db("market") read-only and passes it, degrading to None (charts
show "no research db") if it can't connect. Verified live: bars() returns real
minute rows through the standalone reader.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 00:21:37 -04:00

61 lines
2.5 KiB
Python

"""Run the dashboard API + UI standalone (read-only against the event DB).
python dashboard_api.py
# open http://127.0.0.1:8010
Env: DASHBOARD_HOST, DASHBOARD_PORT, WATCHER_EVENT_DB_PATH,
TRADING_EXECUTION_MODE. For approve/reject/exit buttons to act, run the full
orchestrator (run_live_paper.py) instead, which attaches the execution
service to this same server.
"""
from __future__ import annotations
import os
from dotenv import load_dotenv
# Load .env so DATABASE_URL resolves to the configured Postgres (e.g.
# 192.168.1.5:5432) — the event store reads it. Without this the standalone
# dashboard fell back to the legacy local DuckDB path and showed nothing.
load_dotenv()
from runtime.env_overrides import load as _load_overrides
_load_overrides() # admin-panel overrides beat compose env
from api.main import DashboardState, create_server # noqa: E402
def main() -> None:
host = os.environ.get("DASHBOARD_HOST", "0.0.0.0")
port = int(os.environ.get("DASHBOARD_PORT", "8765"))
# The datastore is Postgres (DATABASE_URL); the event store reads it directly.
# WATCHER_EVENT_DB_PATH is a legacy fallback only (the path is ignored).
db_path = os.environ.get("DATABASE_URL") or os.environ.get(
"WATCHER_EVENT_DB_PATH", "momentum")
mode = os.environ.get("TRADING_EXECUTION_MODE", "alpaca_paper")
# Sparklines: the standalone dashboard needs the SAME research-DB reader the
# embedded (orchestrator) mode passes — without it DashboardState.bars() short-
# circuits to []. The missing wire was purely that this entrypoint never built
# one (DATABASE_URL already reaches the market schema). Read-only use; degrade
# to None (charts show "no research db") if the connection can't be opened.
research_con = None
try:
from research.multi_schema import open_research_db
research_con = open_research_db("market")
except Exception as exc: # noqa: BLE001
print(f"[dashboard] research DB unavailable, sparklines disabled: {exc}")
state = DashboardState(db_path, execution_service=None, execution_mode=mode,
research_con=research_con)
server = create_server(state, host, port)
db_disp = db_path.split("@")[-1] if "@" in db_path else db_path
print(f"[dashboard] serving http://{host}:{port} (event db: {db_disp}, mode: {mode})")
print("[dashboard] read-only mode — run run_live_paper.py for live actions")
try:
server.serve_forever()
except KeyboardInterrupt:
print("\n[dashboard] stopped")
if __name__ == "__main__":
main()