Funding Rate Arbitrage Across Exchanges: How to Profit from Rate Differences
The funding rate on ETHUSDT is 0.01% on Binance and 0.035% on Bybit. The same coin, the same moment in time, but rates differ by 3.5x. Someone pays more, someone pays less. And someone profits from this difference.
Funding rate arbitrage is one of the few strategies in crypto that does not depend on market direction. You do not predict the price. You extract profit from the structural divergence of rates between venues.
Why Funding Rates Differ Across Exchanges
The funding rate is a mechanism that anchors the price of a perpetual futures contract to the spot price. Each exchange calculates it independently based on its own data:
- Trader composition. Binance is dominated by retail traders who tend to go long. Bybit and OKX have more professional participants. Different long/short balance leads to different funding.
- Calculation formula. Each exchange uses its own formula. Binance factors in the premium index and interest rate. Bybit and OKX do the same but with different weights and averaging periods.
- Liquidity. On less liquid exchanges, the premium (difference between futures and spot) is more volatile, causing funding to fluctuate more widely.
- Payment frequency. Most exchanges pay funding every 8 hours (00:00, 08:00, 16:00 UTC). But some (Bybit for certain pairs, dYdX) pay every hour, creating additional opportunities.
Typical Divergences

In a calm market, funding rates on major exchanges are close — the difference is 0.001-0.005%. But during periods of heightened volatility, divergences grow:
| Market Phase | Binance | Bybit | OKX | dYdX | Spread |
|---|---|---|---|---|---|
| Calm | 0.01% | 0.012% | 0.009% | 0.01% | ~0.003% |
| Bullish trend | 0.03% | 0.05% | 0.025% | 0.04% | ~0.025% |
| Extreme bullish | 0.1% | 0.2% | 0.08% | 0.15% | ~0.12% |
| Bearish trend | -0.02% | -0.01% | -0.025% | -0.015% | ~0.015% |
A spread of 0.025% per 8 hours is 0.075% per day. With a position size of 75/day or ~$2,250/month — without directional risk.
Basic Arbitrage Mechanics

The idea is simple: open opposite positions on two exchanges so that you receive funding on one and pay less on the other.
Example
Binance: funding rate = +0.01% (longs pay shorts) Bybit: funding rate = +0.04% (longs pay shorts)
Actions:
- Open a short on Bybit — receive 0.04% every 8 hours
- Open a long on Binance — pay 0.01% every 8 hours
- Positions are mirrored — price risk is neutral
- Net profit: 0.04% - 0.01% = 0.03% per 8 hours
Per day (3 payments): 0.09%. Per month: ~2.7%. Without directional risk.
def funding_arbitrage_pnl(
rate_short_exchange: float, # rate on the exchange where we short
rate_long_exchange: float, # rate on the exchange where we long
position_size: float, # position size in USD
payments_per_day: int = 3,
days: int = 30,
) -> float:
"""
PnL from funding rate arbitrage over a period.
With positive funding: short receives, long pays.
With negative funding: short pays, long receives.
"""
spread = rate_short_exchange - rate_long_exchange
daily_pnl = spread * payments_per_day * position_size
return daily_pnl * days
pnl = funding_arbitrage_pnl(0.0004, 0.0001, 100_000, days=30)
Risks and Pitfalls

