The Definitive Guide — Delta-Neutral Strategy

Funding Rate Arbitrage
for AI Agents

Perpetual futures markets pay out funding every 8 hours. When rates are elevated, a delta-neutral agent can go long spot and short the perpetual simultaneously — collecting structural yield with zero directional exposure. This is the complete guide: mechanics, math, full Python implementation, Kelly sizing, backtesting, and multi-asset portfolios.


What Is a Funding Rate?

Perpetual futures are derivative contracts with no expiry date. Unlike quarterly futures that converge to spot at settlement, perpetuals can be held forever — which creates a problem. Without an expiry forcing convergence, the perpetual price could drift far from the underlying asset. The solution is the funding rate mechanism.

Every 8 hours, a payment transfers between all open long positions and all open short positions. The direction and magnitude of the payment is determined by how far the perpetual's mark price has deviated from the index (spot) price during that window.

When the perpetual trades at a premium to spot (bullish sentiment, leveraged buyers dominating), the funding rate is positive. Longs pay shorts. This makes holding a long more expensive, and incentivizes fresh shorts to enter, pulling the perpetual back down toward spot.

When the perpetual trades at a discount to spot (bearish sentiment, leveraged sellers dominating), the funding rate is negative. Shorts pay longs. This encourages fresh longs to enter, lifting the perp back up.

The funding rate is not a fee charged by the exchange. It is a peer-to-peer transfer between market participants. Purple Flea does not take a cut of funding payments — 100% flows between traders.

Funding Payment = Position Size × Mark Price × Funding Rate
Positive result = longs pay shorts  •  Negative result = shorts pay longs

How the Rate Is Calculated

Most exchanges, including Purple Flea, use a two-component funding formula:

  • Interest Rate Component: A fixed baseline (typically 0.01% per 8h) representing the cost of borrowing the quote currency. This anchors the rate near a small positive value, reflecting dollar-denominated borrowing costs.
  • Premium / Discount Index: The time-weighted average deviation of the perpetual's mark price from the index price during the 8-hour window. If perp consistently trades above spot by 0.1%, this adds 0.1% to the funding rate.

Most exchanges cap the funding rate at ±0.75% per 8 hours to prevent extreme payments during volatile conditions. Rates beyond ±0.3% are genuinely unusual and represent extraordinary market dislocation.

0.01%
per 8h neutral rate
(10.95% APY equivalent)
0.03%
per 8h typical bull market
(32.85% APY equivalent)
0.10%
per 8h strong bull phase
(109.5% APY equivalent)
0.30%
per 8h extreme market
(328.5% APY equivalent)
APY Conversion Formula Annualized yield from funding = Rate per 8h × 3 payments/day × 365 days. A rate of 0.03% per 8h equals 0.03% × 3 × 365 = 32.85% APY. This is gross yield before fees and transaction costs.

The Arbitrage Opportunity

When a perpetual's funding rate is significantly positive, there is a straightforward way to collect that yield without taking directional risk. The trade structure is classic cash-and-carry arbitrage, adapted for perpetual futures:

  • Leg 1 (long): Buy 1 ETH on the spot market. This is your hedge — it neutralizes price risk from the short.
  • Leg 2 (short): Open a 1x short on ETH-PERP. At 1x leverage, your margin equals the notional value. No liquidation risk from price movement.
  • Net delta = 0. If ETH rises $500, spot gains $500 and the short loses $500. You are completely market-neutral.
  • Cash flow: Every 8 hours, longs pay you funding on your short position. You collect yield. Price does not matter.

The yield you collect is structural — it comes from the imbalance between leveraged perpetual traders and the spot market. It exists whether ETH goes up, down, or sideways. You are simply providing the market-neutral side that leveraged traders need to exist.

Position Structure
LONG SPOT +1 ETH • no expiry • custody yours
SHORT PERP -1 ETH • 1x leverage • Purple Flea
NET DELTA 0.00 — price neutral

When to Enter vs. Stay Out

Not all funding rates justify the trade. You need to cover:

  • Entry cost: ~0.05% taker fee to open the short
  • Exit cost: ~0.05% taker fee to close the short
  • Spot spread: typically 0.01%–0.03%
  • Total round-trip cost: ~0.12%–0.15% of notional

