Tradier stack emitted account/positions/orders events hardcoded as broker_name=alpaca_paper while the executor filters snapshots by client.broker_name (tradier) — position cap and re-entry block read zero positions, and the dashboard showed 'Alpaca Paper' over real Tradier values. Mode label now defaults from BROKER/TRADIER_ENV (tradier_live) instead of alpaca_paper. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
62 lines
2.6 KiB
Python
62 lines
2.6 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")
|
|
from trading_mode import TradingModeSettings
|
|
mode = TradingModeSettings.from_env().execution_mode
|
|
# 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()
|