Add dynamic agent registry and options mandate

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.
This commit is contained in:
Aryan Singh committed 2026-07-07 22:00:20 -05:00
1 parent b3821abd69
commit e1ac514f30
36 files changed
+1476 -294

No files matched your search

+42 -4
View File
@@ -16,6 +16,19 @@ from .models import ModelProfile, resolve_profile
REPO_ROOT = Path(__file__).resolve().parent.parent
@lru_cache
def _dotenv_extras() -> dict[str, str]:
"""Raw .env entries (uppercased keys). pydantic-settings drops keys that have no
Settings field (extra='ignore') and does NOT export them to os.environ — but
dashboard-added agents need their ALPACA_<ID>_KEY/SECRET found anyway."""
try:
from dotenv import dotenv_values
return {k.upper(): (v or "") for k, v in dotenv_values(REPO_ROOT / ".env").items()}
except Exception:
return {}
class Settings(BaseSettings):
model_config = SettingsConfigDict(
env_file=REPO_ROOT / ".env",
@@ -34,6 +47,8 @@ class Settings(BaseSettings):
alpaca_warren_lite_secret: str = ""
alpaca_atlas_key: str = ""
alpaca_atlas_secret: str = ""
alpaca_maverick_key: str = ""
alpaca_maverick_secret: str = ""
# --- Finnhub ---
finnhub_api_key: str = ""
@@ -133,16 +148,39 @@ class Settings(BaseSettings):
return self.active_profile.temperature
def alpaca_creds(self, persona_id: str) -> tuple[str, str]:
"""(key, secret) for a persona's paper account, falling back to the single-account keys."""
key = getattr(self, f"alpaca_{persona_id}_key", "") or ""
secret = getattr(self, f"alpaca_{persona_id}_secret", "") or ""
"""(key, secret) for a persona's paper account, falling back to the single-account keys.
Dashboard-added agents have no Settings field, so ``ALPACA_<ID>_KEY/SECRET`` is
also looked up in the process environment and the raw .env file.
"""
import os
pid = persona_id.strip().lower()
env_key, env_secret = f"ALPACA_{pid.upper()}_KEY", f"ALPACA_{pid.upper()}_SECRET"
extras = _dotenv_extras()
key = (
getattr(self, f"alpaca_{pid}_key", "")
or os.environ.get(env_key, "")
or extras.get(env_key, "")
)
secret = (
getattr(self, f"alpaca_{pid}_secret", "")
or os.environ.get(env_secret, "")
or extras.get(env_secret, "")
)
if key and secret:
return key, secret
return self.alpaca_api_key, self.alpaca_secret_key
def alpaca_data_creds(self) -> tuple[str, str]:
"""Any valid creds — Alpaca market data is account-agnostic."""
for pid in ("nova", "warren_lite", "atlas"):
try:
from config.agent_registry import load_personas # lazy: registry imports settings
ids = [p.id for p in load_personas(include_disabled=True)]
except Exception:
ids = ["nova", "warren_lite", "atlas", "maverick"]
for pid in ids:
key, secret = self.alpaca_creds(pid)
if key and secret:
return key, secret