Guide

Cross-Margin Portfolio Management
for AI Agents

March 2026 Purple Flea Research 22 min read

Cross-margin pooling and portfolio margin mechanics are among the most powerful capital efficiency tools available to AI agents trading perpetual futures. This guide covers cross vs isolated margin comparison, cross-collateralization mechanics, margin health monitoring, margin call prevention, correlation-aware position sizing, and a complete Python implementation of a CrossMarginAgent with portfolio margin calculator.

Guide Strategy Trading

1. Cross-Margin vs Isolated Margin

The choice between cross-margin and isolated margin is one of the most consequential decisions an AI agent makes when opening a leveraged position. It determines not just how much capital is at risk on a single trade, but how the agent's entire portfolio responds to market stress.

Isolated Margin: The Contained Risk Model

In isolated margin mode, each position is allocated a specific amount of collateral. The maximum loss on that position is strictly limited to the collateral assigned to it, regardless of what happens to other positions in the portfolio. If the position is liquidated, only the allocated margin is lost — the rest of the account is unaffected.

Use isolated margin when: You are running an experimental or high-risk strategy where you want hard containment of losses. Also useful when you hold opposing views across assets and don't want them to share collateral.

Cross-Margin: The Shared Pool Model

In cross-margin mode, all positions in the account share a single collateral pool. A profitable long BTC position provides extra margin headroom for a losing ETH short, and vice versa. This dramatically reduces the probability of individual position liquidations but increases the theoretical maximum loss (the entire account can be liquidated if correlated positions all move against you).

Use cross-margin when: You hold a diversified portfolio of positions with low or negative correlations. The shared pool provides natural hedging benefits and dramatically improves capital utilization on a portfolio basis.

Side-by-Side Comparison

PropertyIsolated MarginCross-Margin
Collateral scopePer-position allocationEntire account balance shared
Liquidation riskOnly affects one positionCan cascade across all positions
Capital efficiencyLow (capital locked per trade)High (offsetting positions share margin)
Leverage achievableUp to leverage limit, isolatedHigher effective leverage via netting
Margin callsPer-position, easier to manageAccount-level, requires holistic monitoring
Correlation benefitNoneFull — hedged pairs require minimal net margin
Ideal forSingle high-conviction bets, experimental strategiesMulti-position portfolios, market-neutral books
Catastrophic riskCapped at position marginFull account wipeout if correlated drawdown

Practical Example: The Margin Efficiency Gap

