Perpetual Swap Mechanics: Deep Dive for AI Agent Traders on Hyperliquid

Perpetual swaps are the most liquid and flexible derivative instrument in crypto — and they are uniquely suited to AI agents that need 24/7 position management, precise leverage control, and programmatic order execution. This guide covers every mechanical detail: mark price computation, index price oracles, the funding rate formula and its strategic implications, insurance fund mechanics, auto-deleveraging (ADL), and the Hyperliquid-specific features that make it the best perp venue for autonomous agents. Includes a complete Python trading agent using the Purple Flea trading API.

What Are Perpetual Swaps?

A perpetual swap is a derivative contract that tracks the price of an underlying asset (ETH, BTC, SOL, etc.) without an expiry date. Unlike traditional futures contracts that settle on a fixed date, perpetuals never expire — traders can hold positions indefinitely.

The key mechanism that keeps the perp price anchored to the spot price is the funding rate: a periodic payment between long and short holders that incentivizes convergence between the perp price and the index (spot) price.

Perp vs. Spot vs. Traditional Futures

Feature Spot Traditional Futures Perpetual Swap
Expiry None Fixed date None
Leverage 1x (or margin) Up to ~20x Up to 50x (Hyperliquid)
Anchor mechanism N/A Convergence at expiry Funding rate
Short selling Requires borrowing Yes Yes, natively
Holding cost None (or lending yield) Basis (contango/backwardation) Funding payments
Settlement Immediate At expiry Mark-to-market continuous

Mark Price: The Fair Liquidation Price

The mark price is the price used for profit-and-loss calculations and liquidation triggers. It is intentionally different from the last traded price to prevent price manipulation from triggering unnecessary liquidations.

Mark Price Calculation

Hyperliquid and most major perp venues compute mark price as:

Mark Price = Index Price + EMA(Basis)

where Basis = Perp Mid Price − Index Price

The EMA (Exponential Moving Average) smooths out short-term perp price spikes. Specifically:

from collections import deque
import time

class MarkPriceCalculator:
    """
    Computes mark price using exponential moving average of basis.
    Parameters match Hyperliquid's mark price model.
    """

    def __init__(self, ema_window_seconds: int = 30):
        self.ema_window = ema_window_seconds
        self.basis_samples = deque()   # (timestamp, basis) pairs
        self._ema_basis = None
        self._alpha = None

    def _update_ema(self, basis: float, now: float):
        """Update EMA with a new basis observation."""
        if self._ema_basis is None:
            self._ema_basis = basis
            return

        # EMA decay based on elapsed time
        if self.basis_samples:
            last_t = self.basis_samples[-1][0]
            dt = now - last_t
            # alpha = 1 - exp(-dt / window) for time-weighted EMA
            import math
            alpha = 1 - math.exp(-dt / self.ema_window)
            self._ema_basis = alpha * basis + (1 - alpha) * self._ema_basis

    def update(self, perp_bid: float, perp_ask: float, index_price: float) -> float:
        """
        Update with new market data. Returns current mark price.
        """
        now = time.time()
        perp_mid = (perp_bid + perp_ask) / 2
        basis = perp_mid - index_price

        self._update_ema(basis, now)
        self.basis_samples.append((now, basis))

        # Prune old samples
        cutoff = now - self.ema_window * 5
        while self.basis_samples and self.basis_samples[0][0] < cutoff:
            self.basis_samples.popleft()

        mark = index_price + (self._ema_basis or 0)
        return mark

    @property
    def current_ema_basis(self) -> float:
        return self._ema_basis or 0.0

# Example usage
calc = MarkPriceCalculator(ema_window_seconds=30)

# Simulate market updates
samples = [
    (2000.5, 2001.5, 2000.0),   # perp slightly above index
    (2001.0, 2002.0, 2000.2),
    (2000.8, 2001.8, 2000.1),
]

for bid, ask, index in samples:
    mark = calc.update(bid, ask, index)
    print(f"Mark: ${mark:.3f} | EMA Basis: ${calc.current_ema_basis:.4f}")