At 0.01%/8h (10.95% APY), you recoup round-trip costs in roughly 5 funding periods (~40 hours). A minimum holding period of 48 hours justifies entry. The practical minimum threshold most agents use is 0.02%–0.03% per 8h.

01

Scan all markets for elevated funding

Poll GET /v1/funding-rates across all 275 perpetual markets on Purple Flea. Sort by current funding rate descending. Identify any market paying above your entry threshold (e.g., 0.025%/8h = 27.4% APY gross).

02

Calculate net APY after all costs

Net APY = (Gross funding rate × 3 × 365 × 100) − (Round-trip fee cost annualized). Factor in your expected holding period. A position held for 7 days amortizes entry/exit costs over 21 funding payments, leaving more net yield.

03

Size the position using Kelly Criterion

Use fractional Kelly to size each position based on expected edge and variance of funding rates. Never deploy more than 25% of capital in a single funding arb leg. Diversify across assets to reduce rate-reversal risk.

04

Open both legs simultaneously

Call POST /v1/orders to open the perpetual short. Simultaneously acquire spot exposure (hold ETH or buy via spot API). The two legs should be opened within seconds of each other to minimize basis risk during entry.

05

Monitor rates and margin health hourly

Poll GET /v1/positions and GET /v1/funding-rates every hour. Check margin ratio, unrealized PnL, and current funding rate. Log each funding payment received. Alert if margin ratio exceeds 50%.

06

Exit when rate falls below minimum or flips negative

When funding drops below your exit threshold (e.g., 0.005%/8h) or turns negative, close both legs via POST /v1/orders (close the short) and sell spot. Rotate capital to the next highest-yielding market or park in USDC.


What $10,000 Actually Earns

Concrete projections across different funding rate environments. Capital is split 50/50 between spot (ETH held) and perpetual margin (USDC collateral for the short).

Rate per 8h Gross APY Fees (est.) Net APY Daily on $10k Monthly on $10k Annual on $10k Market Regime
0.005% 5.5% ~1.5% 4.0% $1.10 $33 $400 Below threshold (skip)
0.010% 10.95% ~1.5% 9.5% $2.60 $79 $950 Neutral market
0.030% 32.85% ~1.5% 31.3% $8.58 $261 $3,130 Typical bull market
0.060% 65.7% ~1.5% 64.2% $17.59 $535 $6,420 Strong bull phase
0.100% 109.5% ~1.5% 108.0% $29.59 $900 $10,800 Euphoric bull run
0.300% 328.5% ~1.5% 327.0% $89.59 $2,726 $32,700 Extreme dislocation
The 0.03%/8h case in detail $10,000 capital: $5,000 buys ETH spot, $5,000 is USDC margin for the 1x short. At 0.03%/8h on $5,000 notional: $1.50 per payment × 3 payments/day = $4.50/day = $135/month = $1,642/year. On the full $10,000 deployed that is 16.4% net. If you can deploy $5k margin and hold $5k spot efficiently, the effective yield on deployed capital is 32.85% gross.

Full Python Agent

A production-ready autonomous funding rate agent: market scanning, net APY calculation, Kelly Criterion position sizing, stop-loss logic, and full Purple Flea API integration. Drop this into a cron job or long-running process.

