"""get_portfolio — persona-scoped. Each agent sees ONLY its own paper account's holdings (written by execution/reconcile.py, keyed by persona id), so picks build on that book. Built per-persona via make_get_portfolio() in tools/build_toolset. """ from __future__ import annotations import json from langchain_core.tools import tool from config.settings import get_settings from state import db as statedb def make_get_portfolio(account: str): """Return a get_portfolio tool bound to one account (persona id).""" @tool def get_portfolio() -> str: """Return YOUR current paper-account holdings, unrealized PnL, and cash. Use this BEFORE proposing trades so you account for what you already own. """ path = get_settings().db_path if not path.exists(): return "No portfolio data yet (fresh account, no trades recorded)." try: con = statedb.connect(path) snap = statedb.latest_snapshot(con, account or None) con.close() except Exception as e: return f"No portfolio data yet ({e})." if snap is None: return "No holdings yet (fresh account, all cash)." lines = [ f"As of {snap['ts']}: equity=${snap['equity']:,.2f}, " f"cash=${snap['cash']:,.2f}, buying_power=${snap['buying_power']:,.2f}" ] try: positions = json.loads(snap["positions_json"] or "[]") except Exception: positions = [] if not positions: lines.append("Holdings: none (all cash).") else: lines.append("Holdings:") for p in positions: lines.append( f" {p.get('symbol')}: {p.get('qty')} @ {p.get('avg_entry')} " f"-> mv={p.get('market_value')}, unrealized_pl={p.get('unrealized_pl')}" ) return "\n".join(lines) return get_portfolio