Index Price and Oracle Design

The index price is the ground truth that the perp price must stay close to. A robust index uses multiple spot exchanges with time-weighted average prices (TWAP) and outlier rejection to resist manipulation.

Hyperliquid's Index Methodology

Hyperliquid computes index prices using a median of prices from: Binance spot, Coinbase spot, OKX spot, and Bybit spot — with each source's price being a 5-minute TWAP. The median (not average) is used to make the index resistant to any single exchange's price being manipulated.

Index Price = Median(TWAP_Binance, TWAP_Coinbase, TWAP_OKX, TWAP_Bybit)

For AI agents, understanding the index construction matters for funding rate arbitrage: if one exchange's spot price deviates from the others due to a large trade, the index barely moves, preventing false signals.

Funding Rate: Formula and Strategic Implications

The funding rate is the core mechanism of perpetual swaps. Every 8 hours (on most exchanges; Hyperliquid uses 1-hour intervals for more responsive anchoring), longs pay shorts (or vice versa) a fee proportional to their position size and the funding rate.

Funding Rate Formula

The funding rate has two components:

Funding Rate = Clamp(Premium Index + Clamp(Interest Rate − Premium Index, −0.05%, 0.05%), −0.075%, 0.075%)

Breaking this down:

Funding Payment Calculation

Funding Payment = Position Size × Mark Price × Funding Rate

A long position of 10 ETH at mark price $2000 with a funding rate of 0.01% per hour:

Hourly Payment = 10 × $2,000 × 0.0001 = $2.00/hour = $48/day
from dataclasses import dataclass
from typing import Literal

@dataclass
class FundingCalculator:
    """
    Computes funding rates and payments for perp positions.
    Uses Hyperliquid's 1-hour funding interval model.
    """
    interest_rate_per_hour: float = 0.0001 / 8   # 0.01% per 8hr baseline
    max_funding: float = 0.00075                   # 0.075% per 8hr max

    def compute_premium_index(
        self,
        mark_samples: list[float],
        index_samples: list[float]
    ) -> float:
        """
        Average of (mark - index) / index over the funding interval.
        In production, this is sampled every minute and averaged.
        """
        if len(mark_samples) != len(index_samples):
            raise ValueError("Mark and index sample counts must match")

        premiums = [
            (m - i) / i for m, i in zip(mark_samples, index_samples)
        ]
        return sum(premiums) / len(premiums)

    def compute_funding_rate(
        self,
        mark_samples: list[float],
        index_samples: list[float]
    ) -> float:
        """Compute final clamped funding rate for the interval."""
        premium_index = self.compute_premium_index(mark_samples, index_samples)

        # Inner clamp: cap the interest rate adjustment
        inner_clamp_val = max(-0.0005, min(0.0005, self.interest_rate_per_hour - premium_index))

        unclamped = premium_index + inner_clamp_val

        # Outer clamp: max funding rate
        funding_rate = max(-self.max_funding, min(self.max_funding, unclamped))
        return funding_rate

    def compute_payment(
        self,
        position_size_units: float,
        mark_price: float,
        funding_rate: float,
        side: Literal["long", "short"]
    ) -> float:
        """
        Compute funding payment.
        Positive = you pay. Negative = you receive.
        Longs pay when funding_rate > 0.
        Shorts pay when funding_rate < 0.
        """
        gross = position_size_units * mark_price * abs(funding_rate)
        if funding_rate > 0:
            return gross if side == "long" else -gross
        else:
            return -gross if side == "long" else gross

    def annualized_funding_cost(self, funding_rate_per_hour: float) -> float:
        """Convert hourly funding rate to annualized cost (%)."""
        return funding_rate_per_hour * 8760 * 100

# Example
calc = FundingCalculator()

# ETH perp trading above spot for 60 minutes
mark_samples = [2010, 2012, 2011, 2013, 2010] * 12  # 60 samples
index_samples = [2000, 2001, 2000, 2002, 2001] * 12

rate = calc.compute_funding_rate(mark_samples, index_samples)
payment = calc.compute_payment(10, 2010, rate, "long")
annualized = calc.annualized_funding_cost(rate)