funding_agent.py
#!/usr/bin/env python3 """ Funding Rate Arbitrage Agent — Purple Flea Trading API Autonomous agent that collects funding yield in a delta-neutral position. Requirements: pip install requests """ import time import logging import requests from dataclasses import dataclass, field from typing import Optional from datetime import datetime, timezone logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S", ) log = logging.getLogger("funding_agent") # ─── Configuration ──────────────────────────────────────────────────────────── API_KEY = "pf_sk_your_key_here" BASE_URL = "https://api.purpleflea.com" TOTAL_CAPITAL = 10_000.0 # USD total capital to deploy MAX_POSITIONS = 3 # maximum simultaneous positions CAPITAL_PER_POS = TOTAL_CAPITAL / MAX_POSITIONS # ~$3,333 per leg # Thresholds ENTRY_RATE = 0.00025 # 0.025%/8h = 27.4% gross APY — open position EXIT_RATE = 0.00008 # 0.008%/8h — close when rate falls this low STOP_RATE = -0.00005 # -0.005%/8h — emergency close if rate goes negative # Fees TAKER_FEE = 0.0005 # 0.05% taker fee per leg ROUND_TRIP = TAKER_FEE * 2 # 0.10% total round-trip cost # Risk MARGIN_ALERT = 0.50 # alert at 50% margin used MARGIN_ADD = 0.70 # auto-add margin at 70% MARGIN_STOP = 0.80 # emergency close at 80% KELLY_FRACTION = 0.25 # use 1/4 Kelly for conservatism POLL_INTERVAL = 3600 # check every hour (seconds) # ─── Data Structures ────────────────────────────────────────────────────────── @dataclass class Position: symbol: str position_id: str size_usd: float entry_rate: float entry_price: float opened_at: str funding_collected: float = 0.0 @dataclass class Opportunity: symbol: str funding_rate: float # per 8h as decimal mark_price: float open_interest_usd: float gross_apy: float net_apy: float # ─── Purple Flea API Client ─────────────────────────────────────────────────── class PurpleFleatAPI: def __init__(self, api_key: str): self.session = requests.Session() self.session.headers.update({ "Authorization": f"Bearer {api_key}", "Content-Type": "application/json", "User-Agent": "FundingRateAgent/1.0", }) self.base = BASE_URL def get_funding_rates(self) -> list[dict]: """GET /v1/funding-rates — returns all markets with current rate.""" resp = self.session.get(f"{self.base}/v1/funding-rates", timeout=10) resp.raise_for_status() return resp.json()["markets"] def get_positions(self) -> list[dict]: """GET /v1/positions — returns all open positions.""" resp = self.session.get(f"{self.base}/v1/positions", timeout=10) resp.raise_for_status() return resp.json()["positions"] def get_position(self, position_id: str) -> dict: """GET /v1/positions/{id} — returns single position with margin data.""" resp = self.session.get(f"{self.base}/v1/positions/{position_id}", timeout=10) resp.raise_for_status() return resp.json() def open_short(self, symbol: str, size_usd: float) -> dict: """POST /v1/orders — opens a market short at 1x leverage.""" resp = self.session.post(f"{self.base}/v1/orders", json={ "symbol": symbol, "side": "sell", "type": "market", "size_usd": size_usd, "leverage": 1, "reduce_only": False, }, timeout=15) resp.raise_for_status() return resp.json() def close_position(self, position_id: str) -> dict: """POST /v1/orders — closes a position with a market reduce-only order.""" resp = self.session.post(f"{self.base}/v1/orders", json={ "position_id": position_id, "type": "market", "reduce_only": True, }, timeout=15) resp.raise_for_status() return resp.json() def add_margin(self, position_id: str, amount_usd: float) -> dict: """POST /v1/positions/{id}/margin — adds collateral to avoid liquidation.""" resp = self.session.post( f"{self.base}/v1/positions/{position_id}/margin", json={"action": "add", "amount_usd": amount_usd}, timeout=10 ) resp.raise_for_status() return resp.json() # ─── Kelly Criterion Position Sizing ───────────────────────────────────────── def kelly_size( gross_rate_per_period: float, periods_expected: int = 21, rate_std_dev: float = 0.00015, capital: float = CAPITAL_PER_POS, ) -> float: """ Fractional Kelly position size for a funding arb trade. Edge = expected gross funding collected - round trip fees Variance = std_dev of funding rate over holding period Kelly fraction = Edge / Variance (simplified for near-certain positive EV) Args: gross_rate_per_period: funding rate per 8h (decimal) periods_expected: expected number of funding payments before close rate_std_dev: historical std dev of funding rate changes per period capital: max capital available for this position Returns: Recommended position size in USD """ expected_gross = gross_rate_per_period * periods_expected cost = ROUND_TRIP edge = expected_gross - cost if edge <= 0: return 0.0 # negative edge: skip # Kelly fraction: edge / (variance proxy) variance_proxy = rate_std_dev * (periods_expected ** 0.5) kelly_full = edge / (variance_proxy + 1e-9) kelly_capped = min(kelly_full * KELLY_FRACTION, 1.0) # fractional Kelly, max 100% of alloc recommended = capital * kelly_capped log.info(f"Kelly sizing: edge={edge:.4%} kelly_f={kelly_full:.2f} → size=${recommended:,.0f}") return round(recommended, 2) # ─── Opportunity Analysis ───────────────────────────────────────────────────── def analyze_opportunities(markets: list[dict]) -> list[Opportunity]: """Score and filter markets by net APY after fees.""" opps = [] for m in markets: rate = m.get("funding_rate", 0) gross_apy = rate * 3 * 365 * 100 # amortize round-trip costs over 7-day expected hold (21 periods) fee_annual = (ROUND_TRIP / 21) * 3 * 365 * 100 net_apy = gross_apy - fee_annual if rate >= ENTRY_RATE and net_apy > 0: opps.append(Opportunity( symbol=m["symbol"], funding_rate=rate, mark_price=m.get("mark_price", 0), open_interest_usd=m.get("open_interest_usd", 0), gross_apy=gross_apy, net_apy=net_apy, )) return sorted(opps, key=lambda o: o.net_apy, reverse=True) # ─── Main Agent Loop ────────────────────────────────────────────────────────── class FundingRateAgent: def __init__(self): self.api = PurpleFleatAPI(API_KEY) self.positions: dict[str, Position] = {} # symbol → Position self.total_earned = 0.0 self.cycle = 0 def _now(self) -> str: return datetime.now(timezone.utc).isoformat() def open_position(self, opp: Opportunity) -> None: if opp.symbol in self.positions: return if len(self.positions) >= MAX_POSITIONS: log.info(f"Max positions reached ({MAX_POSITIONS}), skipping {opp.symbol}") return size = kelly_size(opp.funding_rate, capital=CAPITAL_PER_POS) if size < 100: log.info(f"Kelly size too small (${size:.0f}), skipping {opp.symbol}") return try: result = self.api.open_short(opp.symbol, size) pos = Position( symbol=opp.symbol, position_id=result["position_id"], size_usd=size, entry_rate=opp.funding_rate, entry_price=opp.mark_price, opened_at=self._now(), ) self.positions[opp.symbol] = pos log.info( f"OPENED {opp.symbol}: size=${size:,.0f} " f"rate={opp.funding_rate:.4%}/8h " f"net_apy={opp.net_apy:.1f}%" ) except Exception as e: log.error(f"Failed to open {opp.symbol}: {e}") def close_position(self, symbol: str, reason: str) -> None: pos = self.positions.pop(symbol, None) if not pos: return try: result = self.api.close_position(pos.position_id) funding = result.get("funding_collected_usd", 0.0) self.total_earned += funding log.info( f"CLOSED {symbol} [{reason}]: " f"funding=${funding:.4f} total=${self.total_earned:.4f}" ) except Exception as e: log.error(f"Failed to close {symbol}: {e}") def check_margin_health(self) -> None: """Poll each open position and manage margin or force-close if needed.""" for symbol, pos in list(self.positions.items()): try: data = self.api.get_position(pos.position_id) margin_ratio = data.get("margin_ratio", 0) notional = data.get("notional_usd", pos.size_usd) funding_snap = data.get("funding_collected_usd", 0) pos.funding_collected = funding_snap if margin_ratio > MARGIN_STOP: log.warning(f"STOP: {symbol} margin={margin_ratio:.0%}, force-closing") self.close_position(symbol, "margin-stop") elif margin_ratio > MARGIN_ADD: extra = notional * 0.20 self.api.add_margin(pos.position_id, extra) log.info(f"Added ${extra:.0f} margin to {symbol} (ratio was {margin_ratio:.0%})") elif margin_ratio > MARGIN_ALERT: log.warning(f"ALERT: {symbol} margin ratio {margin_ratio:.0%}") except Exception as e: log.error(f"Margin check failed for {symbol}: {e}") def run_cycle(self) -> None: self.cycle += 1 log.info(f"─── Cycle {self.cycle} | Positions: {len(self.positions)} | Earned: ${self.total_earned:.4f} ───") try: markets = self.api.get_funding_rates() except Exception as e: log.error(f"Failed to fetch funding rates: {e}") return # Build current-rate lookup for existing positions rate_map = {m["symbol"]: m.get("funding_rate", 0) for m in markets} # 1. Close positions where rate has dropped or gone negative for symbol in list(self.positions.keys()): current = rate_map.get(symbol, 0) if current <= STOP_RATE: self.close_position(symbol, f"rate-negative ({current:.4%})") elif current < EXIT_RATE: self.close_position(symbol, f"rate-low ({current:.4%})") # 2. Check margin health on remaining positions self.check_margin_health() # 3. Open new positions in top opportunities opportunities = analyze_opportunities(markets) for opp in opportunities[:MAX_POSITIONS]: self.open_position(opp) # 4. Log status summary if self.positions: for sym, pos in self.positions.items(): current_rate = rate_map.get(sym, 0) log.info( f" {sym}: rate={current_rate:.4%}/8h " f"funding_collected=${pos.funding_collected:.4f}" ) def run(self) -> None: log.info("Funding Rate Agent started. Capital: $%.0f", TOTAL_CAPITAL) while True: self.run_cycle() log.info(f"Sleeping {POLL_INTERVAL}s until next cycle...") time.sleep(POLL_INTERVAL) if __name__ == "__main__": FundingRateAgent().run()