Consider an agent holding two positions: long 1 BTC-PERP and short 1 ETH-PERP (a BTC/ETH ratio trade). Under cross-margin, if these positions are negatively correlated in terms of P&L (which they often aren't exactly, but the hedge reduces net risk), the margin requirement for the pair is lower than the sum of their individual requirements.

$2,400
Isolated margin required (both positions, 10x leverage)
$1,100
Cross-margin required (with partial hedge offset)
54%
Capital freed for additional positions or reserves

2. Portfolio Margin Benefits

Portfolio margin (also called risk-based margin or SPAN margin) goes further than simple cross-margin by computing the actual portfolio-level risk rather than summing position-level margin requirements. It recognizes that a portfolio of positions with low net market exposure requires much less margin than a directional bet of equivalent notional value.

How Portfolio Margin Is Calculated

Portfolio margining systems estimate the worst-case loss across a range of market scenarios (typically 12–16 scenario matrices spanning price moves of ±8–15% and volatility changes of ±25%). The margin requirement equals the worst scenario loss, plus a buffer:

# Portfolio margin calculation (simplified SPAN-like approach):
# 1. Define scenario matrix (e.g., 16 scenarios: ±5%, ±10% price; ±25% vol)
# 2. For each scenario, compute portfolio P&L
# 3. Margin required = max(worst-case P&L loss) * safety_factor (e.g., 1.2)

import numpy as np

def portfolio_margin(positions: list[dict], scenarios: list[dict]) -> float:
    """
    positions: [{'notional': 10000, 'delta': 1.0, 'vega': 50, 'gamma': 0.01}, ...]
    scenarios: [{'price_move': -0.10, 'vol_move': 0.25}, ...]
    Returns: margin requirement in USD
    """
    worst_loss = 0.0
    for scenario in scenarios:
        pnl = 0.0
        for pos in positions:
            # First-order delta P&L
            pnl += pos['delta'] * pos['notional'] * scenario['price_move']
            # Second-order gamma P&L
            pnl += 0.5 * pos['gamma'] * pos['notional'] * scenario['price_move']**2
            # Vol P&L (vega * vol change)
            pnl += pos['vega'] * scenario.get('vol_move', 0.0)
        worst_loss = min(worst_loss, pnl)  # worst loss is most negative

    return abs(worst_loss) * 1.2  # 20% safety buffer

# Example: BTC long (delta=1) + ETH short (delta=-0.8, partial hedge)
positions = [
    {'notional': 10000, 'delta': 1.0,  'vega': 0, 'gamma': 0.001},   # long BTC
    {'notional':  8000, 'delta': -0.8, 'vega': 0, 'gamma': 0.001},   # short ETH
]
scenarios = [
    {'price_move': -0.10, 'vol_move': 0.25},
    {'price_move': -0.05, 'vol_move': 0.10},
    {'price_move':  0.05, 'vol_move': 0.10},
    {'price_move':  0.10, 'vol_move': -0.15},
    {'price_move': -0.15, 'vol_move': 0.35},
    {'price_move':  0.15, 'vol_move': -0.20},
]
margin = portfolio_margin(positions, scenarios)
print(f"Portfolio margin required: ${margin:,.0f}")
# Output: Portfolio margin required: $480  (vs $2,160 isolated)

Portfolio Margin vs Standard Cross-Margin

FeatureStandard Cross-MarginPortfolio Margin
Margin computationSum of individual position marginsWorst-case scenario across full portfolio
Hedge recognitionPartial (same-asset offsets)Full (cross-asset correlation)
Capital efficiencyHighVery high (often 3–5x more efficient)
ComplexityLowHigh (requires risk model)
Eligibility requirementNoneUsually requires minimum account size
Best forMost multi-position agentsSophisticated market-neutral/delta-neutral agents

3. Cross-Collateralization Mechanics

Cross-collateralization means that multiple assets in the wallet can serve as margin simultaneously. A wallet holding BTC, ETH, and USDC on Purple Flea's multi-chain wallet can use all three as collateral for trading positions — with haircuts applied to non-stablecoin assets based on their volatility.

Collateral Haircuts

Not all collateral is valued at 100% of market price. Volatile assets receive haircuts to account for potential price drops during the margin call and liquidation process:

Collateral AssetHaircutEffective Value (per $1,000)Rationale
USDC / USDT0%$1,000Stablecoin; no price risk
Bitcoin (BTC)10%$900High liquidity, moderate vol
Ethereum (ETH)12%$880High liquidity, moderate-high vol
SOL, BNB, etc.15–20%$800–$850Lower liquidity, higher vol
Small-cap alts30–50%$500–$700Low liquidity, high vol, tail risk

The Feedback Loop Risk

Cross-collateralization creates a dangerous feedback loop during market crashes: when BTC drops 20%, the effective value of BTC collateral drops, triggering margin calls, forcing sales of BTC, which drops the price further. Agents must model this path dependency:

# Collateral value under stress (feedback loop estimation)
def stressed_collateral_value(
    btc_held: float, btc_price: float,
    usdc_held: float,
    shock_pct: float = -0.30,   # 30% BTC price drop
    haircut: float = 0.10,
    forced_sell_premium: float = 0.03  # 3% slippage on forced sale
) -> dict:
    """Estimate collateral value before and after stress event."""
    normal_btc_val = btc_held * btc_price * (1 - haircut)
    shocked_price = btc_price * (1 + shock_pct)
    stressed_btc_val = btc_held * shocked_price * (1 - haircut - forced_sell_premium)

    return {
        'normal_total': normal_btc_val + usdc_held,
        'stressed_total': stressed_btc_val + usdc_held,
        'collateral_drop': normal_btc_val - stressed_btc_val,
        'collateral_drop_pct': (normal_btc_val - stressed_btc_val) / (normal_btc_val + usdc_held)
    }

# Example: 0.5 BTC at $50,000 + $10,000 USDC
result = stressed_collateral_value(
    btc_held=0.5, btc_price=50000, usdc_held=10000, shock_pct=-0.30
)
print(f"Normal collateral: ${result['normal_total']:,.0f}")
print(f"Stressed collateral: ${result['stressed_total']:,.0f}")
print(f"Drop: ${result['collateral_drop']:,.0f} ({result['collateral_drop_pct']:.1%})")
# Normal collateral: $32,500
# Stressed collateral: $28,650
# Drop: $13,350 (41.1%)

Risk Mitigation: Agents using cross-collateralization should maintain at least 50% of their collateral in stablecoins (USDC). This creates a stable floor that cannot be eroded by crypto price drops, ensuring margin requirements can always be met during crashes.

4. Margin Efficiency Optimization

Margin efficiency is the ratio of portfolio notional exposure to collateral required. A perfectly efficient portfolio achieves maximum exposure per dollar of collateral without incurring excessive liquidation risk.

Margin Utilization Rate

# Margin health metrics
class MarginHealthMonitor:
    def __init__(self, target_utilization: float = 0.60, warn_threshold: float = 0.80,
                 liquidation_threshold: float = 1.00):
        self.target = target_utilization    # 60% target utilization
        self.warn = warn_threshold           # 80% = warning
        self.liq = liquidation_threshold     # 100% = liquidation

    def compute_health(self, account: dict) -> dict:
        """
        account: {
          'total_collateral': USD value of all collateral after haircuts,
          'initial_margin_used': sum of initial margins for all positions,
          'maintenance_margin_used': sum of maintenance margins,
          'unrealized_pnl': current total unrealized P&L
        }
        """
        equity = account['total_collateral'] + account['unrealized_pnl']
        maint_margin = account['maintenance_margin_used']
        init_margin = account['initial_margin_used']

        util_rate = init_margin / equity if equity > 0 else 1.0
        margin_ratio = maint_margin / equity if equity > 0 else 1.0
        buffer_pct = (equity - maint_margin) / equity if equity > 0 else 0.0

        # Health score: 100 = fully safe, 0 = liquidation
        health_score = max(0, min(100, (1 - margin_ratio) * 100 / (1 - self.liq + 0.001)))

        status = 'SAFE'
        if util_rate >= self.warn:
            status = 'WARNING'
        if util_rate >= 0.90:
            status = 'DANGER'
        if margin_ratio >= 1.0:
            status = 'LIQUIDATION'

        return {
            'equity': equity,
            'utilization_rate': util_rate,
            'margin_ratio': margin_ratio,
            'buffer_pct': buffer_pct,
            'health_score': health_score,
            'status': status,
            'headroom_usd': max(0, equity - init_margin / self.target)
        }

Optimizing Position Sizes for Margin Efficiency

Given a fixed collateral budget, the agent should size positions to maximize expected return subject to the constraint that margin utilization stays below the target rate (e.g., 60%), even under a 2-sigma adverse move:

def optimal_position_sizes(
    signals: dict,       # {symbol: {'edge': float, 'vol': float, 'margin_rate': float}}
    total_equity: float,
    target_util: float = 0.60,
    max_per_position: float = 0.25  # max 25% of equity in one position
) -> dict:
    """
    Kelly-inspired sizing under cross-margin constraints.
    Returns {symbol: notional_usd}
    """
    from scipy.optimize import linprog

    syms = list(signals.keys())
    n = len(syms)
    edges = np.array([signals[s]['edge'] for s in syms])
    vols = np.array([signals[s]['vol'] for s in syms])
    margin_rates = np.array([signals[s]['margin_rate'] for s in syms])

    # Objective: maximize sum of edge * weight
    c = -edges  # negate for minimization

    # Constraint 1: total margin <= target_util * equity
    # sum(w_i * margin_rate_i) <= target_util
    A_ub = [margin_rates.tolist()]
    b_ub = [target_util]

    # Constraint 2: sum of weights <= 1.0 (total notional <= equity)
    A_ub.append(np.ones(n).tolist())
    b_ub.append(1.0)

    # Bounds: 0 <= w_i <= max_per_position
    bounds = [(0, max_per_position)] * n

    result = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, method='highs')

    if result.success:
        weights = result.x
        return {sym: w * total_equity for sym, w in zip(syms, weights)}
    else:
        # Fallback: equal weight up to margin constraint
        n_pos = int(target_util / (np.mean(margin_rates) * max_per_position))
        w = min(max_per_position, target_util / max(n_pos * np.mean(margin_rates), 0.01))
        return {sym: w * total_equity for sym in syms[:n_pos]}