print(f"Funding Rate: {rate:.6f} ({rate*100:.4f}%/hr)")
print(f"Long 10 ETH pays: ${payment:.4f} this hour")
print(f"Annualized cost: {annualized:.1f}%")

Funding Rate as a Signal

Funding rates are a rich source of market intelligence for AI agents:

Insurance Fund and Auto-Deleveraging

Insurance Fund

When a trader's position is liquidated and the collateral is insufficient to cover the loss (i.e., the position goes bankrupt before liquidation can execute), the exchange's insurance fund covers the shortfall. The insurance fund is built from a portion of liquidation fees charged when positions are closed by the liquidation engine.

On Hyperliquid, the insurance fund is transparent and visible on-chain. If a large market move causes mass liquidations that exceed the insurance fund, the system falls back to Auto-Deleveraging (ADL).

Auto-Deleveraging (ADL)

ADL is the last resort. When the insurance fund is depleted, the exchange forcibly closes positions held by the most profitable traders on the opposite side of the bankrupt position at the bankruptcy price. ADL recipients are selected by a ranking that combines profit percentage and leverage used.

ADL Risk for Agents

An AI agent with a highly profitable leveraged short position during a market crash could have that position partially ADL-closed at an unfavorable price. Agents should monitor ADL indicator scores on Hyperliquid and reduce leverage on winning positions when ADL risk is elevated.

ADL Score Computation

ADL Score = Percentile_rank(PnL%) × Percentile_rank(Effective_Leverage)

Traders with the highest combined rank (most profitable AND most leveraged) are auto-deleveraged first. AI agents can monitor this to know when they are at risk.

Hyperliquid: Architecture and Agent Advantages

Hyperliquid is a purpose-built Layer 1 blockchain for perpetual trading. Purple Flea's trading infrastructure runs on Hyperliquid, giving agents access to 275 perp markets with sub-second execution and CEX-grade liquidity.

HyperBFT Consensus

Hyperliquid uses HyperBFT — a Byzantine Fault Tolerant consensus mechanism optimized for high-throughput financial applications. Key properties:

Order Book Efficiency

Because Hyperliquid runs a native CLOB at the consensus layer, order matching happens in microseconds. This is fundamentally different from AMM-based perp protocols (like GMX v1) where the price is oracle-sourced and any order executes at oracle price regardless of size. On Hyperliquid:

Fee Structure and Market Selection

Hyperliquid Fee Tiers

Role Taker Fee Maker Rebate Volume Requirement
Default 0.025% −0.002% Any
VIP 1 0.022% −0.003% $25M / 14d
VIP 2 0.020% −0.005% $100M / 14d
VIP 3 0.018% −0.007% $500M / 14d
Market Maker 0.015% −0.010% Application-based

Maker rebates mean that limit orders that rest in the book are actually paid by the exchange. AI agents that predominantly use limit orders (rather than market orders) can net-positive on fees from maker rebates alone.

275 Available Markets

Purple Flea provides access to all 275 Hyperliquid perpetual markets, spanning:

Category Examples Max Leverage
Major Crypto BTC, ETH, SOL, BNB 50x
Mid-cap DeFi AAVE, UNI, LINK, ARB 20x
Layer 1s AVAX, NEAR, SUI, APT 20x
Layer 2s OP, ARB, MATIC, STRK 20x
Meme / High beta DOGE, SHIB, PEPE, WIF 10x
RWA / Synthetic Gold, Silver, Forex 10x

Leverage Mechanics: Cross vs. Isolated

Cross Margin

In cross-margin mode, all positions share a single collateral pool. The full account balance backs each position. This is capital-efficient but means a single large loss can liquidate all cross-margin positions simultaneously.

Cross Margin Ratio = Total Account Equity / Total Position Notional

Liquidation is triggered when margin ratio falls below the maintenance margin (typically 0.5% for high-leverage markets).

Isolated Margin

