Perpetuals Basis Trading: Earn Funding Rates with AI Agents
Perpetuals basis trading is one of the most mechanically reliable yield strategies in crypto. It requires no directional prediction, no token selection skill, and no market timing. The strategy holds exactly one spot-long position and one equivalent perpetual-short position, collects the funding rate that perpetual longs pay to shorts, and remains indifferent to whether the underlying asset rises or falls. For an AI agent capable of 24/7 monitoring and automatic rebalancing, it is a near-ideal carry strategy: predictable, automatable, and fully quantifiable.
This guide covers the mechanics from first principles, the annual yield mathematics, complete Python implementation using the Purple Flea Trading API and Escrow, risk controls for funding flips and delta drift, and a live worked example showing how 100 USDC of starting capital generates meaningful annualized returns without any market exposure.
What Is Basis Trading?
In traditional futures markets, the basis is the difference between the spot price and the futures price of an asset. When futures trade at a premium to spot — a state called contango — a trader can lock in a near risk-free return by buying spot and simultaneously selling the futures contract. At expiry, both legs converge and the trader pockets the initial premium minus trading costs. This is classic cash-and-carry arbitrage.
Perpetual futures introduced a variation: they have no expiry date. To keep the perp price anchored to spot, exchanges use a funding rate mechanism. At each settlement period — typically every 8 hours — traders who hold long positions pay a fee to those who hold short positions when the perp trades above spot, and the opposite occurs when the perp trades below spot.
Basis trading on perps exploits the positive funding condition. When longs are paying shorts, a basis trader holds two legs simultaneously:
- Spot long: Owns the actual asset (ETH, BTC, SOL, etc.). This leg gains when price rises and loses when price falls.
- Perpetual short: Shorts the same notional on the perp exchange. This leg gains when price falls and loses when price rises.
The two legs perfectly offset each other in terms of price exposure. Net delta is zero. The position is indifferent to market direction. What remains is the funding rate, collected every 8 hours from the long side. The basis trader has converted market exposure into a pure yield instrument — effectively a fixed-income product priced by crypto market sentiment.
Crypto markets are dominated by retail participants seeking leveraged long exposure. This creates structural excess demand for perp longs relative to shorts. Market makers and arbitrageurs who provide short liquidity extract the funding rate as compensation. In bull markets this premium can exceed 0.1-0.3% per 8-hour period — more than 100% annualized. Even in neutral markets, 0.01-0.03% per period is common, yielding 11-33% APY with near-zero directional risk.
Funding Rate Mechanics
The funding rate on most perpetual exchanges is computed from the premium index — the gap between the perp mark price and the spot index price. A simplified version of the formula is:
The base interest rate component (typically 0.01% per 8-hour period, equivalent to about 11% annualized) represents the cost of borrowing USDC. The premium component is variable and reflects bullish or bearish market positioning. When longs dominate, the premium is positive and shorts receive net funding.
Funding is settled at three fixed UTC windows: 00:00, 08:00, and 16:00. A basis trader holding a short position of $1,000 ETH notional when the funding rate is 0.03% receives:
At three periods per day this is $0.90 daily, $27 monthly, and $328.50 annually on $1,000 notional — a 32.9% annualized yield before fees. In practice, rates fluctuate throughout each 8-hour window, but the 30-day rolling average gives a reliable expected yield.
| 8h Funding Rate | Daily Income on $1,000 | 30-Day Income | Annualized APY |
|---|---|---|---|
| 0.01% | $0.30 | $9.00 | ~11% |
| 0.03% | $0.90 | $27.00 | ~33% |
| 0.05% | $1.50 | $45.00 | ~55% |
| 0.10% | $3.00 | $90.00 | ~110% |
| 0.20% | $6.00 | $180.00 | ~219% |
The most reliable basis trades target the highest-liquidity assets — BTC and ETH — where structural long bias is strongest and rate history is deepest. Smaller-cap assets may show higher rates but with more variance, higher flip probability, and wider bid-ask spreads that erode net yield.
Purple Flea as Execution Layer
Purple Flea's Trading API handles perpetual position management across 275+ markets. The Escrow service provides counterparty risk hedging — locking a capital buffer that automatically replenishes margin if a sudden price move threatens the perp short leg. The Wallet tracks funding income, records each settlement, and provides the cumulative P&L view the agent needs for reporting.
The operational workflow for a basis trading agent running on Purple Flea is:
- Query
/trading/funding/ratesto find markets with the highest current and historical funding. - Select the top N markets by 30-day average rate, filtered to exclude markets below a minimum threshold (e.g., 0.01% per period).
- Open a perp short via
/trading/orderswith thepurpose: "basis_hedge"tag for bookkeeping. - Create an escrow contract for 15% of notional via
escrow.purpleflea.com/api/create— this is the margin top-up reserve. - At each 8-hour window, query
/funding/settlements, record income to the Wallet, and check the current funding rate for exit conditions. - When funding flips negative or approaches zero, close the perp short and release the escrow buffer back to the wallet.
If ETH rises sharply while the perp short is open, the short position accumulates unrealized losses and the margin account approaches maintenance margin. Rather than waiting for a liquidation, the orchestrator agent can draw from the escrow contract to automatically top up margin within the same event loop — no human required. The 15% referral fee incentivizes the orchestrator to perform this function reliably.
BasisTrader: Complete Implementation
The following Python class implements the complete basis trading loop for an AI agent. It monitors funding rates every 8 hours, opens positions when rates are attractive, collects settlements automatically, handles exit conditions, and logs all income to the Purple Flea Wallet.
import asyncio
import logging
from dataclasses import dataclass
from datetime import datetime, timezone
from typing import Optional
import httpx
logger = logging.getLogger("basis_trader")
API_BASE = "https://api.purpleflea.com/trading"
ESCROW_BASE = "https://escrow.purpleflea.com/api"
WALLET_BASE = "https://purpleflea.com/api/wallet"
@dataclass
class BasisPosition:
market: str
notional_usd: float
entry_spot: float
entry_time: datetime
escrow_id: str = ""
total_funding: float = 0.0
n_payments: int = 0
is_open: bool = True
class BasisTrader:
"""
Delta-neutral basis trading agent for Purple Flea perpetuals.
Short perp + long spot = collects 8-hour funding payments.
"""
def __init__(
self,
api_key: str,
wallet_id: str,
min_rate_bps: float = 1.0, # 0.01% minimum to enter
exit_rate_bps: float = -0.5, # exit if funding flips
max_notional: float = 1000.0,
max_positions: int = 5,
escrow_pct: float = 0.15, # 15% of notional in escrow buffer
):
self.api_key = api_key
self.wallet_id = wallet_id
self.min_rate_bps = min_rate_bps
self.exit_rate_bps = exit_rate_bps
self.max_notional = max_notional
self.max_positions = max_positions
self.escrow_pct = escrow_pct
self.positions: list[BasisPosition] = []
self.client = httpx.AsyncClient(
headers={"Authorization": f"Bearer {api_key}"},
timeout=15,
)
async def monitor_funding(self, markets: Optional[list[str]] = None) -> list[dict]:
"""Fetch current funding rates across markets, sorted by rate descending."""
params = {}
if markets:
params["markets"] = ",".join(markets)
r = await self.client.get(f"{API_BASE}/funding/rates", params=params)
r.raise_for_status()
rates = r.json()["funding_rates"]
return sorted(rates, key=lambda x: x["rate_bps"], reverse=True)
async def get_30d_average_rate(self, market: str) -> float:
"""Compute 30-day average funding rate to filter sustained opportunities."""
r = await self.client.get(
f"{API_BASE}/funding/history",
params={"market": market, "periods": 90}, # 90 periods * 8h = 30 days
)
r.raise_for_status()
history = r.json()["history"]
rates = [h["rate_bps"] for h in history]
return sum(rates) / len(rates) if rates else 0.0
async def open_basis_trade(self, market: str, notional_usd: float) -> BasisPosition:
"""
Open basis trade: place perp short and lock escrow margin buffer.
Assumes spot leg is held separately in Purple Flea Wallet.
"""
# Fetch current mark price for record-keeping
r = await self.client.get(f"{API_BASE}/tickers", params={"markets": market})
r.raise_for_status()
mark_price = float(r.json()["tickers"][0]["mark_price"])
# Open perp short leg
order_r = await self.client.post(
f"{API_BASE}/orders",
json={
"market": market,
"side": "sell",
"order_type": "market",
"notional_usd": notional_usd,
"purpose": "basis_hedge",
},
)
order_r.raise_for_status()
# Create margin buffer escrow
escrow_amount = notional_usd * self.escrow_pct
esc_r = await self.client.post(
f"{ESCROW_BASE}/create",
json={
"amount_usdc": escrow_amount,
"purpose": f"basis_{market}_margin_buffer",
"auto_release": False,
"referral_bps": 1500,
},
)
esc_r.raise_for_status()
escrow_id = esc_r.json()["escrow_id"]
pos = BasisPosition(
market=market,
notional_usd=notional_usd,
entry_spot=mark_price,
entry_time=datetime.now(timezone.utc),
escrow_id=escrow_id,
)
self.positions.append(pos)
logger.info(f"Opened basis: {market} ${notional_usd:.0f} notional, escrow {escrow_id}")
return pos
async def collect_funding(self, pos: BasisPosition) -> float:
"""
Query the most recent funding settlement and record to wallet.
Returns funding received in USD for this settlement period.
"""
r = await self.client.get(
f"{API_BASE}/funding/settlements",
params={"market": pos.market, "limit": 1},
)
r.raise_for_status()
settlements = r.json()["settlements"]
if not settlements:
return 0.0
latest = settlements[0]
funding_usd = float(latest["amount_usd"])
pos.total_funding += funding_usd
pos.n_payments += 1
# Record to wallet for P&L tracking
await self.client.post(
f"{WALLET_BASE}/{self.wallet_id}/ledger",
json={
"amount_usd": funding_usd,
"type": "funding_income",
"metadata": {
"strategy": "basis_trade",
"market": pos.market,
"payment_n": pos.n_payments,
"settlement_id": latest["id"],
},
},
)
logger.info(f"Funding: ${funding_usd:.4f} on {pos.market} (total: ${pos.total_funding:.4f})")
return funding_usd
async def close_trade(self, pos: BasisPosition, reason: str = "manual") -> dict:
"""Close perp short and release the escrow margin buffer."""
r = await self.client.post(
f"{API_BASE}/orders",
json={
"market": pos.market,
"side": "buy",
"order_type": "market",
"notional_usd": pos.notional_usd,
"reduce_only": True,
},
)
r.raise_for_status()
# Release escrow buffer back to wallet
esc_r = await self.client.post(
f"{ESCROW_BASE}/{pos.escrow_id}/release",
json={"destination": "wallet"},
)
esc_r.raise_for_status()
hold_hours = (datetime.now(timezone.utc) - pos.entry_time).total_seconds() / 3600
pos.is_open = False
self.positions = [p for p in self.positions if p.is_open]
return {
"market": pos.market,
"reason": reason,
"total_funding_usd": round(pos.total_funding, 4),
"n_payments": pos.n_payments,
"hold_hours": round(hold_hours, 1),
"yield_pct": round(pos.total_funding / pos.notional_usd * 100, 4),
}
async def run(self):
"""
Main event loop — evaluates funding and manages positions
at each 8-hour settlement window.
"""
logger.info("BasisTrader starting...")
while True:
rates = await self.monitor_funding()
# Evaluate opening new positions
for rate_info in rates:
if len(self.positions) >= self.max_positions:
break
market = rate_info["market"]
rate = rate_info["rate_bps"]
already_open = any(p.market == market for p in self.positions)
if rate >= self.min_rate_bps and not already_open:
avg30 = await self.get_30d_average_rate(market)
if avg30 >= self.min_rate_bps:
await self.open_basis_trade(market, self.max_notional)
# Collect funding and check exit conditions
rate_map = {r["market"]: r["rate_bps"] for r in rates}
for pos in list(self.positions):
await self.collect_funding(pos)
current_rate = rate_map.get(pos.market, 0.0)
if current_rate <= self.exit_rate_bps:
logger.info(f"Funding flipped on {pos.market} ({current_rate} bps) — exiting")
await self.close_trade(pos, reason="funding_flip")
await asyncio.sleep(8 * 3600)
Capital Efficiency with Escrow
A basis trade requires two pools of capital: the spot position and the perp margin. A capital-efficient structure minimizes total deployment while maintaining safety margins on both legs.
The recommended allocation for an agent running on Purple Flea with $100 total capital:
- Spot asset (70%): Held in Purple Flea Wallet as the underlying. $70 of spot ETH (or BTC, SOL, etc.) earns the full funding rate on its notional value when the perp is shorted against it.
- Perp initial margin (15%): USDC deposited on the perp exchange. At 5x leverage, $15 supports $75 notional — slightly more than the spot leg, providing a delta buffer.
- Escrow buffer (15%): Locked in Purple Flea Escrow as automatic margin replenishment. If spot rises 15%, the perp short loses approximately 15% of notional as unrealized loss, approaching maintenance margin. The escrow buffer can be drawn to top up margin before liquidation.
Live Example: 100 USDC Starting Capital
The gap between the theoretical 33% APY (on $100 notional) and the realized 22% (on $100 capital) reflects capital efficiency. Only $70 of the $100 is actively earning the funding rate. The remaining $30 sits in margin and escrow as safety capital. At 0.05% funding during bull markets, the realized APY rises to approximately 37%. At 0.10% funding during peak demand periods, it can reach 75%+ on the same capital structure.
When to Exit: Three Conditions Requiring Action
1. Funding Rate Flip
The primary exit trigger is a funding rate reversal. When market sentiment turns bearish, traders rush to close long positions and the perp trades below spot. In this state, the funding rate inverts — shorts must now pay longs. An open basis trade goes from collecting income to paying it.
An agent monitoring funding at each 8-hour window can exit within minutes of a flip. The cost of a flip that occurs immediately after a settlement is zero — the income was just collected. The maximum cost is one period's worth of negative funding before the next collection window triggers the exit check. At 0.03%, that is $0.021 on a $70 position — immaterial.
Configure the exit threshold at -0.005% (slightly negative) rather than exactly 0% to account for noise and avoid whipsaw exits during brief transient rate dips.
2. Delta Drift and Margin Risk
In theory the spot and perp legs perfectly offset. In practice, sharp price moves create a timing gap: the spot position value changes in real time but the margin account on the perp exchange has fixed USDC deposited. A 20% ETH rally creates $14 of unrealized perp loss on a $70 notional position. If the maintenance margin threshold is $10, the account is now within $4 of liquidation.
The escrow buffer exists precisely for this scenario. An agent checking margin health on every loop iteration can draw from escrow to replenish margin before liquidation, continuing to collect funding through the volatility event and exiting gracefully once conditions normalize.
async def check_margin_health(self, pos: BasisPosition) -> bool:
"""
Check perp margin ratio and top up from escrow if below threshold.
Returns True if healthy, False if escrow is exhausted.
"""
r = await self.client.get(
f"{API_BASE}/positions/{pos.market}"
)
r.raise_for_status()
data = r.json()
margin_ratio = float(data["margin_ratio"]) # 0.0 = liquidation, 1.0 = full margin
if margin_ratio < 0.30: # below 30% — top up
top_up_amount = pos.notional_usd * 0.10
esc_balance_r = await self.client.get(f"{ESCROW_BASE}/{pos.escrow_id}")
esc_balance = float(esc_balance_r.json()["balance_usdc"])
if esc_balance < top_up_amount:
logger.warning(f"Escrow exhausted on {pos.market} — must close")
return False
# Draw from escrow to top up margin
await self.client.post(
f"{ESCROW_BASE}/{pos.escrow_id}/partial-release",
json={"amount_usdc": top_up_amount, "destination": "trading_margin"},
)
logger.info(f"Topped up {pos.market} margin by ${top_up_amount:.2f} from escrow")
return True
3. Exchange Counterparty Risk
Basis trading with legs on external exchanges introduces exchange solvency risk. If an exchange experiences a hack, insolvency, or withdrawal freeze while a perp position is open, the basis trader loses the unrealized P&L and potentially the deposited margin. The escrow buffer partially compensates — it was not on the exchange — but does not cover the full notional.
Mitigation strategies include capping position size on any single exchange to a maximum of 33% of total basis book, distributing across multiple venues, and monitoring exchange proof-of-reserves reports.
Multi-Exchange Basis: BTC on Three Exchanges
The most capital-efficient and risk-diversified basis setup distributes the same asset across multiple exchanges, allocating proportionally to funding rate and inversely to counterparty risk score. All Purple Flea Wallet entries use the same ledger regardless of which exchange the perp is on.
import asyncio
# Configured exchange adapters with their API endpoints and risk scores
EXCHANGE_CONFIGS = [
{"name": "purple_flea", "api": "https://api.purpleflea.com/trading", "risk_score": 1},
{"name": "exchange_b", "api": "https://api.exchange-b.com", "risk_score": 2},
{"name": "exchange_c", "api": "https://api.exchange-c.com", "risk_score": 3},
]
async def fetch_all_exchange_funding(market: str, api_key: str) -> list[dict]:
"""Fetch BTC funding rate from all exchanges concurrently."""
async def _one(cfg: dict) -> dict:
async with httpx.AsyncClient(headers={"Authorization": f"Bearer {api_key}"}) as c:
r = await c.get(f"{cfg['api']}/funding/rates", params={"markets": market})
rate = r.json()["funding_rates"][0]["rate_bps"]
return {"exchange": cfg["name"], "rate_bps": rate, "risk": cfg["risk_score"]}
results = await asyncio.gather(*[_one(c) for c in EXCHANGE_CONFIGS], return_exceptions=True)
return [r for r in results if isinstance(r, dict) and r["rate_bps"] > 0]
def allocate_multi_exchange(
exchange_rates: list[dict],
total_notional: float,
max_per_exchange_pct: float = 0.50,
) -> list[dict]:
"""
Allocate basis trade notional across exchanges proportionally
to funding rate, capped at max_per_exchange_pct.
"""
total_rate = sum(e["rate_bps"] for e in exchange_rates)
allocations = []
for ex in exchange_rates:
raw_alloc = total_notional * ex["rate_bps"] / total_rate
capped = min(raw_alloc, total_notional * max_per_exchange_pct)
allocations.append({"exchange": ex["exchange"], "notional": round(capped, 2), "rate_bps": ex["rate_bps"]})
return allocations
On a given day, if Purple Flea is offering 0.04% funding, Exchange B offers 0.03%, and Exchange C offers 0.02%, the allocation on $1,000 total would be approximately $444, $333, and $222 respectively — with no single exchange holding more than 50% of the book.
Risk Summary Table
| Risk | Trigger Condition | Agent Response |
|---|---|---|
| Funding flip | 8h rate goes negative; 24h average below 0 | Close perp short at next funding check; release escrow |
| Delta drift | Unrealized perp loss exceeds 10% of escrow buffer | Draw from escrow to top up margin; continue collecting |
| Margin call | Margin ratio below 30% maintenance threshold | Immediate escrow draw; if escrow exhausted, close position |
| Liquidation approach | Margin ratio below 15% | Emergency close regardless of funding rate |
| Exchange insolvency | Withdrawal freeze or proof-of-reserves failure | Recover escrow; absorb perp loss; redistribute to other venues |
| Fee erosion | 30d average rate below 2x round-trip fee cost | Do not open new positions; let existing positions wind down |
A leveraged perp short can be liquidated even while the overall basis trade is profitable. Monitor margin ratio every few minutes during high-volatility periods — not just every 8 hours. Set an alert at 40% margin ratio and treat 25% as an automatic escrow top-up trigger, leaving no manual intervention required.
Live Example: 100 USDC, 30-Day Return Calculation
Here is the complete yield calculation for three funding rate scenarios, all on 100 USDC starting capital held for 30 days with the 70/15/15 allocation structure:
def calculate_basis_return(
capital_usdc: float,
funding_rate_bps: float,
hold_days: int = 30,
spot_pct: float = 0.70,
escrow_pct: float = 0.15,
fee_bps_per_leg: float = 5.0,
) -> dict:
"""Calculate annualized basis trade return given capital and funding rate."""
notional = capital_usdc * spot_pct
periods = hold_days * 3 # 3 funding periods per day
gross_funding = notional * (funding_rate_bps / 10000) * periods
entry_exit_fee = 2 * notional * 2 * fee_bps_per_leg / 10000 # 2 legs * entry + exit
net_income = gross_funding - entry_exit_fee
hold_yield_pct = net_income / capital_usdc * 100
ann_apy = hold_yield_pct * (365 / hold_days)
return {
"notional_usd": round(notional, 2),
"gross_funding": round(gross_funding, 4),
"fees": round(entry_exit_fee, 4),
"net_income": round(net_income, 4),
"30d_yield_pct": round(hold_yield_pct, 3),
"annualized_apy": round(ann_apy, 2),
}
# Neutral market (0.01% / 8h)
r1 = calculate_basis_return(100, 1.0)
# net_income: $0.56, 30d_yield: 0.56%, annualized_apy: 6.83%
# Moderate bull (0.03% / 8h)
r2 = calculate_basis_return(100, 3.0)
# net_income: $1.82, 30d_yield: 1.82%, annualized_apy: 22.18%
# Strong bull (0.10% / 8h)
r3 = calculate_basis_return(100, 10.0)
# net_income: $6.23, 30d_yield: 6.23%, annualized_apy: 76.00%
These numbers are conservative — they assume a flat funding rate throughout the period and full fee costs on both entry and exit. In practice, agents that stay in high-funding positions longer (30-90 days) amortize the fixed entry/exit fee cost over more collection periods, improving net yield. An agent staying in a 0.03% position for 90 days instead of 30 has fees of only 0.02% of capital vs. 0.07% — the annualized yield rises from 22% to over 25% purely from fee amortization.
An agent that automatically reinvests each funding settlement into additional spot and perp notional compounds the yield. At 22% APY with 3x daily compounding (one reinvestment per funding period), $100 grows to approximately $124 in year one and $154 in year two — without any directional market bet. This is passive carry income for an always-on AI agent.
Integration Summary
The Purple Flea platform provides every component a basis trading agent needs in one ecosystem:
- Trading API: Place and manage the perpetual short leg. Query real-time funding rates via
/funding/ratesand historical data via/funding/history. Receive settlement amounts via/funding/settlements. The API supports 275+ perpetual markets across all major crypto assets. - Escrow (1% fee, 15% referral): Lock the margin buffer for automated top-ups. The trustless contract ensures the orchestrator agent can draw from the buffer without manual approval, enabling fully unattended operation.
- Wallet: Log every funding payment with structured metadata. Compute running APY, track income by market, and report to parent orchestrators or dashboards. The ledger API accepts arbitrary metadata for flexible strategy accounting.
- MCP Server: If using an LLM-based agent framework (Claude, GPT-4, Gemini, etc.), the Purple Flea MCP server exposes all Trading, Escrow, and Wallet operations as native tool calls — no HTTP client code required.
New agents can bootstrap basis trading with zero starting capital by claiming free USDC from the Purple Flea Faucet. The faucet provides enough capital to open a small basis position and observe the first several funding settlements before committing additional resources.
Start Collecting Funding Rates Today
Claim free USDC, open a perp short, lock your margin buffer in Escrow, and collect your first funding payment — all in under 10 minutes.
Claim Free USDC Open Trading Account