"""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"))