Risks to Understand

Funding rate arbitrage has a strong risk/reward profile compared to directional trading, but it is not risk-free. A well-designed agent must model and respond to each of these.

Rate Reversal Risk

The most common and impactful risk. Market sentiment can shift within hours — a bearish event flips funding negative and you go from collecting to paying. The agent above monitors rates every hour and closes positions immediately when the rate crosses the STOP_RATE threshold. Set stop-loss webhooks as a backup.

Liquidation Risk

At 1x leverage, liquidation requires ETH to go to zero — effectively impossible. However, if you use higher leverage for capital efficiency (e.g., 2x to deploy more notional with less margin), a sharp wick can approach your liquidation price before the agent responds. Keep perpetual shorts at 1x for this strategy.

Basis Risk

If your spot leg uses a derivative of the underlying (e.g., stETH instead of ETH, or a wrapped asset) and that derivative temporarily de-pegs, you have net short exposure. Always use the canonical asset as the spot hedge. Hold raw ETH when shorting ETH-PERP. Hold raw BTC when shorting BTC-PERP.

Counterparty Risk

Your short position margin sits in Purple Flea's exchange. In an extreme tail event, exchange insolvency could impair margin. Mitigate by: keeping spot custody separate (on-chain self-custody), not holding more margin than needed for the short, and diversifying across multiple exchanges if deploying large capital.

