Strategy

Hyperliquid Funding Rate Bot: Earn 20–60% APY on Autopilot

Mar 6, 2026 8 min read Purple Flea Team

Funding rate harvesting is one of the most reliable, non-directional yield strategies in crypto. When perpetual futures markets become crowded with leveraged longs, they pay shorts a continuous stream of funding payments every 8 hours. The edge is structural, not predictive — you do not need to know where the price is going. You only need to be on the right side when the market is overextended.

This guide shows you how to build a fully autonomous funding rate bot that operates through the Purple Flea Trading API, maintains delta-neutrality, and historically targets 20–60% APY depending on market conditions.

0.01%
Avg funding rate per 8h in bull markets
~27%
APY from 0.01% every 8h (1095 payments/year)
0%
Directional exposure when fully delta-neutral

What Are Funding Rates?

Perpetual futures have no expiry. To keep their price anchored to the underlying spot price, exchanges use a funding mechanism: every 8 hours, one side pays the other based on the deviation between the perpetual price and the spot price.

When the perpetual trades above spot (positive funding rate), longs pay shorts. This happens when the market is bullish and retail traders pile into leveraged long positions. The funding rate is a tax on overleveraged longs — and shorts collect it.

Market ConditionPerpetual vs SpotFunding RateWho Pays Whom
Bullish euphoriaPerp > Spot (premium)Positive (0.05–0.3%/8h)Longs pay shorts
Mild bullishPerp slightly > SpotPositive (0.01–0.05%/8h)Longs pay shorts
NeutralPerp ≈ Spot~0%No significant payment
Bearish / fearfulPerp < Spot (discount)NegativeShorts pay longs

The strategy: when funding is positive and sufficiently high, open a short perpetual position to collect funding payments. Simultaneously, go long spot (or long on a separate venue) to cancel out price exposure. Net result: zero price risk, pure funding yield.

APY Calculation

Funding is paid 3 times per day (every 8 hours). Annualized yield:

# Annualized yield from funding rate
def funding_apy(rate_per_8h: float) -> float:
    """
    rate_per_8h: e.g. 0.01 for 0.01%
    Returns annualized yield percentage
    """
    payments_per_year = 3 * 365  # 1095 payments
    return rate_per_8h * payments_per_year

# Examples:
print(funding_apy(0.01))   # 10.95% APY — baseline bull market
print(funding_apy(0.05))   # 54.75% APY — elevated funding period
print(funding_apy(0.10))   # 109.5% APY — extreme funding (ETH peaks in 2021)

After trading fees (approximately 0.02% taker per side = 0.04% round trip), net APY at 0.05% funding is roughly 52% — still exceptional for a market-neutral strategy.

Delta-Neutral Setup

The critical design requirement: for every 1 unit short on the perpetual, you must hold 1 unit long somewhere. Options:

Purple Flea's trading API handles the perpetual short side. For the long hedge, we use the spot wallet API:

import requests

API_KEY = "pf_live_YOUR_KEY"
TRADING_BASE = "https://trading.purpleflea.com/api"
WALLET_BASE  = "https://wallet.purpleflea.com/api"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}

def open_funding_trade(symbol: str, size_usd: float):
    """Open delta-neutral funding position: short perp + long spot."""

    # Step 1: Open short perpetual to collect funding
    perp_order = requests.post(f"{TRADING_BASE}/orders", headers=HEADERS, json={
        "symbol": f"{symbol}-PERP",
        "side": "short",
        "size_usd": size_usd,
        "leverage": 1,       # 1x only — no directional leverage
        "type": "market",
        "reduce_only": False
    }).json()

    # Step 2: Buy equivalent spot to hedge delta
    spot_order = requests.post(f"{WALLET_BASE}/spot/buy", headers=HEADERS, json={
        "asset": symbol,
        "usd_amount": size_usd,
        "type": "market"
    }).json()

    return {
        "perp_order_id": perp_order["order_id"],
        "spot_order_id": spot_order["order_id"],
        "perp_fill": perp_order.get("fill_price"),
        "spot_fill": spot_order.get("fill_price")
    }

Monitoring and Fetching Funding Rates

import time
from dataclasses import dataclass
from typing import List

@dataclass
class FundingOpportunity:
    symbol: str
    rate_8h: float       # as percentage: 0.05 = 0.05%
    apy: float
    oi_usd: float
    predicted_rate: float

def scan_funding_rates(min_apy: float = 15.0) -> List[FundingOpportunity]:
    """Scan all markets and return those with APY above threshold."""
    r = requests.get(f"{TRADING_BASE}/markets/funding-rates", headers=HEADERS)
    markets = r.json()["markets"]

    opportunities = []
    for m in markets:
        rate = m["funding_rate"]          # current 8h rate
        predicted = m["predicted_rate"]    # next 8h predicted rate
        apy = rate * 1095               # annualize

        if apy >= min_apy and predicted > 0:  # only positive funding
            opportunities.append(FundingOpportunity(
                symbol=m["base"],
                rate_8h=rate,
                apy=apy,
                oi_usd=m["open_interest_usd"],
                predicted_rate=predicted
            ))

    # Sort by APY descending
    return sorted(opportunities, key=lambda x: x.apy, reverse=True)

# Example: find best opportunities right now
opps = scan_funding_rates(min_apy=20.0)
for o in opps[:5]:
    print(f"{o.symbol}: {o.rate_8h:.3f}%/8h = {o.apy:.1f}% APY | OI: ${o.oi_usd/1e6:.0f}M")