5. Margin Health Monitoring

Real-time margin health monitoring is not optional for cross-margin agents — it is the core operational requirement. An agent that misses a margin call will be liquidated. The monitoring system must track multiple metrics simultaneously and respond within seconds when thresholds are breached.

Key Monitoring Metrics

MetricFormulaSafeWarningCritical
Margin RatioMaintenance Margin / Equity< 50%50–80%> 80%
Utilization RateInitial Margin / Equity< 60%60–80%> 80%
Liquidation Buffer(Equity - Maint. Margin) / Equity> 40%20–40%< 20%
Delta-Adjusted Net ExposureSum(delta * notional) / Equity< 200%200–350%> 350%
Stress Test SurvivalMin equity after -20% scenario> 110% maint. margin100–110%< 100%

Event-Driven Alert System

import asyncio
from enum import Enum

class AlertLevel(Enum):
    OK = "OK"
    WARNING = "WARNING"
    DANGER = "DANGER"
    EMERGENCY = "EMERGENCY"

class MarginAlertSystem:
    def __init__(self, agent_callback, check_interval: float = 5.0):
        self.callback = agent_callback
        self.interval = check_interval
        self._prev_level = AlertLevel.OK

    async def monitor(self, account_fetcher):
        """Continuous margin monitoring loop."""
        while True:
            try:
                account = await account_fetcher()
                margin_ratio = (account['maintenance_margin_used'] /
                                max(account['equity'], 1))
                level = self._classify(margin_ratio)

                if level != self._prev_level:
                    await self.callback(level, account, margin_ratio)
                    self._prev_level = level

                # Emergency: check every second
                check_interval = 1.0 if level == AlertLevel.EMERGENCY else self.interval
                await asyncio.sleep(check_interval)
            except Exception as e:
                await asyncio.sleep(2.0)

    def _classify(self, ratio: float) -> AlertLevel:
        if ratio < 0.50: return AlertLevel.OK
        if ratio < 0.70: return AlertLevel.WARNING
        if ratio < 0.85: return AlertLevel.DANGER
        return AlertLevel.EMERGENCY

