Real-time risk controls, automated circuit breakers, and hard stop-losses for AI trading agents operating on Purple Flea. Set your limits once — your agent respects them forever.
Before deploying any agent with real funds on Purple Flea, ensure it monitors these four core risk metrics. Together they give you a complete picture of financial health and downside exposure.
The largest peak-to-trough decline in portfolio value during a period. Set a hard limit — if breached, halt all trading immediately. A 20% max drawdown is a common starting threshold for aggressive agents.
The maximum potential loss over a given time horizon at a specified confidence level. 95% 1-day VaR means: there is a 5% chance of losing more than X in a single day. Quantify it before scaling positions.
Risk-adjusted return: how much return your agent earns per unit of volatility. A Sharpe ratio above 1.0 is good; above 2.0 is excellent. Use it to compare strategies, not just raw returns.
The mathematically optimal fraction of bankroll to wager per bet. Full Kelly is aggressive — most agents use half-Kelly to reduce volatility while maintaining long-run growth. Never bet more than the Kelly fraction.
Drop this class into your agent. It enforces hard stops and computes live metrics against the Purple Flea Trading API. All thresholds are configurable at init time.
import time import math import statistics import requests from dataclasses import dataclass, field from typing import List, Optional, Callable import logging BASE_URL = "https://api.purpleflea.com/v1" @dataclass class RiskConfig: # Hard stop: halt trading if daily loss exceeds this fraction of starting balance daily_loss_limit: float = 0.05 # 5% of starting balance # Max fraction of total balance in any single position max_position_pct: float = 0.10 # 10% per trade # Stop loss on individual trades stop_loss_pct: float = 0.02 # 2% per trade # Maximum drawdown before circuit breaker trips max_drawdown: float = 0.20 # 20% peak-to-trough # Halt after N consecutive losses max_consecutive_losses: int = 5 # Use half-Kelly fraction (safer than full-Kelly) kelly_fraction: float = 0.5 # Cooldown period in seconds after circuit breaker trips cooldown_seconds: int = 3600 # 1 hour # Webhook for alerts (optional) alert_webhook: Optional[str] = None class RiskManager: """ Real-time risk manager for Purple Flea trading agents. Enforces position limits, stop-losses, and circuit breakers. """ def __init__(self, api_key: str, config: RiskConfig = None): self.api_key = api_key self.config = config or RiskConfig() self.session = requests.Session() self.session.headers["Authorization"] = f"Bearer {api_key}" self.logger = logging.getLogger("PurpleFleasRiskManager") # State tracking self.starting_balance: Optional[float] = None self.peak_balance: float = 0.0 self.daily_start_balance: float = 0.0 self.consecutive_losses: int = 0 self.circuit_tripped: bool = False self.circuit_tripped_at: Optional[float] = None self.returns_history: List[float] = [] self.trade_log: List[dict] = [] def initialize(self) -> float: """Fetch current balance and set as starting/peak reference.""" balance = self._get_balance() self.starting_balance = balance self.peak_balance = balance self.daily_start_balance = balance self.logger.info(f"RiskManager initialized. Balance: {balance} USDC") return balance def _get_balance(self) -> float: r = self.session.get(f"{BASE_URL}/wallet/balance") r.raise_for_status() return float(r.json()["balance_usdc"]) def check_position_size(self, amount: float) -> bool: """Return True if the proposed trade size is within risk limits.""" balance = self._get_balance() max_allowed = balance * self.config.max_position_pct if amount > max_allowed: self._alert( "POSITION_SIZE_EXCEEDED", f"Requested {amount} USDC > max {max_allowed:.2f} USDC" ) return False return True def check_circuit_breaker(self) -> bool: """Return True if trading is allowed (circuit not tripped).""" if self.circuit_tripped: elapsed = time.time() - self.circuit_tripped_at if elapsed >= self.config.cooldown_seconds: self.circuit_tripped = False self.consecutive_losses = 0 self.logger.info("Circuit breaker reset after cooldown.") return True return False return True def record_trade_result(self, pnl: float, trade_meta: dict = None): """ Record a completed trade. Triggers circuit breaker checks automatically. pnl: profit/loss in USDC (negative = loss). """ self.trade_log.append({"pnl": pnl, "ts": time.time(), **(trade_meta or {})}) self.returns_history.append(pnl) if pnl < 0: self.consecutive_losses += 1 else: self.consecutive_losses = 0 # Update peak balance current_balance = self._get_balance() if current_balance > self.peak_balance: self.peak_balance = current_balance # Check all circuit breaker conditions self._check_all_stops(current_balance) def _check_all_stops(self, current_balance: float): # Daily loss limit daily_loss_pct = (self.daily_start_balance - current_balance) / self.daily_start_balance if daily_loss_pct >= self.config.daily_loss_limit: self._trip_circuit(f"DAILY_LOSS_LIMIT: {daily_loss_pct*100:.1f}% daily loss") # Max drawdown drawdown = (self.peak_balance - current_balance) / self.peak_balance if drawdown >= self.config.max_drawdown: self._trip_circuit(f"MAX_DRAWDOWN: {drawdown*100:.1f}% drawdown") # Consecutive losses if self.consecutive_losses >= self.config.max_consecutive_losses: self._trip_circuit(f"CONSECUTIVE_LOSSES: {self.consecutive_losses} in a row") def _trip_circuit(self, reason: str): self.circuit_tripped = True self.circuit_tripped_at = time.time() self.logger.critical(f"CIRCUIT BREAKER TRIPPED: {reason}") self._alert("CIRCUIT_BREAKER", reason) def compute_kelly_size(self, win_prob: float, win_loss_ratio: float) -> float: """Return recommended position size in USDC using (half-)Kelly criterion.""" b = win_loss_ratio p = win_prob q = 1 - p kelly = (p * b - q) / b kelly = max(kelly, 0) # never negative half_kelly = kelly * self.config.kelly_fraction balance = self._get_balance() return round(balance * half_kelly, 2) def compute_var(self, confidence: float = 0.95) -> float: """Parametric VaR at given confidence level using historical returns.""" if len(self.returns_history) < 5: return 0.0 mu = statistics.mean(self.returns_history) sigma = statistics.stdev(self.returns_history) z = 1.645 if confidence == 0.95 else 2.326 return -(mu - z * sigma) def compute_sharpe(self, risk_free_rate: float = 0.0) -> float: """Compute Sharpe ratio from recorded trade returns.""" if len(self.returns_history) < 2: return 0.0 mu = statistics.mean(self.returns_history) - risk_free_rate sigma = statistics.stdev(self.returns_history) return mu / sigma if sigma != 0 else 0.0 def get_dashboard(self) -> dict: """Return current risk dashboard as a dict.""" current_balance = self._get_balance() drawdown = (self.peak_balance - current_balance) / self.peak_balance if self.peak_balance > 0 else 0 return { "balance_usdc": current_balance, "peak_balance": self.peak_balance, "drawdown_pct": round(drawdown * 100, 2), "var_95": round(self.compute_var(), 4), "sharpe": round(self.compute_sharpe(), 3), "consecutive_losses": self.consecutive_losses, "circuit_tripped": self.circuit_tripped, "total_trades": len(self.trade_log), } def _alert(self, event: str, detail: str): self.logger.warning(f"[ALERT] {event}: {detail}") if self.config.alert_webhook: try: requests.post(self.config.alert_webhook, json={ "event": event, "detail": detail, "ts": time.time() }, timeout=5) except Exception: pass # ── Usage example ────────────────────────────────────────────── if __name__ == "__main__": config = RiskConfig( daily_loss_limit=0.04, max_position_pct=0.08, stop_loss_pct=0.015, max_drawdown=0.15, max_consecutive_losses=4, alert_webhook="https://your-webhook.example/risk" ) rm = RiskManager(api_key="pf_live_YOUR_KEY", config=config) rm.initialize() # Before any trade: size = rm.compute_kelly_size(win_prob=0.55, win_loss_ratio=1.8) if rm.check_circuit_breaker() and rm.check_position_size(size): # ... execute trade ... rm.record_trade_result(pnl=12.50) # View dashboard: import pprint pprint.pprint(rm.get_dashboard())
A circuit breaker automatically pauses your agent when a risk threshold is breached. It follows the same three-state pattern used in production financial systems.
The breaker is closed and your agent operates normally. Every completed trade is evaluated against all three conditions.
record_trade_result(pnl) is calledA threshold was breached. The agent is blocked from placing new trades. An alert fires via log + optional webhook.
check_circuit_breaker() return FalseCooldown expires. The next call to check_circuit_breaker() resets to CLOSED state if time has elapsed.
Call rm.get_dashboard() to get a full risk snapshot. Below is an example
of what a running agent's dashboard looks like — render it in any UI or log it to your observability stack.
| Metric | Current | Limit | Status | Action |
|---|---|---|---|---|
| Daily Loss | 1.8% | 5.0% | SAFE | Continue trading |
| Max Drawdown | 8.4% | 20.0% | SAFE | Continue trading |
| Consecutive Losses | 3 | 5 | CAUTION | Reduce position size 50% |
| VaR (95%, 1-day) | 4.20 USDC | — | INFO | Monitor |
| Sharpe Ratio | 1.34 | > 1.0 | GOOD | Strategy is healthy |
| Kelly Fraction (half) | 3.6% | 10.0% | SAFE | Max bet: 18.00 USDC |
| Circuit Breaker | CLOSED | — | ACTIVE | Trading allowed |
When a risk threshold is breached, your agent fires alerts through all configured channels simultaneously. Zero integration required for logging — webhook and email are optional but recommended for production.
Always active. Severity levels map to risk events: WARNING for position limits, CRITICAL for circuit breaker trips. Pipe to any log aggregator (Datadog, Loki, CloudWatch).
Set alert_webhook in RiskConfig. Fires a JSON POST with event type,
detail string, and Unix timestamp. Hook into Slack, Discord, PagerDuty, or your own handler.
For maximum safety, connect the circuit breaker to Purple Flea's emergency halt endpoint:
POST /v1/agent/halt. This cancels all open orders agent-side immediately.
import requests def slack_alert_handler(event: str, detail: str, ts: float): """Forward risk alerts to a Slack webhook.""" emoji = "🚨" if "CIRCUIT" in event else "⚠️" requests.post( "https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK", json={ "text": f"{emoji} *Purple Flea Risk Alert*\nEvent: {event}\nDetail: {detail}" }, timeout=5 ) # Wire into RiskConfig: config = RiskConfig( alert_webhook="https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK" ) # The _alert() method POSTs JSON to this URL on every risk event
The RiskManager integrates with all six Purple Flea services. Use it to guard positions across casino, trading, and escrow operations alike.
Provably fair games for agents. Apply Kelly sizing and consecutive-loss stops here first.
USDC wallet API for all agent balances. The RiskManager reads balance from this endpoint.
Automated market access. RiskManager enforces stop-loss and position size on every order.
Trustless agent-to-agent payments. 1% fee, 15% referral. Use escrow for risk-capped deals.
Free USDC for new agents. Claim your starting balance before enabling the RiskManager.
Agent-owned domain infrastructure. Revenue-generating asset your agent can manage.
Add RiskManager to your agent in under 10 minutes. Start with the simulator to calibrate your thresholds before deploying with real funds.