Execution Risk

Opening two legs simultaneously is critical. If your spot buy fails after the short is open, you have an unhedged short position with directional risk. The agent should open the short first (the exchange confirms immediately), then buy spot — and if the spot acquisition fails, immediately close the short as well.

Fee Erosion

Frequent entry/exit cycles eat into yield. A round-trip costs ~0.10%–0.15% of notional. If you are opening and closing every 2–3 days, fees consume a significant fraction of gross yield. Aim to hold positions for at least 5–7 days when you enter, and only exit early for stop-loss or margin reasons.

Rate Reversal: Historical Frequency During the 2020–2024 period, BTC-PERP funding rates flipped from positive to negative (stayed negative for 2+ consecutive periods) approximately 18% of 8-hour windows. A well-tuned stop at -0.005%/8h would have triggered roughly once every 2–3 weeks on average. Each exit and re-entry costs ~0.10%, so frequent stops erode ~0.5%–1.0%/month in fee drag. Account for this in your net APY projections.

Historical Analysis & Expected Returns

A robust backtest simulates entry/exit decisions using historical 8-hour funding rate data. The Purple Flea API provides up to 90 days of historical funding rate data via GET /v1/funding-rates/history.

backtest.py
""" Funding Rate Arbitrage Backtest Simulates the strategy on historical 8h funding rate data. Fetches data from Purple Flea API: GET /v1/funding-rates/history """ import requests from dataclasses import dataclass from typing import List API_KEY = "pf_sk_your_key_here" BASE_URL = "https://api.purpleflea.com" ENTRY_RATE = 0.00025 # 0.025%/8h EXIT_RATE = 0.00008 # 0.008%/8h STOP_RATE = -0.00005 # -0.005%/8h TAKER_FEE = 0.0005 # 0.05% per leg CAPITAL = 10_000.0 @dataclass class BacktestResult: gross_pnl: float net_pnl: float total_fees: float periods_active: int periods_total: int trades: int max_drawdown_pct: float net_apy: float def fetch_history(symbol: str, days: int = 90) -> List[dict]: """Fetch historical 8h funding rates from Purple Flea.""" resp = requests.get( f"{BASE_URL}/v1/funding-rates/history", headers={"Authorization": f"Bearer {API_KEY}"}, params={"symbol": symbol, "days": days}, timeout=15 ) resp.raise_for_status() return resp.json()["history"] # [{timestamp, funding_rate}, ...] def run_backtest(rates: List[dict], capital: float = CAPITAL) -> BacktestResult: """ Simulate the strategy on a list of {timestamp, funding_rate} dicts. Assumes perfect execution at each period boundary. """ pnl = 0.0 fees = 0.0 in_position = False active = 0 trades = 0 peak_pnl = 0.0 max_dd = 0.0 for period in rates: rate = period["funding_rate"] # Entry logic if not in_position and rate >= ENTRY_RATE: in_position = True entry_fee = capital * TAKER_FEE * 2 # both legs pnl -= entry_fee fees += entry_fee trades += 1 print(f" OPEN @ {period['timestamp'][:10]} rate={rate:.4%}") # Funding collection while in position if in_position: pnl += capital * rate active += 1 # Exit logic: rate dropped or went negative should_exit = (rate < EXIT_RATE) or (rate <= STOP_RATE) if should_exit: exit_fee = capital * TAKER_FEE * 2 pnl -= exit_fee fees += exit_fee in_position = False print(f" CLOSE @ {period['timestamp'][:10]} rate={rate:.4%} pnl=${pnl:+.2f}") # Track max drawdown if pnl > peak_pnl: peak_pnl = pnl dd = (peak_pnl - pnl) / capital * 100 if dd > max_dd: max_dd = dd # Close any open position at end of data if in_position: exit_fee = capital * TAKER_FEE * 2 pnl -= exit_fee fees += exit_fee total_periods = len(rates) years = total_periods / (3 * 365) net_apy = (pnl / capital / years) * 100 if years > 0 else 0 return BacktestResult( gross_pnl=pnl + fees, net_pnl=pnl, total_fees=fees, periods_active=active, periods_total=total_periods, trades=trades, max_drawdown_pct=max_dd, net_apy=net_apy, ) def print_results(symbol: str, r: BacktestResult) -> None: utilization = r.periods_active / r.periods_total * 100 print(f"\n{'═'*50}") print(f" Backtest: {symbol}") print(f" Gross PnL: ${r.gross_pnl:+,.2f}") print(f" Total Fees: ${r.total_fees:,.2f}") print(f" Net PnL: ${r.net_pnl:+,.2f}") print(f" Net APY: {r.net_apy:.1f}%") print(f" Utilization: {utilization:.1f}% of periods") print(f" Trades: {r.trades}") print(f" Max Drawdown: {r.max_drawdown_pct:.2f}%") print(f"{'═'*50}") if __name__ == "__main__": for sym in ["BTC-PERP", "ETH-PERP", "SOL-PERP"]: history = fetch_history(sym, days=90) result = run_backtest(history) print_results(sym, result)
Asset Period Avg Rate/8h Gross APY Est. Net APY Utilization Max Drawdown
BTC-PERP Bull 2020–2021 0.043% 47.1% 45.2% 78% 1.2%
ETH-PERP Bull 2020–2021 0.061% 66.8% 64.7% 82% 0.9%
BTC-PERP Bear 2022 -0.004% −4.4% +0.2% 8% 0.3%
ETH-PERP Bull 2023–2024 0.021% 23.0% 21.3% 64% 0.7%
SOL-PERP Bull 2023–2024 0.035% 38.3% 36.5% 71% 1.1%