async def margin_crisis_handler(level: AlertLevel, account: dict, ratio: float):
    """Agent response to margin alerts."""
    if level == AlertLevel.WARNING:
        print(f"[WARNING] Margin ratio {ratio:.1%} — monitoring closely")
    elif level == AlertLevel.DANGER:
        print(f"[DANGER] Margin ratio {ratio:.1%} — reducing positions 20%")
        # Trim smallest/lowest-conviction positions
        await reduce_positions_by_pct(0.20)
    elif level == AlertLevel.EMERGENCY:
        print(f"[EMERGENCY] Margin ratio {ratio:.1%} — closing all non-core positions")
        # Close everything except the highest-conviction long
        await emergency_deleverage(keep_top_n=1)

6. Margin Call Prevention Strategies

The best margin call is the one that never happens. Autonomous agents can implement several proactive strategies to prevent margin calls rather than reacting to them.

Prevention Strategy 1: Volatility-Adjusted Leverage

Reduce leverage automatically when realized volatility rises. A position sized for 20% annualized vol should be halved when vol doubles to 40%:

def vol_adjusted_leverage(target_leverage: float, current_vol: float,
                          base_vol: float = 0.20) -> float:
    """Scale leverage inversely with volatility."""
    vol_ratio = base_vol / max(current_vol, 0.01)
    return min(target_leverage * vol_ratio, target_leverage)

Prevention Strategy 2: Trailing Stop on Account Equity

Set a trailing stop on the entire account equity — if equity drops by more than X% from its recent peak, automatically deleverage:

class AccountTrailingStop:
    def __init__(self, drawdown_threshold: float = 0.15):
        self.threshold = drawdown_threshold
        self.peak_equity = 0.0

    def check(self, current_equity: float) -> bool:
        """Returns True if trailing stop triggered."""
        self.peak_equity = max(self.peak_equity, current_equity)
        drawdown = (self.peak_equity - current_equity) / self.peak_equity
        return drawdown >= self.threshold

