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.
46 lines
1.6 KiB
Python
46 lines
1.6 KiB
Python
"""run_python — the calculator. Agents call this for ALL arithmetic instead of doing
|
|
mental math (LLMs are unreliable at it). Executes in an isolated subprocess with a hard
|
|
timeout and a **scrubbed environment** (no account secrets), so model-generated code can't
|
|
read API keys. For heavier data/analysis work, agents use run_analysis (tools/sandbox.py).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
|
|
from langchain_core.tools import tool
|
|
|
|
_TIMEOUT_S = 5
|
|
|
|
|
|
@tool
|
|
def run_python(code: str) -> str:
|
|
"""Execute a short Python snippet for EXACT math and return its stdout.
|
|
|
|
Use for ANY arithmetic: position sizing, percentages, P/E, returns, averages, ratios,
|
|
option breakevens. STANDARD LIBRARY ONLY (`math`, `statistics`) — NO pandas, numpy, or
|
|
yfinance here; for data analysis use run_analysis instead. You MUST print() the result.
|
|
Example: `print(round(1000 / 23.55, 4))`. No access to secrets; 5s limit.
|
|
"""
|
|
from tools.sandbox import scrubbed_env
|
|
|
|
try:
|
|
proc = subprocess.run(
|
|
[sys.executable, "-I", "-c", code],
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=_TIMEOUT_S,
|
|
env=scrubbed_env(tempfile.gettempdir()),
|
|
)
|
|
except subprocess.TimeoutExpired:
|
|
return f"ERROR: execution timed out ({_TIMEOUT_S}s limit)."
|
|
except Exception as e: # pragma: no cover - defensive
|
|
return f"ERROR: {type(e).__name__}: {e}"
|
|
|
|
if proc.returncode != 0:
|
|
return f"ERROR: {proc.stderr.strip() or 'non-zero exit'}"
|
|
out = proc.stdout.strip()
|
|
return out or "(no output — did you forget to print()?)"
|