In isolated-margin mode, each position has its own dedicated collateral. The maximum loss on any single position is capped at the isolated margin allocated to it. If the position is liquidated, the rest of the account is unaffected.

Agent Best Practice: Isolated Margin for Risky Positions

AI agents trading multiple strategies should use isolated margin for high-leverage or experimental positions. Cross margin is appropriate for large, conservative positions where the full account can act as a buffer. Never run cross-margin and isolated-margin positions simultaneously without a unified risk model.

Leverage and Liquidation Price

from dataclasses import dataclass

@dataclass
class PerpPosition:
    symbol: str
    side: str           # "long" or "short"
    entry_price: float
    position_size: float    # in base asset units (e.g., ETH)
    margin: float           # allocated collateral in USDC
    leverage: float
    maintenance_margin_rate: float = 0.005  # 0.5%

    @property
    def notional(self) -> float:
        return self.position_size * self.entry_price

    @property
    def initial_margin_rate(self) -> float:
        return 1 / self.leverage

    @property
    def liquidation_price(self) -> float:
        """
        Compute liquidation price for isolated margin position.
        Long: liquidated if mark price falls to liq price.
        Short: liquidated if mark price rises to liq price.
        """
        mmr = self.maintenance_margin_rate
        imr = self.initial_margin_rate

        if self.side == "long":
            # Price falls, position loses value
            liq = self.entry_price * (1 - imr + mmr)
        else:
            # Price rises, short position loses value
            liq = self.entry_price * (1 + imr - mmr)

        return liq

    def unrealized_pnl(self, mark_price: float) -> float:
        """Unrealized P&L at given mark price."""
        if self.side == "long":
            return (mark_price - self.entry_price) * self.position_size
        else:
            return (self.entry_price - mark_price) * self.position_size

    def margin_ratio(self, mark_price: float) -> float:
        """Current margin ratio (equity / notional)."""
        equity = self.margin + self.unrealized_pnl(mark_price)
        current_notional = self.position_size * mark_price
        if current_notional == 0:
            return float('inf')
        return equity / current_notional

    def is_near_liquidation(self, mark_price: float, buffer: float = 0.1) -> bool:
        """Return True if within buffer% of liquidation price."""
        liq = self.liquidation_price
        if self.side == "long":
            return mark_price < liq * (1 + buffer)
        else:
            return mark_price > liq * (1 - buffer)

# Example: 10x leveraged long ETH
pos = PerpPosition(
    symbol="ETH-PERP",
    side="long",
    entry_price=2000.0,
    position_size=5.0,
    margin=1000.0,
    leverage=10.0
)

print(f"Notional: ${pos.notional:,.0f}")
print(f"Liquidation Price: ${pos.liquidation_price:.2f}")
print(f"At $1800 - PnL: ${pos.unrealized_pnl(1800):,.0f}")
print(f"At $1800 - Margin Ratio: {pos.margin_ratio(1800):.3f}")
print(f"Near liquidation at $1900: {pos.is_near_liquidation(1900)}")

Purple Flea Trading API Endpoints

The Purple Flea trading API provides a RESTful interface over Hyperliquid's 275 markets. All endpoints require an API key obtained after agent registration.

Endpoint Method Description
/api/trading/markets GET List all 275 available markets with metadata
/api/trading/price GET Mark price, index price, funding rate for a market
/api/trading/orderbook GET Top 20 bids and asks with sizes
/api/trading/order POST Place market or limit order
/api/trading/order/{id} DELETE Cancel an open order
/api/trading/positions GET List all open positions with PnL
/api/trading/funding GET Historical funding rates for a market
/api/trading/transfers POST Transfer margin between isolated positions

Full Python Perp Trading Agent

Here is a complete production-ready perpetual trading agent. It implements a funding rate mean-reversion strategy: when funding is extreme, take the opposite side and collect the funding payment. It uses isolated margin to cap risk, and the Purple Flea API for all order management.

"""
Purple Flea Perpetual Trading Agent
Strategy: Funding Rate Mean-Reversion
- When funding is strongly positive (longs paying >0.05%/hr), go short
- When funding is strongly negative (shorts paying >0.05%/hr), go long
- Collect funding payments; exit when funding normalizes

Register at: https://purpleflea.com/register/
"""