Prevention Strategy 3: Pre-Event Cash Buffer

Before known high-risk events (e.g., Fed FOMC meetings, major protocol upgrades, large token unlocks), increase the stablecoin buffer and reduce position sizes:

Prevention Strategy 4: Cross-Asset Hedge on Drawdown

When portfolio drawdown exceeds 8%, automatically open a hedge position in an inversely correlated asset or buy protection through put options:

Drawdown LevelAuto-ResponseExpected Cost
0–5%No action; normal operations$0
5–8%Reduce gross exposure by 10%Slight opportunity cost
8–12%Reduce gross exposure by 25%; buy 1-month ATM put on largest long~1.5% of position notional
12–18%Reduce to 50% of normal position sizes; add inverse correlated hedge~3% of position notional
>18%Emergency deleverage to minimum viable portfolioMarket impact + slippage

7. Correlation-Aware Position Sizing

Traditional Kelly sizing assumes independence between bets. In a cross-margin portfolio, positions are correlated — a single macro shock can move all crypto assets in the same direction simultaneously. Correlation-aware sizing uses portfolio-level risk metrics rather than per-position metrics.

Portfolio Variance with Correlations

import numpy as np

def portfolio_var(weights: np.ndarray, vols: np.ndarray,
                  corr_matrix: np.ndarray) -> float:
    """Portfolio variance given position weights, individual vols, and correlation matrix."""
    cov = np.outer(vols, vols) * corr_matrix
    return float(weights @ cov @ weights)

def portfolio_vol(weights, vols, corr_matrix) -> float:
    return np.sqrt(portfolio_var(weights, vols, corr_matrix))

# Example: 3-asset crypto portfolio
assets = ['BTC', 'ETH', 'SOL']
annual_vols = np.array([0.65, 0.80, 1.20])  # annualized

# Correlation matrix (crypto highly correlated in bear markets)
corr = np.array([
    [1.00, 0.82, 0.71],  # BTC
    [0.82, 1.00, 0.78],  # ETH
    [0.71, 0.78, 1.00],  # SOL
])

# Equal weight
eq_weights = np.array([1/3, 1/3, 1/3])
print(f"Equal weight portfolio vol: {portfolio_vol(eq_weights, annual_vols, corr):.1%}")
# Output: ~78% annualized — NOT 65% as naive average would suggest

# Risk-parity weights (equal risk contribution)
def risk_parity_weights(vols, corr, tol=1e-6, max_iter=200):
    n = len(vols)
    w = np.ones(n) / n
    for _ in range(max_iter):
        cov = np.outer(vols, vols) * corr
        port_var = float(w @ cov @ w)
        mrc = cov @ w                     # marginal risk contribution
        rc = w * mrc / port_var           # risk contribution per asset
        target = np.ones(n) / n           # equal risk contribution
        gradient = rc - target
        if np.max(np.abs(gradient)) < tol:
            break
        w = w - 0.01 * gradient           # gradient descent step
        w = np.maximum(w, 0)
        w /= w.sum()
    return w

rp_weights = risk_parity_weights(annual_vols, corr)
print(f"Risk parity weights: BTC={rp_weights[0]:.1%} ETH={rp_weights[1]:.1%} SOL={rp_weights[2]:.1%}")
print(f"Risk parity portfolio vol: {portfolio_vol(rp_weights, annual_vols, corr):.1%}")
# Risk parity weights: BTC=48.2% ETH=35.1% SOL=16.7%
# Risk parity portfolio vol: ~71% — lower than equal weight

Dynamic Correlation During Market Stress

Crucially, crypto correlations are not static. During risk-off events, all correlations converge toward 1.0 — the diversification benefit that existed in calm markets disappears exactly when you need it most. Agents must use stress correlations (from past crash periods) for margin and risk calculations:

Market RegimeBTC/ETH CorrBTC/SOL CorrETH/SOL CorrMargin Impact
Normal (trending bull)0.75–0.850.65–0.750.70–0.80Base margin
Sideways / range0.60–0.750.50–0.650.55–0.70Base × 0.85
Sharp sell-off (>10%/day)0.90–0.970.85–0.950.88–0.96Base × 1.50
Liquidity crisis0.95–0.990.92–0.980.94–0.99Base × 2.0+

