Introduces a dashboard-managed agent registry (`state/agents.json`) used by nightly research and market-open execution, including support for custom/disabled agents and per-agent time budgets. Expands personas to four agents (adds Maverick), shifts pick targets to include mandatory options, and updates prompts/finalization nudges to enforce option research without hard-failing runs. Strengthens execution safety by adding option-specific guardrails (conviction floor, premium cap, reserved option slot through trade/day/buying-power caps), validates OCC option symbols in schemas, and normalizes invalid option direction cases. The dashboard was refactored into modular views, adds an Agents admin tab, and improves live portfolio visibility with Alpaca-first data and snapshot fallback.
39 lines
1.7 KiB
Python
39 lines
1.7 KiB
Python
"""Portfolio tab: per-account positions (options rendered readably) + equity curves."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pandas as pd
|
|
import streamlit as st
|
|
|
|
from web.views.common import describe_symbol, query_df
|
|
|
|
|
|
def render(latest: pd.DataFrame, live_pos: pd.DataFrame | None = None) -> None:
|
|
"""Per-account positions. ``live_pos`` (from Alpaca, via common.live_overview) when
|
|
online; falls back to the reconciled DB positions table when None."""
|
|
if latest.empty:
|
|
st.info("No positions recorded yet — run execution once Alpaca keys are set.")
|
|
return
|
|
for _, acc in latest.iterrows():
|
|
st.subheader(f"{acc['account']} · ${acc['equity']:,.0f} equity · ${acc['cash']:,.0f} cash")
|
|
if live_pos is not None:
|
|
p = live_pos[live_pos["account"] == acc["account"]] if not live_pos.empty else live_pos
|
|
p = p.drop(columns=["account"]).sort_values("market_value", ascending=False) if not p.empty else p
|
|
else:
|
|
p = query_df(
|
|
"SELECT symbol, qty, avg_entry, market_value, unrealized_pl FROM positions "
|
|
"WHERE account = ? ORDER BY market_value DESC",
|
|
(acc["account"],),
|
|
)
|
|
if p.empty:
|
|
st.caption("all cash — no open positions")
|
|
else:
|
|
p = p.copy()
|
|
p.insert(1, "instrument", p["symbol"].map(describe_symbol))
|
|
st.dataframe(p, use_container_width=True, hide_index=True)
|
|
equity = query_df("SELECT ts, account, equity FROM snapshots ORDER BY ts")
|
|
if len(equity) > 1:
|
|
st.subheader("Equity curves")
|
|
equity["ts"] = pd.to_datetime(equity["ts"])
|
|
st.line_chart(equity.pivot_table(index="ts", columns="account", values="equity"))
|