The strategy looks like "free money." It is not. There are several serious risks.
1. Price Divergence Between Exchanges
Positions on different exchanges are not at the same price. The spread between Binance and Bybit is usually 0.01-0.05%, but during moments of high volatility it can reach 0.5-1%. If you do not open positions simultaneously, the divergence can exceed the funding profit.
Solution: simultaneous opening via API with minimal latency. Ideally — collocated servers near both exchanges.
2. Funding Rate Changes
You opened positions at a spread of 0.03%. An hour later the spread narrowed to 0.005% or reversed. Now you are paying on both exchanges.
Solution: real-time spread monitoring and automatic closing when the spread falls below a threshold.
def should_close(
current_spread: float,
entry_spread: float,
min_spread: float = 0.0001, # 0.01%
trading_costs: float = 0.0005, # 0.05% for opening + closing
) -> bool:
"""
Close the position if the spread has fallen below the threshold
or if the current spread does not cover trading costs.
"""
return current_spread < min_spread or current_spread < trading_costs
3. Trading Commissions
Opening and closing positions on two exchanges means 4 orders. At a maker fee of 0.02% and taker fee of 0.05%:
- Optimistic scenario (all maker):
- Pessimistic scenario (all taker):
For commissions to break even, the position must be held long enough:
def breakeven_days(
total_commissions_pct: float, # total commissions in %
spread: float, # funding rate spread
payments_per_day: int = 3,
) -> float:
daily_income = spread * payments_per_day
return total_commissions_pct / daily_income if daily_income > 0 else float('inf')
4. Margin Requirements
Positions on both exchanges require collateral. At 5x leverage on each exchange with a $100K position:
- Binance: $20K collateral
- Bybit: $20K collateral
- Total locked: **100K position
Return on capital:
At 10x leverage, collateral drops to $20K, ROC rises to 13.5%. But the risk of liquidation from price divergence also increases.
5. Liquidation Risk
If the asset price moves sharply, one of the positions generates an unrealized loss. On the exchange with the losing position, margin must be maintained. If margin is insufficient — liquidation. Meanwhile, the profit on the other exchange does not help — it is in a different account.
Solution:
- Keep a margin reserve (at least 2x the minimum)
- Set up margin level alerts
- Automatic rebalancing: when an imbalance occurs — transfer funds between exchanges
Funding Rate Monitoring System

The first step toward arbitrage is data collection. You need to track funding rates across all exchanges of interest in real time.
import asyncio
import ccxt.pro as ccxt
from dataclasses import dataclass
from datetime import datetime
@dataclass
class FundingSnapshot:
exchange: str
symbol: str
rate: float
next_funding_time: datetime
timestamp: datetime
class FundingMonitor:
"""
Monitor funding rates across multiple exchanges.
"""
def __init__(self, symbols: list[str], exchanges: list[str]):
self.symbols = symbols
self.exchanges = {
name: getattr(ccxt, name)() for name in exchanges
}
self.latest: dict[str, dict[str, FundingSnapshot]] = {}
async def fetch_funding(self, exchange_name: str, exchange, symbol: str):
"""Fetch current funding rate from an exchange."""
try:
funding = await exchange.fetch_funding_rate(symbol)
return FundingSnapshot(
exchange=exchange_name,
symbol=symbol,
rate=funding['fundingRate'],
next_funding_time=datetime.fromtimestamp(
funding['fundingTimestamp'] / 1000
),
timestamp=datetime.utcnow(),
)
except Exception as e:
print(f"Error fetching {exchange_name} {symbol}: {e}")
return None
async def scan(self) -> list[dict]:
"""
Scan all exchanges and find arbitrage opportunities.
"""
tasks = []
for ex_name, ex in self.exchanges.items():
for symbol in self.symbols:
tasks.append(self.fetch_funding(ex_name, ex, symbol))
snapshots = await asyncio.gather(*tasks)
snapshots = [s for s in snapshots if s is not None]
by_symbol: dict[str, list[FundingSnapshot]] = {}
for s in snapshots:
by_symbol.setdefault(s.symbol, []).append(s)
opportunities = []
for symbol, rates in by_symbol.items():
rates.sort(key=lambda x: x.rate)
lowest = rates[0] # long here (pay less)
highest = rates[-1] # short here (receive more)
spread = highest.rate - lowest.rate
opportunities.append({
'symbol': symbol,
'long_exchange': lowest.exchange,
'long_rate': lowest.rate,
'short_exchange': highest.exchange,
'short_rate': highest.rate,
'spread': spread,
'annualized': spread * 3 * 365 * 100, # in % annualized
})
return sorted(opportunities, key=lambda x: -x['spread'])
Sample Output
Symbol | Long @ | Rate | Short @ | Rate | Spread | APR
-----------+-------------+---------+-------------+---------+---------+--------
ETHUSDT | Binance | 0.010% | Bybit | 0.040% | 0.030% | 32.9%
BTCUSDT | OKX | 0.008% | Binance | 0.020% | 0.012% | 13.1%
SOLUSDT | Binance | 0.015% | dYdX | 0.055% | 0.040% | 43.8%
ARBUSDT | Bybit | 0.005% | OKX | 0.030% | 0.025% | 27.4%
Execution: Simultaneous Position Opening