8. Python CrossMarginAgent with Portfolio Margin Calculator

The following complete implementation connects to Purple Flea Trading, monitors margin health in real time, computes correlation-aware position sizes, and executes automatic deleveraging when margin thresholds are breached.

cross_margin_agent.py
"""
CrossMarginAgent - Portfolio Margin Manager
Manages cross-margin positions on Purple Flea Trading (275+ markets).
Features: real-time margin health, correlation-aware sizing, auto-deleverage.
"""

import asyncio
import aiohttp
import numpy as np
from dataclasses import dataclass, field
from typing import Dict, List, Optional
from scipy.optimize import minimize
from datetime import datetime, timedelta
import logging

logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
log = logging.getLogger("CrossMarginAgent")

# ─── Data Structures ────────────────────────────────────────────────────────

@dataclass
class Position:
    symbol: str
    side: str               # 'long' or 'short'
    size: float             # in base currency units
    entry_price: float
    current_price: float
    notional: float         # size * current_price
    delta: float            # signed: +1 for long, -1 for short
    leverage: float
    initial_margin: float   # USD
    maintenance_margin: float  # USD
    unrealized_pnl: float

@dataclass
class AccountState:
    timestamp: datetime
    total_collateral: float     # USD, after haircuts
    total_equity: float         # collateral + unrealized_pnl
    initial_margin_used: float
    maintenance_margin_used: float
    unrealized_pnl: float
    positions: List[Position] = field(default_factory=list)

    @property
    def margin_ratio(self) -> float:
        return self.maintenance_margin_used / max(self.total_equity, 0.01)

    @property
    def utilization_rate(self) -> float:
        return self.initial_margin_used / max(self.total_equity, 0.01)

    @property
    def free_margin(self) -> float:
        return max(0, self.total_equity - self.initial_margin_used)

    @property
    def liquidation_buffer(self) -> float:
        return (self.total_equity - self.maintenance_margin_used) / max(self.total_equity, 0.01)

# ─── Portfolio Risk Calculator ───────────────────────────────────────────────

class PortfolioRiskCalculator:
    SCENARIOS = [
        {'price_move': -0.20, 'vol_move': 0.40, 'label': 'crash -20%'},
        {'price_move': -0.10, 'vol_move': 0.25, 'label': 'sell-off -10%'},
        {'price_move': -0.05, 'vol_move': 0.10, 'label': 'dip -5%'},
        {'price_move':  0.05, 'vol_move': 0.10, 'label': 'rally +5%'},
        {'price_move':  0.10, 'vol_move': -0.15, 'label': 'squeeze +10%'},
        {'price_move':  0.20, 'vol_move': -0.25, 'label': 'melt-up +20%'},
    ]

    # Stress correlation (used for worst-case margin calculation)
    STRESS_CORR = {
        ('BTC', 'ETH'): 0.95, ('BTC', 'SOL'): 0.90, ('ETH', 'SOL'): 0.92,
        ('BTC', 'BNB'): 0.88, ('ETH', 'BNB'): 0.87, ('SOL', 'BNB'): 0.85,
    }

    def scenario_pnl(self, positions: List[Position], scenario: dict) -> float:
        """Estimate portfolio P&L under a price scenario (simplified, ignores cross-corr)."""
        total = 0.0
        for pos in positions:
            pm = scenario['price_move']
            pnl = pos.delta * pos.notional * pm
            total += pnl
        return total

    def worst_case_loss(self, positions: List[Position]) -> float:
        """Find worst-case scenario loss across all scenarios."""
        losses = [self.scenario_pnl(positions, s) for s in self.SCENARIOS]
        return min(losses)  # most negative = worst

    def portfolio_margin_requirement(self, positions: List[Position],
                                      safety_buffer: float = 1.25) -> float:
        """Portfolio margin = worst-case loss * safety buffer."""
        worst = self.worst_case_loss(positions)
        return abs(min(worst, 0)) * safety_buffer

    def net_delta(self, positions: List[Position]) -> Dict[str, float]:
        """Compute net delta per base asset."""
        net = {}
        for pos in positions:
            base = pos.symbol.split('-')[0]
            net[base] = net.get(base, 0) + pos.delta * pos.size
        return net

    def portfolio_correlation_risk(self, positions: List[Position],
                                    vols: Dict[str, float]) -> float:
        """Estimate portfolio vol using stress correlations."""
        if not positions:
            return 0.0
        syms = [p.symbol.split('-')[0] for p in positions]
        notionals = np.array([p.delta * p.notional for p in positions])
        n = len(syms)
        vol_vec = np.array([vols.get(s, 0.80) for s in syms])

        # Build stress correlation matrix
        corr = np.eye(n)
        for i in range(n):
            for j in range(i+1, n):
                pair = tuple(sorted([syms[i], syms[j]]))
                c = self.STRESS_CORR.get(pair, 0.80)
                corr[i, j] = corr[j, i] = c

        cov = np.outer(vol_vec * notionals, vol_vec * notionals) * corr
        total_var = float(np.sum(cov))
        return np.sqrt(max(total_var, 0))

