PearPear
API IntegrationImportant Terms

Net Funding

What it costs to hold a basket once the paid and received funding legs net out, and why the sign on GET /markets/baskets is the opposite of the trader's-cost convention.

Perpetual futures charge or pay a funding rate periodically. In a pair trade, you're paying funding on one side and receiving it on the other. The net funding rate tells you the combined funding cost (or income) of the entire basket.

How It Works

  • Long positions pay funding when the rate is positive (and receive when negative).
  • Short positions receive funding when the rate is positive (and pay when negative).

Netting the legs takes a sign convention, and Pear uses two opposite ones. Read the sign of the number you have before you colour it.

Where the number comes fromFormulaA positive value means
netFunding on GET /markets/basketsMean of the long legs' rates − mean of the short legs' rates. Unweighted.You pay.
A rate you compute the trader's-cost way, as @pear-protocol/market-sdk doesSubtract each long leg's rate, add each short leg's rate, weighted.You earn.

The rest of this page uses the trader's-cost convention, where negative means you pay. The API field is the other way round.

Two more properties of netFunding on that endpoint:

  • It ignores weights. Every leg counts equally, because a basket there carries no weights at all.
  • It is a difference of means, so a multi-leg basket is not the sum of its legs' contributions.

See Which formula runs where.

Code

This computes the trader's-cost rate: negative means you pay. Weights are percents, matching @pear-protocol/market-sdk, which divides each weight by 100. The gateway's own request bodies take fractions instead.

type Token = { symbol: string; weight: number }; // weight is a percent: 50, not 0.5
type TokenPriceMap = Map<string, { markPrice: number; funding: number }>;

function computeNetFundingRate(
  longTokens: Token[],
  shortTokens: Token[],
  prices: TokenPriceMap,
): number {
  let total = 0;
  for (const t of longTokens) {
    const funding = prices.get(t.symbol)?.funding;
    if (funding !== undefined && t.weight > 0) {
      total += (-funding * t.weight) / 100;
    }
  }
  for (const t of shortTokens) {
    const funding = prices.get(t.symbol)?.funding;
    if (funding !== undefined && t.weight > 0) {
      total += (funding * t.weight) / 100;
    }
  }
  return total;
}

Example

Given a basket: 50% long HYPE (funding: 0.01%), 25% short ASTER (funding: 0.03%), 25% short XPL (funding: −0.02%).

The trader's-cost rate subtracts the long leg and adds the shorts, each times its weight:

net = (−0.01% × 0.50) + (0.03% × 0.25) + (−0.02% × 0.25)
    = −0.005% + 0.0075% + (−0.005%)
    = −0.0025%

Slightly negative, so you are paying a small amount to hold this basket.

GET /markets/baskets reports the same basket as netFunding = +0.005%: the mean long rate 0.01% minus the mean short rate 0.005%, with the weights ignored. Same conclusion, opposite sign, different magnitude.

The rate is per interval, not per year

r is the venue's own funding rate for one funding interval, passed through untouched. It is not an APR, and the interval is not the same everywhere:

  • Hyperliquid funds hourly.
  • The centralized-exchange connectors fund on their own schedules, typically every 8 hours.

So a Hyperliquid rate and a Binance rate are not comparable side by side until you put them on the same basis. GET /funding gives you what you need to do that: for each instrument it returns r, the rate as a decimal string, and nft, the next funding time in milliseconds. Consecutive nft values give you the interval.

Annualized funding

@pear-protocol/market-sdk annualizes a funding series in two steps. Both are worth reproducing if you build your own.

  1. Bucket the raw rates. Rates inside one UTC day, week, or month are summed, not averaged, because each one was actually charged. Buckets are 1d, 1w (weeks start Monday), and 1M. The window they cover is 1w, 1m, or 1y.
  2. Multiply to a year. A bucketed series multiplies by a fixed factor: 365 for 1d, 52 for 1w, 12 for 1M. An unbucketed series multiplies by the milliseconds in a 365-day year divided by the average observed gap between entries, so a venue's real interval is inferred rather than assumed.

An unbucketed series of fewer than two entries has no gap to infer from, so it comes back unannualized.

Reading it from the API

You do not have to compute a forecast rate to display it. GET /markets/baskets returns netFunding on every basket, already netted across the legs, on the sign convention in the table above.

For the raw inputs, GET /funding returns the current rate per instrument, keyed by instrument ID:

{ "funding": { "0": { "r": "0.0000125", "nft": 1767225600000 } } }

connector is required on that call. The endpoint returns no prices; mid and mark are on GET /prices.

What you actually paid

The computed rate is a forecast. What a position has really paid or earned is on the position itself. GET /positions returns totalFundingPayment, keyed by instrument ID, in USD:

{ "totalFundingPayment": { "0": "-1.25", "1": "+0.84" } }

Negative means you paid. Here the basket has paid 1.25 USD of funding on its BTC leg and earned 0.84 USD on its ETH leg, for a net cost of 0.41 USD over the position's life. Sum the values to get the basket's net funding to date.

Trading on it

A funding rate is a trigger condition, so a basket can open when funding turns in your favour:

{
  "condition": {
    "type": "funding_rate",
    "data": {
      "connector": "hyperliquid",
      "symbol": "0",
      "track": "PEAK",
      "threshold": 0.0003
    }
  }
}

This condition is the one exception to Pear's threshold rule: threshold is a plain signed number here, so a negative funding rate is a valid target. Every other condition requires a threshold above 0. connector is required, and names the venue whose rate is watched.

See Trigger Order.

On this page