feat(web): Streamlit dashboard (per-account books, theses, orders, logs) + CLI report
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016DvdRZkyCy9NJpHYDdPqmS
This commit is contained in:
1 parent
709870b1d5
commit
05a8457067
3 files changed
+226
No files matched your search
@@ -0,0 +1,69 @@
|
||||
"""ENTRYPOINT #3 — quick CLI summary of holdings, PnL, orders, and last night's theses.
|
||||
The dashboard covers this visually; this is a terminal-friendly glance.
|
||||
|
||||
uv run python -m reporting.report
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import sqlite3
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
||||
|
||||
from config.settings import get_settings # noqa: E402
|
||||
|
||||
|
||||
def main() -> int:
|
||||
s = get_settings()
|
||||
print("=== Overnight Trading Agent — report ===\n")
|
||||
|
||||
if s.db_path.exists():
|
||||
con = sqlite3.connect(str(s.db_path))
|
||||
con.row_factory = sqlite3.Row
|
||||
snap = con.execute("SELECT * FROM snapshots ORDER BY ts DESC LIMIT 1").fetchone()
|
||||
if snap:
|
||||
print(f"Equity ${snap['equity']:,.2f} | cash ${snap['cash']:,.2f} | "
|
||||
f"buying power ${snap['buying_power']:,.2f}")
|
||||
print(f" (as of {snap['ts']})")
|
||||
positions = con.execute(
|
||||
"SELECT * FROM positions ORDER BY market_value DESC"
|
||||
).fetchall()
|
||||
if positions:
|
||||
print("\nHoldings:")
|
||||
for p in positions:
|
||||
print(f" {p['symbol']:6} qty {p['qty']:>6} @ {p['avg_entry']:>8} "
|
||||
f"→ mv {p['market_value']:>10} | uPnL {p['unrealized_pl']:>8}")
|
||||
orders = con.execute("SELECT * FROM orders ORDER BY submitted_at DESC LIMIT 10").fetchall()
|
||||
if orders:
|
||||
print("\nRecent orders:")
|
||||
for o in orders:
|
||||
print(f" {o['trade_date']} {o['agent']:12} {o['ticker']:6} x{o['qty']:<4} "
|
||||
f"{o['status']}")
|
||||
con.close()
|
||||
else:
|
||||
print("No state DB yet — run execution once to populate it.")
|
||||
|
||||
dates = (
|
||||
sorted((p.name for p in s.output_dir.iterdir() if p.is_dir()), reverse=True)
|
||||
if s.output_dir.exists()
|
||||
else []
|
||||
)
|
||||
if dates:
|
||||
d = dates[0]
|
||||
print(f"\nLatest theses ({d}):")
|
||||
for f in sorted((s.output_dir / d).glob("*.json")):
|
||||
if f.name.startswith("_"):
|
||||
continue
|
||||
j = json.loads(f.read_text())
|
||||
picks = ", ".join(f"{p['ticker']}({p['conviction']:.2f})" for p in j["picks"])
|
||||
print(f" {j['agent_name']:35} [{j['status']:8}] {picks or '(no picks)'}")
|
||||
else:
|
||||
print("\nNo nightly output yet.")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in new issue
Block a user