It is critically important to open the long and short as simultaneously as possible to avoid directional risk exposure.
import asyncio
async def execute_arbitrage(
long_exchange,
short_exchange,
symbol: str,
size: float,
max_slippage_pct: float = 0.05,
):
"""
Simultaneously open a long and short on two exchanges.
"""
long_ticker = await long_exchange.fetch_ticker(symbol)
short_ticker = await short_exchange.fetch_ticker(symbol)
price_spread = abs(
long_ticker['ask'] - short_ticker['bid']
) / long_ticker['ask'] * 100
if price_spread > max_slippage_pct:
raise ValueError(
f"Price spread {price_spread:.3f}% exceeds max slippage"
)
long_order, short_order = await asyncio.gather(
long_exchange.create_market_buy_order(symbol, size),
short_exchange.create_market_sell_order(symbol, size),
)
return long_order, short_order
Position Management
After opening, continuous monitoring is required:
- Funding rate spread. If the spread contracts below the threshold — close.
- Margin balance. If margin on one exchange falls below the safe level — rebalance or close.
- Price divergence. If unrealized P&L on one side exceeds the limit — close.
async def monitor_and_manage(
long_exchange,
short_exchange,
symbol: str,
size: float,
min_spread: float = 0.0001,
max_unrealized_loss_pct: float = 2.0,
check_interval: int = 60,
):
"""
Monitor an open arbitrage position.
"""
while True:
long_funding = await long_exchange.fetch_funding_rate(symbol)
short_funding = await short_exchange.fetch_funding_rate(symbol)
current_spread = (
short_funding['fundingRate'] - long_funding['fundingRate']
)
long_balance = await long_exchange.fetch_balance()
short_balance = await short_exchange.fetch_balance()
long_positions = await long_exchange.fetch_positions([symbol])
short_positions = await short_exchange.fetch_positions([symbol])
long_upnl = long_positions[0]['unrealizedPnl'] if long_positions else 0
short_upnl = short_positions[0]['unrealizedPnl'] if short_positions else 0
total_upnl_pct = (long_upnl + short_upnl) / size * 100
if current_spread < min_spread:
print(f"Spread collapsed: {current_spread:.4%}")
await close_both(long_exchange, short_exchange, symbol, size)
break
if abs(total_upnl_pct) > max_unrealized_loss_pct:
print(f"Unrealized loss exceeded: {total_upnl_pct:.2f}%")
await close_both(long_exchange, short_exchange, symbol, size)
break
await asyncio.sleep(check_interval)
Advanced Variants
Spot-Perp Arbitrage

Instead of futures on two exchanges, you can use spot + futures on a single exchange:
- Buy spot (no funding)
- Short the perpetual futures (receive funding when the rate is positive)
Advantage: everything on one exchange, simpler margin management. Disadvantage: only works with positive funding (longs pay shorts), which is observed ~70% of the time during a bull market.
def spot_perp_carry(
funding_rate: float, # current funding rate
spot_fee: float = 0.001, # spot commission (0.1%)
perp_fee: float = 0.0005, # futures commission (0.05%)
leverage: int = 1,
) -> dict:
"""
Calculate the yield of a spot-perp carry trade.
"""
total_fees = (spot_fee + perp_fee) * 2 # opening + closing
daily_income = funding_rate * 3
breakeven_days = total_fees / daily_income if daily_income > 0 else float('inf')
return {
'daily_income_pct': daily_income * 100,
'monthly_income_pct': daily_income * 30 * 100,
'annualized_pct': daily_income * 365 * 100,
'total_fees_pct': total_fees * 100,
'breakeven_days': breakeven_days,
}
result = spot_perp_carry(0.0003)
Multi-Exchange Arbitrage