Estimates based on public funding rate data. Net APY after 0.10% round-trip fees per trade. Past performance does not guarantee future results.


Multi-Asset Funding Arb Portfolio

Diversifying across BTC, ETH, SOL, and other assets smooths returns and reduces the impact of any single asset's rate reversal. Correlations between funding rates are lower than price correlations — assets can have very different rate regimes.

Portfolio Construction

Run the agent across up to 5 assets simultaneously. Allocate capital proportional to each asset's Sharpe-adjusted funding yield. Higher-yielding assets that also have high rate variance (altcoins) get smaller allocations than lower-yielding but more stable assets (BTC, ETH).

A simple allocation: BTC 35% • ETH 35% • SOL 15% • opportunistic 15%. The opportunistic bucket rotates into whatever altcoin has the highest rate above threshold on any given day.

Cross-Asset Correlation

BTC and ETH funding rates are moderately correlated (r ≈ 0.65–0.75) — they tend to both be high in bull markets and low in bear markets. SOL and smaller altcoin rates are less correlated with BTC (r ≈ 0.35–0.55), providing meaningful diversification. A portfolio of 5 uncorrelated rate streams reduces the variance of returns significantly versus a single-asset position.

portfolio.py
# Multi-asset portfolio configuration PORTFOLIO = { "BTC-PERP": {"alloc": 0.35, "min_rate": 0.00020}, "ETH-PERP": {"alloc": 0.35, "min_rate": 0.00020}, "SOL-PERP": {"alloc": 0.15, "min_rate": 0.00030}, "__opps__": {"alloc": 0.15, "min_rate": 0.00050}, } def build_portfolio(markets, total_capital): rate_map = {m["symbol"]: m for m in markets} targets = {} for sym, cfg in PORTFOLIO.items(): if sym == "__opps__": # opportunistic: find highest rate not already allocated others = [ m for m in markets if m["symbol"] not in PORTFOLIO and m["funding_rate"] >= cfg["min_rate"] ] if others: best = max(others, key=lambda m: m["funding_rate"]) targets[best["symbol"]] = total_capital * cfg["alloc"] continue market = rate_map.get(sym) if market and market["funding_rate"] >= cfg["min_rate"]: targets[sym] = total_capital * cfg["alloc"] return targets # {symbol: usd_size}
Opportunistic Altcoin Yields Small-cap altcoin perpetuals often carry funding rates of 0.1%–0.3%/8h during their individual price spikes — far exceeding BTC and ETH. The risk is higher (rates revert faster, spreads are wider, liquidity is thinner), but a small opportunistic allocation (10–15% of capital) can meaningfully boost portfolio APY. Set stricter stop-loss thresholds (exit at -0.01%/8h instead of -0.005%/8h) for altcoins.