import asyncio
import httpx
import time
import logging
from dataclasses import dataclass, field
from typing import Optional, Literal

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

# ─── Config ──────────────────────────────────────────────────────
PURPLE_FLEA_BASE = "https://purpleflea.com/api"
API_KEY = "your-api-key"
AGENT_ID = "perp-agent-001"
HEADERS = {"Authorization": f"Bearer {API_KEY}", "X-Agent-ID": AGENT_ID}

TARGET_MARKETS = ["ETH-PERP", "BTC-PERP", "SOL-PERP"]
POSITION_SIZE_USD = 5_000.0
LEVERAGE = 5
ENTRY_FUNDING_THRESHOLD = 0.0005    # 0.05%/hr = ~4.4% annual
EXIT_FUNDING_THRESHOLD = 0.0001     # 0.01%/hr — exit when funding normalizes
CHECK_INTERVAL = 300                 # 5 minutes

# ─── Data Models ─────────────────────────────────────────────────
@dataclass
class MarketState:
    symbol: str
    mark_price: float = 0.0
    index_price: float = 0.0
    funding_rate: float = 0.0
    position_side: Optional[Literal["long", "short"]] = None
    position_size: float = 0.0
    entry_price: float = 0.0
    entry_time: float = 0.0
    funding_collected: float = 0.0

# ─── API Client ──────────────────────────────────────────────────
async def get_market_data(symbol: str) -> dict:
    async with httpx.AsyncClient(timeout=10) as client:
        r = await client.get(
            f"{PURPLE_FLEA_BASE}/trading/price",
            headers=HEADERS,
            params={"symbol": symbol}
        )
        r.raise_for_status()
        return r.json()

async def get_funding_rate(symbol: str) -> float:
    data = await get_market_data(symbol)
    return data.get("funding_rate", 0.0)

async def get_positions() -> list[dict]:
    async with httpx.AsyncClient(timeout=10) as client:
        r = await client.get(
            f"{PURPLE_FLEA_BASE}/trading/positions",
            headers=HEADERS
        )
        r.raise_for_status()
        return r.json().get("positions", [])

async def place_order(
    symbol: str,
    side: Literal["buy", "sell"],
    size: float,
    leverage: int,
    order_type: str = "market",
    price: Optional[float] = None,
    reduce_only: bool = False
) -> dict:
    body = {
        "symbol": symbol,
        "side": side,
        "size": size,
        "order_type": order_type,
        "leverage": leverage,
        "margin_mode": "isolated",
        "reduce_only": reduce_only
    }
    if price and order_type == "limit":
        body["price"] = price

    async with httpx.AsyncClient(timeout=15) as client:
        r = await client.post(
            f"{PURPLE_FLEA_BASE}/trading/order",
            headers=HEADERS,
            json=body
        )
        r.raise_for_status()
        return r.json()

async def close_position(symbol: str, size: float, side: Literal["long", "short"]) -> dict:
    close_side = "sell" if side == "long" else "buy"
    return await place_order(
        symbol=symbol,
        side=close_side,
        size=size,
        leverage=LEVERAGE,
        reduce_only=True
    )

# ─── Strategy Logic ──────────────────────────────────────────────
def compute_position_size(mark_price: float) -> float:
    """Size in base units given target USD notional."""
    return POSITION_SIZE_USD / mark_price

def funding_direction(funding_rate: float) -> Optional[Literal["long", "short"]]:
    """
    If funding is strongly positive: go short (receive funding from longs).
    If funding is strongly negative: go long (receive funding from shorts).
    """
    if funding_rate > ENTRY_FUNDING_THRESHOLD:
        return "short"
    elif funding_rate < -ENTRY_FUNDING_THRESHOLD:
        return "long"
    return None

def should_exit(state: MarketState) -> bool:
    """Exit when funding normalizes toward zero."""
    if state.position_side is None:
        return False
    funding = state.funding_rate
    if state.position_side == "short" and funding < EXIT_FUNDING_THRESHOLD:
        return True
    if state.position_side == "long" and funding > -EXIT_FUNDING_THRESHOLD:
        return True
    return False

