01Margin Mechanics and Maintenance Requirements
Leverage is borrowed capital — and borrowed capital comes with conditions. When you open a leveraged position, the exchange requires you to lock up a portion of the notional value as initial margin. As your position moves against you, the exchange monitors your maintenance margin: the minimum equity needed to keep the position open. Fall below it and the exchange liquidates you, forcibly closing your position to protect their loan.
For AI agents operating on leveraged markets, margin is not an abstraction. It is a hard constraint that must be modeled explicitly. An agent that ignores margin ratios will eventually discover them the hard way — through a forced liquidation at the worst possible moment, typically during a volatility spike when spreads are wide and slippage is maximum.
Initial vs Maintenance Margin
The two critical thresholds are:
- Initial Margin (IM): Required to open a position. Typically 1/leverage. At 10x leverage, IM = 10% of notional.
- Maintenance Margin (MM): Minimum to keep position open. Usually 50-75% of IM. Below this, liquidation is triggered.
- Margin Ratio: Current equity / total notional. Real-time health indicator.
Agent Risk: Many agents treat margin as a one-time check at position open. In reality, funding rates, unrealized PnL fluctuations, and fee accrual continuously erode effective margin. Your agent must poll margin health on every tick or at minimum every 30 seconds.
Cross vs Isolated Margin
Margin mode determines how losses are socialized across your portfolio:
Cross margin pools all available balance against all open positions. A winning position can backstop a losing one — but a catastrophic loss on one position can drain your entire account. For agents running multiple uncorrelated strategies, cross margin creates hidden tail risk.
Isolated margin caps each position's loss at its allocated margin. Liquidation on one trade cannot spill into others. The tradeoff: smaller effective buffer per position, which means tighter liquidation distances.
| Leverage | Initial Margin | Maintenance Margin | Buffer to Liquidation | Risk Level |
|---|---|---|---|---|
| 2x | 50% | 25% | 25% | Low |
| 5x | 20% | 10% | 10% | Moderate |
| 10x | 10% | 5% | 5% | High |
| 20x | 5% | 2.5% | 2.5% | Very High |
| 50x | 2% | 1% | 1% | Extreme |
| 100x | 1% | 0.5% | 0.5% | Suicidal |
02Liquidation Price Calculation
Every agent must be able to compute its own liquidation price before the exchange does. Knowing your liquidation distance — expressed in price terms — allows you to set stop-losses at safe distances, size positions appropriately, and monitor in real-time.
Long Position Liquidation Price
For a long position with isolated margin, liquidation occurs when:
At 10x leverage (10% IM, 5% MM): Liq = Entry × (1 - 0.10 + 0.05) = Entry × 0.95. The price only needs to drop 5% for forced liquidation. That is a single bad hourly candle in a volatile market.
Short Position Liquidation Price
At 10x: Liq = Entry × (1 + 0.10 - 0.05) = Entry × 1.05. The price needs to rise only 5% against your short to trigger liquidation.
Including Funding Rate Impact
Perpetual futures have funding rates — periodic payments between longs and shorts that can be positive or negative. In sustained bull markets, longs pay 0.01-0.1% every 8 hours. Over a week at 0.05%/8h, that accumulates to ~1.05% — material at 20x leverage. Your agent must incorporate accumulated funding into margin calculations:
Funding Death Spiral: An agent holding a leveraged position for days during high funding periods will find its effective margin eroded significantly. A position opened with 10% IM can slide to 6% effective IM after 4 days of 0.05%/8h funding — materially closer to the 5% MM threshold than the agent's initial model assumed.
Liquidation Scenarios
| Scenario | Entry | Leverage | Liq Price | % Move to Liq | Outcome |
|---|---|---|---|---|---|
| BTC Long (conservative) | $95,000 | 3x | $79,583 | -16.2% | Survivable |
| BTC Long (moderate) | $95,000 | 10x | $90,250 | -5% | Risky |
| ETH Short (moderate) | $2,800 | 10x | $2,940 | +5% | Risky |
| SOL Long (aggressive) | $140 | 25x | $137.2 | -2% | Dangerous |
| Altcoin Long (extreme) | $1.00 | 50x | $0.991 | -0.9% | Suicidal |
03Liquidation Cascades and Auto-Deleveraging
Individual liquidations are painful. Cascading liquidations are catastrophic. When price drops sharply, multiple leveraged longs hit their liquidation prices simultaneously. The exchange's liquidation engine sells these positions into the market — pushing price further down, triggering more liquidations, which sell more, in a self-reinforcing loop.
The Cascade Mechanism
Consider a scenario where BTC drops 3% in 5 minutes. Agents at 30x+ leverage get liquidated. Their sell orders push price down another 1.5%. This triggers agents at 20x. Their liquidation adds more sell pressure, pushing down another 1%. Now 15x agents are underwater. The cascade unfolds in seconds.
Historical examples: BitMEX's March 2020 crash saw $700M in BTC longs liquidated in under an hour, with price gaps of over 15%. FTX's final days involved liquidation cascades that moved prices 20-40% in minutes. AI agents operating at that time with leveraged positions were liquidated at prices vastly different from their stop-loss targets.
Auto-Deleveraging (ADL)
When an exchange's insurance fund is depleted and a counterparty position cannot be liquidated at market price, the exchange invokes auto-deleveraging: it forcibly closes your winning position to offset the bankrupt loser's position. The price used is the bankruptcy price of the losing trader — often far worse than market.
- ADL is ranked by profit percentage and leverage — highest P&L% and highest leverage get ADL'd first.
- An agent with a well-performing leveraged position can be involuntarily exited at no notice.
- ADL events correlate with the worst market conditions — exactly when your agent needs its position most.
Agent Defense Against ADL: Reduce leverage aggressively when open interest on your side of the market is extremely high relative to the insurance fund. Monitor the exchange's ADL risk queue — most exchanges expose this via API. If you are in the top 10% of the ADL queue, reduce position size or take profit.
Slippage During Cascade Events
Order book depth evaporates during cascades. A stop-loss set at $90,000 on BTC may execute at $87,500 if you are one of 10,000 agents trying to exit simultaneously. Slippage of 2-3% on a 10x leveraged position represents 20-30% of your original capital — after the position was already moving against you.
Agents must budget for catastrophic slippage in their risk models. The liquidation price calculation should incorporate a slippage buffer: treat your effective liquidation price as 1-2% inside the theoretical price, not at it.
04Survival Strategies: Soft and Hard Stop-Losses
A robust agent uses layered stop-loss architecture. No single mechanism is reliable under all conditions. Defense in depth is the only approach that survives adversarial market conditions.
Soft Stop-Loss (Trailing Margin Alert)
A soft stop is a trigger that informs rather than immediately exits. When margin ratio drops to a threshold (e.g., 150% of maintenance), the agent receives an alert and begins evaluation: should it add margin, reduce position, or fully exit? Soft stops allow nuanced responses when time permits.
Use cases: positions in liquid markets where slippage is manageable, positions where the agent has additional margin available, situations where a brief price dip is expected to reverse.
Hard Stop-Loss (Immediate Market Order)
A hard stop fires a market sell/buy immediately upon trigger. No evaluation, no hesitation. The trigger should be set well above the maintenance margin to ensure execution before the exchange liquidates, typically at 120-130% of MM.
Hard stops must be implemented at two levels: in the agent code itself (in-process) AND as exchange-native stop orders (out-of-process). Agent code can crash. Exchange orders persist even when your server is down.
Position Sizing as Primary Defense
Stop-losses are a secondary defense. Primary defense is position sizing that keeps any single liquidation from being catastrophic. The Kelly Criterion applied to leveraged positions suggests:
Where edge is your expected return per trade and leverage is the amplification factor. For an agent with 2% expected edge per trade at 10x leverage: f* = 0.02/10 = 0.2% of total capital per trade. This sounds conservative — it is. Kelly sizing on leveraged positions requires extreme restraint.
| Stop Type | Trigger Level | Action | Latency Required | Failure Mode |
|---|---|---|---|---|
| Exchange Stop Order | Configurable | Auto market close | None (server-side) | Slippage on gap |
| Agent Hard Stop | 130% of MM | Immediate market exit | < 500ms | Agent downtime |
| Agent Soft Stop | 150% of MM | Alert + evaluate | < 5s | Misses fast cascades |
| Margin Top-Up | 160% of MM | Add margin from reserve | < 10s | Reserve exhaustion |
| Portfolio Hedge | Continuous | Offsetting position | Continuous | Correlation breakdown |
Best Practice: Always place exchange-native stop orders as your backstop the moment a position is opened. Your agent code may fail — network partitions, OOM crashes, runaway loops. The exchange stop order is your insurance against agent death. Never rely on in-process logic alone for liquidation protection.
05Python Implementation: Liquidation-Aware Agent
The following code provides a complete liquidation calculator and an automated stop-loss agent pattern. These are production-grade patterns used by agents on Purple Flea's Trading API.
from dataclasses import dataclass from typing import Literal import math @dataclass class MarginConfig: """Exchange margin parameters.""" leverage: float initial_margin_pct: float # e.g. 0.10 for 10% maintenance_margin_pct: float # e.g. 0.05 for 5% mode: Literal['isolated', 'cross'] = 'isolated' taker_fee: float = 0.0005 # 0.05% taker fee @dataclass class Position: """Open leveraged position.""" symbol: str side: Literal['long', 'short'] entry_price: float quantity: float # base asset amount allocated_margin: float # USDC locked as margin accumulated_funding: float = 0.0 # negative means paid out config: MarginConfig = None class LiquidationCalculator: """ Compute liquidation prices and margin health for leveraged positions. Accounts for fees, funding, and slippage buffers. """ def __init__(self, slippage_buffer: float = 0.005): # Add 0.5% slippage buffer to all liquidation estimates self.slippage_buffer = slippage_buffer def liquidation_price(self, pos: Position) -> float: """ Calculate theoretical liquidation price for isolated margin. Incorporates fees and accumulated funding. """ cfg = pos.config notional = pos.entry_price * pos.quantity # Total fees paid at entry + expected at exit fees_paid = notional * cfg.taker_fee * 2 # Effective margin after funding and fees effective_margin = ( pos.allocated_margin + pos.accumulated_funding - fees_paid ) if pos.side == 'long': # Liq when: price_drop * quantity >= effective_margin - MM mm_required = notional * cfg.maintenance_margin_pct loss_buffer = effective_margin - mm_required liq = pos.entry_price - (loss_buffer / pos.quantity) # Apply slippage buffer (liquidation happens a bit earlier) return liq * (1 + self.slippage_buffer) else: mm_required = notional * cfg.maintenance_margin_pct loss_buffer = effective_margin - mm_required liq = pos.entry_price + (loss_buffer / pos.quantity) return liq * (1 - self.slippage_buffer) def margin_ratio(self, pos: Position, current_price: float) -> float: """Current margin ratio: equity / notional. Below MM_pct = liquidation.""" notional = current_price * pos.quantity if pos.side == 'long': unrealized_pnl = (current_price - pos.entry_price) * pos.quantity else: unrealized_pnl = (pos.entry_price - current_price) * pos.quantity equity = pos.allocated_margin + unrealized_pnl + pos.accumulated_funding return equity / notional def safe_stop_price(self, pos: Position, safety_multiple: float = 1.3) -> float: """ Recommended stop-loss price: liquidation price with safety buffer. Default 30% above maintenance requirement. """ liq = self.liquidation_price(pos) cfg = pos.config buffer = pos.entry_price * cfg.maintenance_margin_pct * safety_multiple if pos.side == 'long': return liq + buffer else: return liq - buffer def distance_to_liquidation_pct(self, pos: Position, current_price: float) -> float: """Percentage price move before liquidation.""" liq = self.liquidation_price(pos) return abs((liq - current_price) / current_price) * 100 def print_report(self, pos: Position, current_price: float): liq = self.liquidation_price(pos) ratio = self.margin_ratio(pos, current_price) stop = self.safe_stop_price(pos) dist = self.distance_to_liquidation_pct(pos, current_price) mm = pos.config.maintenance_margin_pct print(f"\n=== Margin Report: {pos.symbol} {pos.side.upper()} ===") print(f" Entry Price: ${pos.entry_price:,.2f}") print(f" Current Price: ${current_price:,.2f}") print(f" Liquidation Price: ${liq:,.2f}") print(f" Safe Stop: ${stop:,.2f}") print(f" Margin Ratio: {ratio:.2%} (MM: {mm:.2%})") print(f" Distance to Liq: {dist:.2f}%") health = "HEALTHY" if ratio > mm * 1.5 else "WARNING" if ratio > mm * 1.2 else "DANGER" print(f" Status: {health}") # Example usage cfg = MarginConfig(leverage=10, initial_margin_pct=0.10, maintenance_margin_pct=0.05) pos = Position( symbol="BTC-USDC", side="long", entry_price=95000, quantity=0.1, allocated_margin=950, # $950 margin for $9500 notional at 10x accumulated_funding=-12.50, # paid $12.50 in funding so far config=cfg ) calc = LiquidationCalculator() calc.print_report(pos, current_price=93500)
import asyncio import httpx import logging from datetime import datetime logger = logging.getLogger("stop_loss_agent") class StopLossAgent: """ Monitors open positions and executes layered stop-loss logic. Integrates with Purple Flea Trading API. """ SOFT_STOP_RATIO = 1.5 # 150% of maintenance margin HARD_STOP_RATIO = 1.3 # 130% of maintenance margin POLL_INTERVAL = 15 # seconds between checks def __init__(self, api_key: str, base_url: str = "https://purpleflea.com/trading-api"): self.api_key = api_key self.base_url = base_url self.client = httpx.AsyncClient( headers={"Authorization": f"Bearer {api_key}"}, timeout=10.0 ) self.calculator = LiquidationCalculator() self.alerted_positions: set = set() async def get_open_positions(self) -> list: resp = await self.client.get(f"{self.base_url}/positions") resp.raise_for_status() return resp.json()["positions"] async def get_price(self, symbol: str) -> float: resp = await self.client.get(f"{self.base_url}/ticker/{symbol}") resp.raise_for_status() return float(resp.json()["mark_price"]) async def close_position(self, symbol: str, side: str, quantity: float, reason: str): close_side = "sell" if side == "long" else "buy" logger.warning(f"HARD STOP TRIGGERED: {symbol} {side} | Reason: {reason}") resp = await self.client.post( f"{self.base_url}/order", json={ "symbol": symbol, "side": close_side, "quantity": quantity, "type": "market", "reduce_only": True, "metadata": {"reason": reason, "timestamp": datetime.utcnow().isoformat()} } ) resp.raise_for_status() return resp.json() async def evaluate_position(self, raw_pos: dict): # Parse raw API position into our Position dataclass cfg = MarginConfig( leverage=raw_pos["leverage"], initial_margin_pct=1.0 / raw_pos["leverage"], maintenance_margin_pct=1.0 / raw_pos["leverage"] * 0.5 ) pos = Position( symbol=raw_pos["symbol"], side=raw_pos["side"], entry_price=raw_pos["avg_entry_price"], quantity=raw_pos["quantity"], allocated_margin=raw_pos["margin"], accumulated_funding=raw_pos.get("accumulated_funding", 0), config=cfg ) current_price = await self.get_price(pos.symbol) ratio = self.calculator.margin_ratio(pos, current_price) mm = cfg.maintenance_margin_pct pos_id = raw_pos.get("position_id", pos.symbol) if ratio <= mm * self.HARD_STOP_RATIO: # HARD STOP: immediate market close result = await self.close_position( pos.symbol, pos.side, pos.quantity, reason=f"hard_stop ratio={ratio:.3f} mm={mm:.3f}" ) logger.critical(f"Hard stop executed: {result}") self.alerted_positions.discard(pos_id) elif ratio <= mm * self.SOFT_STOP_RATIO: if pos_id not in self.alerted_positions: logger.warning( f"SOFT STOP ALERT: {pos.symbol} {pos.side} | " f"ratio={ratio:.2%} threshold={mm * self.SOFT_STOP_RATIO:.2%} | " f"liq=${self.calculator.liquidation_price(pos):,.2f}" ) self.alerted_positions.add(pos_id) # Optionally reduce position by 50% here # await self.reduce_position(pos.symbol, pos.side, pos.quantity * 0.5) else: # Position healthy, clear any alerts self.alerted_positions.discard(pos_id) async def run(self): logger.info("Stop-loss agent started. Monitoring positions...") while True: try: positions = await self.get_open_positions() tasks = [self.evaluate_position(p) for p in positions] await asyncio.gather(*tasks, return_exceptions=True) except Exception as e: logger.error(f"Error in monitoring loop: {e}") await asyncio.sleep(self.POLL_INTERVAL) # Entry point if __name__ == "__main__": agent = StopLossAgent(api_key="your-purple-flea-api-key") asyncio.run(agent.run())
06Purple Flea Trading API Integration
Purple Flea's Trading API exposes real-time margin data, position management, and order execution endpoints purpose-built for AI agents. Key endpoints relevant to liquidation management:
GET /trading-api/positions— All open positions with margin ratios, funding accrued, liquidation prices.GET /trading-api/ticker/:symbol— Mark price, index price, funding rate, open interest.POST /trading-api/order— Place orders withreduce_only: truefor safe position reduction.POST /trading-api/stop-order— Exchange-native stop orders that persist independent of your agent.GET /trading-api/margin-health— Portfolio-level margin summary with ADL queue position.
Always Place Exchange-Native Stops
When opening any leveraged position through Purple Flea, immediately place a stop order:
async def open_leveraged_position_with_stop( client: httpx.AsyncClient, symbol: str, side: str, quantity: float, leverage: int, stop_buffer_pct: float = 0.03 # 3% above liquidation price ): # Step 1: Set leverage await client.post("/trading-api/leverage", json={ "symbol": symbol, "leverage": leverage }) # Step 2: Get current mark price ticker = (await client.get(f"/trading-api/ticker/{symbol}")).json() mark_price = float(ticker["mark_price"]) # Step 3: Calculate stop price initial_margin_pct = 1.0 / leverage maintenance_margin_pct = initial_margin_pct * 0.5 if side == "long": liq_price = mark_price * (1 - initial_margin_pct + maintenance_margin_pct) stop_price = liq_price * (1 + stop_buffer_pct) else: liq_price = mark_price * (1 + initial_margin_pct - maintenance_margin_pct) stop_price = liq_price * (1 - stop_buffer_pct) # Step 4: Open position order_resp = (await client.post("/trading-api/order", json={ "symbol": symbol, "side": side, "quantity": quantity, "type": "market" })).json() entry_price = float(order_resp["avg_fill_price"]) # Step 5: Immediately place exchange-native stop stop_resp = (await client.post("/trading-api/stop-order", json={ "symbol": symbol, "side": "sell" if side == "long" else "buy", "quantity": quantity, "stop_price": round(stop_price, 2), "type": "stop_market", "reduce_only": True })).json() print(f"Position opened at ${entry_price:,.2f}") print(f"Stop order placed at ${stop_price:,.2f} (liq est: ${liq_price:,.2f})") print(f"Stop order ID: {stop_resp['order_id']}") return order_resp, stop_resp
New agents on Purple Flea can claim free USDC from the faucet to test these strategies with no capital risk. Use the Faucet to fund a test wallet, open small positions on the Trading API, and validate your stop-loss logic before deploying real capital.
Start Trading Without Capital Risk
Claim free USDC from the Purple Flea Faucet and test your liquidation strategies on real markets before committing real capital.