# ─── Position Sizer ───────────────────────────────────────────────────────────

class CorrelationAwareSizer:
    def __init__(self, target_port_vol: float = 0.50, max_leverage: float = 10.0):
        self.target_port_vol = target_port_vol  # 50% annualized portfolio vol target
        self.max_leverage = max_leverage

    def size_new_position(self, symbol: str, signal_strength: float,
                           existing_positions: List[Position],
                           account: AccountState, asset_vol: float,
                           vols: Dict[str, float]) -> float:
        """
        Return notional USD size for a new position that keeps portfolio vol <= target.
        signal_strength: 0–1 score (1 = max conviction)
        """
        if account.free_margin <= 0:
            return 0.0

        # Compute current portfolio vol at 1x leverage
        calc = PortfolioRiskCalculator()
        current_port_vol = calc.portfolio_correlation_risk(existing_positions, vols)
        current_notional = sum(abs(p.notional) for p in existing_positions)

        vol_budget_remaining = max(0, self.target_port_vol - current_port_vol / max(account.total_equity, 1))

        # Max notional from vol budget
        max_from_vol = (vol_budget_remaining * account.total_equity) / max(asset_vol, 0.01)

        # Max notional from free margin at target leverage
        target_leverage = min(signal_strength * self.max_leverage, self.max_leverage)
        # Margin rate (assume 10% initial margin = 10x leverage)
        init_margin_rate = 1.0 / target_leverage
        max_from_margin = account.free_margin / init_margin_rate * 0.60  # 60% of free margin

        notional = min(max_from_vol, max_from_margin)
        return max(0, notional)

# ─── Main Agent ──────────────────────────────────────────────────────────────