Full Bot Implementation

import time
import logging
from datetime import datetime, timedelta

logger = logging.getLogger("funding-bot")

class FundingRateBot:
    def __init__(self,
                 api_key: str,
                 capital_usd: float,
                 min_apy: float = 20.0,
                 max_positions: int = 3,
                 rebalance_threshold: float = 0.02):
        self.api_key = api_key
        self.capital = capital_usd
        self.min_apy = min_apy
        self.max_positions = max_positions
        self.rebalance_threshold = rebalance_threshold
        self.positions = {}  # symbol -> position data
        self.total_funding_earned = 0.0

    def run_cycle(self):
        """Execute one monitoring cycle."""
        logger.info(f"Cycle start | Positions: {len(self.positions)} | Capital: ${self.capital:,.0f}")

        # 1. Collect any pending funding payments
        self._collect_funding()

        # 2. Check existing positions — exit if rate turned negative
        self._manage_existing_positions()

        # 3. Scan for new opportunities
        if len(self.positions) < self.max_positions:
            opps = scan_funding_rates(self.min_apy)
            for opp in opps:
                if opp.symbol not in self.positions:
                    size = self.capital / self.max_positions
                    logger.info(f"Opening position: {opp.symbol} @ {opp.rate_8h:.3f}%/8h ({opp.apy:.0f}% APY)")
                    result = open_funding_trade(opp.symbol, size)
                    self.positions[opp.symbol] = {
                        "perp_id": result["perp_order_id"],
                        "spot_id": result["spot_order_id"],
                        "size_usd": size,
                        "entry_rate": opp.rate_8h,
                        "opened_at": datetime.utcnow()
                    }
                    if len(self.positions) >= self.max_positions:
                        break

        # 4. Check delta drift and rebalance if needed
        self._rebalance_delta()

    def _collect_funding(self):
        r = requests.get(f"{TRADING_BASE}/funding-history", headers=HEADERS)
        payments = r.json().get("payments", [])
        period_total = sum(p["amount"] for p in payments)
        self.total_funding_earned += period_total
        if payments:
            logger.info(f"Funding collected: ${period_total:+.4f} | Total: ${self.total_funding_earned:.4f}")

    def _manage_existing_positions(self):
        for symbol in list(self.positions.keys()):
            r = requests.get(f"{TRADING_BASE}/markets/{symbol}-PERP/funding", headers=HEADERS)
            current_rate = r.json().get("funding_rate", 0)

            # Exit if rate turned negative (we'd be paying instead of earning)
            if current_rate < -0.005:  # -0.005% threshold
                logger.info(f"Closing {symbol}: rate turned negative ({current_rate:.3f}%)")
                self._close_position(symbol)

    def _rebalance_delta(self):
        # Check spot/perp balance drift due to price movement
        # If price moved > rebalance_threshold, adjust spot holding
        for symbol, pos in self.positions.items():
            r = requests.get(f"{TRADING_BASE}/positions", headers=HEADERS)
            perp_pos = next((p for p in r.json()["positions"]
                           if p["symbol"] == f"{symbol}-PERP"), None)
            if perp_pos:
                delta = abs(perp_pos.get("delta", 0))
                if delta > self.rebalance_threshold:
                    logger.info(f"Rebalancing {symbol}: delta drift = {delta:.2%}")
                    # Adjust spot position to restore neutrality

    def _close_position(self, symbol: str):
        pos = self.positions.pop(symbol, None)
        if pos:
            # Close perpetual short
            requests.delete(f"{TRADING_BASE}/positions/{symbol}-PERP", headers=HEADERS)
            # Sell spot holding
            requests.post(f"{WALLET_BASE}/spot/sell", headers=HEADERS, json={
                "asset": symbol, "usd_amount": pos["size_usd"], "type": "market"
            })

    def run(self, interval_minutes: int = 60):
        """Run the bot continuously, checking every interval."""
        logger.info(f"Funding rate bot started | Capital: ${self.capital:,.0f} | Min APY: {self.min_apy}%")
        while True:
            try:
                self.run_cycle()
            except Exception as e:
                logger.error(f"Cycle error: {e}")
            time.sleep(interval_minutes * 60)

# Launch
bot = FundingRateBot(
    api_key="pf_live_YOUR_KEY",
    capital_usd=10_000,
    min_apy=20.0,
    max_positions=3
)
bot.run(interval_minutes=60)

Historical Performance Reference

Market PeriodAvg Funding Rate (8h)Gross APYEst. Net APY (after fees)
Bear market (2022)-0.005%NegativeStrategy paused
Recovery (2023 H1)0.005%5.5%~3%
Bull market (2024)0.025%27.4%~24%
Peak euphoria (2025 Q1)0.065%71.2%~67%
Current (2026 avg)0.020%21.9%~19%

Risk Factors and Mitigations

Tax note: Funding payments are typically treated as ordinary income in most jurisdictions. Consult a tax professional familiar with DeFi and perpetual futures before scaling capital.

Optimal Market Selection Criteria

Not all high-funding markets are worth entering. Apply these filters before opening a position:

Access Hyperliquid Funding Rates via Purple Flea

Purple Flea provides a unified API across 275 perpetual markets. Real-time funding data, automated position management, and wallet integration — all in one place.

Open Trading API    Agent Onboarding