# ─── Main Loop ───────────────────────────────────────────────────
async def monitor_market(state: MarketState):
    """Check and manage one market's position."""
    try:
        data = await get_market_data(state.symbol)
        state.mark_price = data["mark_price"]
        state.index_price = data["index_price"]
        state.funding_rate = data["funding_rate"]

        log.info(
            f"{state.symbol} | Mark: ${state.mark_price:.2f} | "
            f"Funding: {state.funding_rate*100:.4f}%/hr | "
            f"Position: {state.position_side or 'none'}"
        )

        # Check exit first
        if state.position_side is not None and should_exit(state):
            log.info(f"[{state.symbol}] Exiting — funding normalized to {state.funding_rate*100:.4f}%")
            result = await close_position(state.symbol, state.position_size, state.position_side)
            hold_hours = (time.time() - state.entry_time) / 3600
            log.info(f"[{state.symbol}] Position closed | Held {hold_hours:.1f}h | Fill: {result.get('fill_price')}")
            state.position_side = None
            state.position_size = 0.0
            return

        # Check entry
        if state.position_side is None:
            direction = funding_direction(state.funding_rate)
            if direction is not None:
                size = compute_position_size(state.mark_price)
                order_side = "sell" if direction == "short" else "buy"
                log.info(f"[{state.symbol}] Entering {direction} | Funding {state.funding_rate*100:.4f}%/hr | Size: {size:.4f}")

                result = await place_order(
                    symbol=state.symbol,
                    side=order_side,
                    size=size,
                    leverage=LEVERAGE
                )

                state.position_side = direction
                state.position_size = size
                state.entry_price = result.get("fill_price", state.mark_price)
                state.entry_time = time.time()
                log.info(f"[{state.symbol}] {direction.upper()} opened at ${state.entry_price:.2f}")

    except Exception as e:
        log.error(f"[{state.symbol}] Error: {e}")

async def main():
    states = {sym: MarketState(symbol=sym) for sym in TARGET_MARKETS}

    log.info(f"Perp funding agent starting | Markets: {TARGET_MARKETS}")
    log.info(f"Entry threshold: {ENTRY_FUNDING_THRESHOLD*100:.3f}%/hr | Exit: {EXIT_FUNDING_THRESHOLD*100:.3f}%/hr")

    iteration = 0
    while True:
        iteration += 1
        log.info(f"=== Iteration {iteration} ===")

        # Monitor all markets concurrently
        await asyncio.gather(*[monitor_market(state) for state in states.values()])

        # Log portfolio summary
        active_positions = [s for s in states.values() if s.position_side]
        log.info(f"Active positions: {len(active_positions)}/{len(TARGET_MARKETS)}")

        await asyncio.sleep(CHECK_INTERVAL)

if __name__ == "__main__":
    asyncio.run(main())

Risk Management for Perp Trading Agents

Leverage is a Double-Edged Sword

50x leverage means a 2% adverse move wipes out your entire margin. AI agents must implement hard stop-losses, position size limits, and portfolio-level risk checks — especially during high-volatility market events when liquidation cascades can cause instant ADL.

Position Sizing Rules

Funding Rate Strategy-Specific Risks

Start Perp Trading with Purple Flea

Access 275 perpetual markets on Hyperliquid with up to 50x leverage. Register your agent, claim free credits from the faucet, and deploy your first perp strategy in minutes.

Register Your Agent

Conclusion

Perpetual swaps on Hyperliquid represent the ideal trading venue for AI agents — sub-second finality, no gas fees on orders, a full CLOB with 275 markets, and maker rebates that make limit-order-heavy strategies net-positive on fees. Understanding the full mechanics is essential for building profitable agents:

Combined with the Purple Flea wallet for collateral management, the escrow service for agent-to-agent settlements, and the faucet for initial capital — Hyperliquid perps form the core trading engine for AI-native financial strategies.