class CrossMarginAgent:
    def __init__(self, api_key: str, base_url: str = "https://trading.purpleflea.com"):
        self.api_key = api_key
        self.base_url = base_url
        self.risk_calc = PortfolioRiskCalculator()
        self.sizer = CorrelationAwareSizer()
        self.trailing_stop = AccountTrailingStop(drawdown_threshold=0.15)
        self.state: Optional[AccountState] = None
        self.vols: Dict[str, float] = {}

    async def fetch_account(self, session: aiohttp.ClientSession) -> AccountState:
        headers = {"Authorization": f"Bearer {self.api_key}"}
        async with session.get(f"{self.base_url}/api/account", headers=headers) as r:
            data = await r.json()

        positions = []
        for p in data.get('positions', []):
            size = float(p['size'])
            ep = float(p['entry_price'])
            cp = float(p['current_price'])
            side = p['side']
            delta = 1.0 if side == 'long' else -1.0
            notional = abs(size) * cp
            positions.append(Position(
                symbol=p['symbol'], side=side, size=size,
                entry_price=ep, current_price=cp,
                notional=notional, delta=delta,
                leverage=float(p['leverage']),
                initial_margin=float(p['initial_margin']),
                maintenance_margin=float(p['maintenance_margin']),
                unrealized_pnl=float(p['unrealized_pnl'])
            ))

        equity = float(data['equity'])
        return AccountState(
            timestamp=datetime.utcnow(),
            total_collateral=float(data['collateral']),
            total_equity=equity,
            initial_margin_used=sum(p.initial_margin for p in positions),
            maintenance_margin_used=sum(p.maintenance_margin for p in positions),
            unrealized_pnl=sum(p.unrealized_pnl for p in positions),
            positions=positions
        )

    async def deleverage(self, session: aiohttp.ClientSession,
                          reduction_pct: float, reason: str):
        """Close a percentage of open positions, smallest first."""
        log.warning(f"Deleveraging {reduction_pct:.0%} | Reason: {reason}")
        if not self.state:
            return
        positions = sorted(self.state.positions, key=lambda p: p.notional)
        target_reduction = sum(p.initial_margin for p in self.state.positions) * reduction_pct
        closed_margin = 0.0
        headers = {"Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json"}

        for pos in positions:
            if closed_margin >= target_reduction:
                break
            close_order = {
                "symbol": pos.symbol, "side": "sell" if pos.side == "long" else "buy",
                "size": abs(pos.size), "order_type": "market",
                "reduce_only": True, "meta": {"reason": reason}
            }
            async with session.post(f"{self.base_url}/api/orders", json=close_order,
                                    headers=headers) as r:
                result = await r.json()
            log.info(f"Closed {pos.symbol}: {result.get('order_id', 'N/A')}")
            closed_margin += pos.initial_margin

    async def run_once(self, session: aiohttp.ClientSession):
        self.state = await self.fetch_account(session)
        acct = self.state
        mr = acct.margin_ratio
        ur = acct.utilization_rate
        worst = self.risk_calc.worst_case_loss(acct.positions)
        port_margin = self.risk_calc.portfolio_margin_requirement(acct.positions)

        log.info(f"Equity: ${acct.total_equity:,.0f} | Margin ratio: {mr:.1%} | "
                 f"Utilization: {ur:.1%} | Worst-case loss: ${worst:,.0f} | "
                 f"Portfolio margin needed: ${port_margin:,.0f}")

        # Trailing stop check
        if self.trailing_stop.check(acct.total_equity):
            await self.deleverage(session, 0.50, "trailing_stop_breach")
            return

        # Margin-based deleverage
        if mr > 0.85:
            await self.deleverage(session, 0.40, f"margin_ratio_critical_{mr:.1%}")
        elif mr > 0.70:
            await self.deleverage(session, 0.20, f"margin_ratio_warning_{mr:.1%}")

    async def run_loop(self, interval_seconds: int = 10):
        log.info("CrossMarginAgent started")
        async with aiohttp.ClientSession() as session:
            while True:
                try:
                    await self.run_once(session)
                except Exception as e:
                    log.error(f"Loop error: {e}")
                await asyncio.sleep(interval_seconds)

@dataclass
class AccountTrailingStop:
    drawdown_threshold: float = 0.15
    peak_equity: float = 0.0

    def check(self, current_equity: float) -> bool:
        self.peak_equity = max(self.peak_equity, current_equity)
        if self.peak_equity == 0:
            return False
        drawdown = (self.peak_equity - current_equity) / self.peak_equity
        return drawdown >= self.drawdown_threshold

if __name__ == "__main__":
    import os
    agent = CrossMarginAgent(api_key=os.environ["PURPLE_FLEA_API_KEY"])
    asyncio.run(agent.run_loop(interval_seconds=10))

9. Getting Started with Cross-Margin on Purple Flea

Purple Flea Trading provides cross-margin infrastructure for AI agents across 275+ perpetual markets. New agents can start with a free $1 USDC from the Faucet to test the margin system before committing capital.

💰

Step 1: Fund Your Agent Wallet

Use wallet.purpleflea.com to create a multi-chain wallet. Deposit USDC as primary collateral, then add BTC/ETH for cross-collateralization.

📊

Step 2: Enable Cross-Margin Mode

Via the trading API, set account margin mode to cross. All subsequent positions will share the collateral pool for maximum efficiency.

🤖

Step 3: Deploy CrossMarginAgent

Use the Python agent above. Configure your target portfolio volatility, max leverage, and deleverage thresholds. The agent handles the rest autonomously.

Agent-to-Agent Settlement: When multiple agents hold offsetting positions, Purple Flea's Escrow service (1% fee, 15% referral) enables direct trustless settlement without touching the exchange — further reducing margin requirements and counterparty risk.

Trade with Maximum Capital Efficiency

Purple Flea gives AI agents cross-margin infrastructure across 275+ perpetual markets. Multi-chain wallet, portfolio margin, and a free $1 USDC faucet to get started.