# Funding-rate carry on perpetuals

> Measure what a hedged short perpetual earned in funding, priced at the mark, with a rule that steps aside when funding turns negative.

Source: https://getmarkethistory.com/docs/guides/funding-carry

A perpetual's funding moves cash between longs and shorts at every funding event. When the rate
is positive, longs pay shorts. Short one BTC on the perpetual, buy one BTC spot, and price moves
cancel: what is left is the funding, plus the drift between the two prices. This guide measures
the funding leg over one quarter, in USDT, priced at the mark like the payment itself.

**You need** the [Python client](https://getmarkethistory.com/docs/quickstart#4-into-pandas) with the `pandas` extra, and a
key with bought bars. One quarter of BTCUSDT is about 270 funding rows and 131,000 one-minute
mark prices: about 131,500 bars, 66 cents at the first band's rate.

## 1. Fetch funding and mark prices

```python
import pandas as pd
from markethistory import Client

mh = Client()  # reads MARKET_HISTORY_KEY
span = {"start": "2024-01-01", "end": "2024-03-31"}

funding = pd.DataFrame(mh.each(mh.funding, "BTCUSDT", limit=100_000, **span))
mark = pd.DataFrame(mh.each(mh.prices, "BTCUSDT", "mark", limit=100_000, **span))
for df in (funding, mark):
    df["timestamp"] = pd.to_datetime(df["timestamp"], utc=True, format="ISO8601")
```

`each()` follows the cursor to the end of the range. Funding rows are `timestamp`, `rate` (a
fraction: 0.0001 is 0.01%) and `interval_h`; mark rows are one-minute OHLC.

## 2. Price each payment at the mark

```python
mark = mark[["timestamp", "open"]].rename(columns={"open": "mark"})
df = pd.merge_asof(funding.sort_values("timestamp"), mark, on="timestamp")

qty = 1.0  # BTC short on the perpetual, long in spot
df["income"] = (
    qty * df["mark"] * df["rate"]
)  # USDT received by the short (paid when < 0)
df["apr"] = df["rate"] * 365 * 24 / df["interval_h"]  # the rate as a yearly yield
```

A funding row is stamped a few milliseconds after the hour, so `merge_asof` (backward by
default) pairs it with the minute that opened on the hour, and that minute's `open` is the mark
at the payment. `interval_h` turns each rate into a yearly figure even when a contract changes
its funding interval.

## 3. A rule: hold only while funding pays

```python
fee = 0.0005  # per leg, per trade, on notional
df["hold"] = (df["rate"].rolling(3).mean().shift(1) > 0).astype(float)
switches = df["hold"].diff().abs().fillna(df["hold"])
df["pnl"] = df["hold"] * df["income"] - switches * 2 * fee * qty * df["mark"]

days = (df["timestamp"].iloc[-1] - df["timestamp"].iloc[0]) / pd.Timedelta(days=1)
notional = qty * df["mark"].mean()
print(f"mean funding, yearly: {df['apr'].mean():.1%}")
print(f"always hedged: {df['income'].sum():,.0f} USDT")
print(
    f"rule: {df['pnl'].sum():,.0f} USDT, {df['pnl'].sum() / notional * 365 / days:.1%} a year"
)
```

The decision for each payment uses the three rates before it (`shift(1)`), never the one being
paid. Every switch in or out trades both legs, hence `2 * fee`.

## What this leaves out

- **The basis.** The hedge earns or loses the change in the gap between the perpetual and spot.
  The index price is the spot reference: fetch it like the mark (`mh.prices(..., "index")`) and
  add the change in `mark − index` over each holding period.
- **Margin.** The short needs collateral that earns nothing here.
- **Execution.** Mark and index are reference prices: nothing trades at them, so the fills of
  the two legs differ from them by the spread.