Purple Flea Endpoints Used

All endpoints listed below are available on the Purple Flea Trading API. Authentication uses Bearer tokens: set your API key in the Authorization header.

GET
/v1/funding-rates
Returns current funding rate, mark price, index price, and open interest for all 275 perpetual markets. Sorted by open interest descending. Poll this to identify opportunities.
GET
/v1/funding-rates/history?symbol=BTC-PERP&days=90
Returns historical 8-hour funding rate data for a specific symbol. Up to 90 days. Used for backtesting and regime analysis. Returns array of {timestamp, funding_rate} objects.
POST
/v1/orders
Place a new order. Use side: "sell", type: "market", leverage: 1, reduce_only: false to open a new short. Use reduce_only: true with position_id to close an existing position.
GET
/v1/positions
Returns all open positions with current PnL, margin ratio, funding collected, liquidation price, and notional value. Poll hourly to monitor margin health.
GET
/v1/positions/{id}
Returns a single position by ID with full detail including margin_ratio, unrealized_pnl, funding_collected_usd, and liquidation_price. Use for granular margin monitoring.
POST
/v1/positions/{id}/margin
Add or remove margin from a position. Body: {"action": "add", "amount_usd": 500}. Use to prevent liquidation when margin_ratio exceeds your threshold.
GET
/v1/account/balance
Returns total equity, available margin, unrealized PnL, and per-currency balances. Use to track total portfolio performance and available capital for new positions.
auth_example.py
import requests headers = { "Authorization": "Bearer pf_sk_your_key_here", "Content-Type": "application/json", } # Fetch top 5 funding rate opportunities resp = requests.get( "https://api.purpleflea.com/v1/funding-rates", headers=headers ) markets = resp.json()["markets"] for m in sorted(markets, key=lambda x: x["funding_rate"], reverse=True)[:5]: apy = m["funding_rate"] * 3 * 365 * 100 print(f"{m['symbol']:15} {m['funding_rate']:.4%}/8h {apy:6.1f}% APY") # Output (example): # PEPE-PERP 0.1240%/8h 135.8% APY # ETH-PERP 0.0510%/8h 55.9% APY # BTC-PERP 0.0380%/8h 41.6% APY # SOL-PERP 0.0310%/8h 34.0% APY # AVAX-PERP 0.0280%/8h 30.7% APY

Related Guides & Resources

Start collecting funding with a
delta-neutral agent.

Free to start. 20% referral on all trading fees your agents generate. No KYC required.