When monitoring 5+ exchanges simultaneously, you can find more favorable opportunities. Algorithm:
- Collect funding rates from all exchanges
- Find the pair with the maximum spread
- Check liquidity and order book depth on both exchanges
- If spread > threshold — open positions
- Continuously rescan: if the best pair changes — rotate
def find_best_pair(
rates: dict[str, float], # {"binance": 0.01, "bybit": 0.04, "okx": 0.02}
min_spread: float = 0.0002,
) -> tuple[str, str, float] | None:
"""
Find the exchange pair with the maximum funding rate spread.
Returns: (long_exchange, short_exchange, spread) or None.
"""
exchanges = list(rates.keys())
best = None
for i, ex_long in enumerate(exchanges):
for ex_short in exchanges[i+1:]:
if rates[ex_long] < rates[ex_short]:
spread = rates[ex_short] - rates[ex_long]
long_ex, short_ex = ex_long, ex_short
else:
spread = rates[ex_long] - rates[ex_short]
long_ex, short_ex = ex_short, ex_long
if spread >= min_spread:
if best is None or spread > best[2]:
best = (long_ex, short_ex, spread)
return best
Predicting the Funding Rate

The funding rate is calculated using a formula that includes the premium index — the difference between the futures price and the spot price. The premium updates more frequently than funding (every minute vs every 8 hours). This means you can predict the next funding rate minutes or hours before the payment.
def predict_next_funding(
premium_index: float,
interest_rate: float = 0.0001, # 0.01% per 8h (standard)
clamp_range: float = 0.0005, # ±0.05%
) -> float:
"""
Predict the next funding rate based on the current premium index.
Binance formula: FR = clamp(Premium - Interest, -0.05%, 0.05%) + Interest
"""
diff = premium_index - interest_rate
clamped = max(-clamp_range, min(clamp_range, diff))
return clamped + interest_rate
Knowing the predicted funding rate, you can open positions before the payment, when the spread has not yet attracted the attention of other arbitrageurs.
Infrastructure Requirements

For serious funding rate arbitrage, you need infrastructure:
| Component | Minimum | Optimal |
|---|---|---|
| Server | Cloud VPS | Collocated near exchanges |
| Latency | < 500ms | < 50ms |
| API keys | 2 exchanges | 5+ exchanges |
| Capital per exchange | $10K each | $50K+ each |
| Monitoring | Logs + alerts | Dashboard + auto-rebalancing |
| Data | REST API polling | WebSocket streaming |
Economics at Different Scales
| Capital | Position (5x) | Spread 0.03% | Monthly PnL | ROC |
|---|---|---|---|---|
| $10K | $25K | 0.03% | ~$675 | ~6.75% |
| $50K | $125K | 0.03% | ~$3,375 | ~6.75% |
| $200K | $500K | 0.03% | ~$13,500 | ~6.75% |
ROC does not depend on scale (given sufficient liquidity). But the absolute profit at $10K capital may not justify infrastructure costs and time.
Conclusion
Funding rate arbitrage is a structural, delta-neutral strategy. It does not require price prediction, but it requires:
- Infrastructure — real-time monitoring of rates across multiple exchanges
- Execution speed — simultaneous opening of positions across different venues
- Risk management — controlling margin, price divergence, and spread changes
- Capital — profit is proportional to position size
Funding rate spreads are not constant. They widen during periods of volatility and narrow during calm periods. The task is to automatically find and exploit divergences while they exist.
For more on how funding rates affect leveraged strategies — see the article Funding Rates Kill Your Leverage: Why PnL×50x Is a Fiction.
Useful Links
- Binance — Funding Rate History
- Binance — Introduction to Funding Rates
- Bybit — Understanding Funding Rates
- dYdX — Perpetual Funding Rate Mechanism
- Coinglass — Funding Rate Monitor
Citation
@article{soloviov2026fundingarbitrage,
author = {Soloviov, Eugen},
title = {Funding Rate Arbitrage Across Exchanges: How to Profit from Rate Differences},
year = {2026},
url = {https://marketmaker.cc/ru/blog/post/funding-rate-arbitrage-cross-exchange},
description = {How funding rate arbitrage works across crypto exchanges, why rates differ on Binance, Bybit, OKX and dYdX, and how to build a monitoring and execution system.}
}
MarketMaker.cc Team
Сандық зерттеулер және стратегия