Funding Rates on BitMEX are set by the formulae given in the docs at page https://www.bitmex.com/app/perpetualContractsGuide#Funding-Rate-Calculations
Sometimes these formulas result in funding rates that seem higher or lower than anticipated by some users. However, the numbers can be reproduced using the procedure described here, taking contract ONDOUSDT as an example. Python snippets for each calculation are provided at the bottom of the page.
The Funding Rate payment is set using the formula described in the link above,
Funding Rate (F) = Premium Index (P) + clamp(Interest Rate (I) - Premium Index (P), 0.05%, -0.05%)
The Premium Index in this formula is the value of the .ONDOUSDTPI8H index at the end of the PREVIOUS funding rate 8-hour window. This means that for the payment at 2025-01-14 04:00:00, we need to use the value of the Premium Index at the end of the previous window, at 2025-01-13 20:00:00, which we can get out of the API here: https://www.bitmex.com/api/v1/trade?symbol=.ONDOUSDTPI8H&count=1&reverse=true
That returns the following values:
timestamp | symbol | side | size | price | tickDirection | trdType |
2025-01-13T20:00:00.000Z | .ONDOUSDTPI8H | Buy | 0 | -0.00184 | MinusTick | Referential |
The interest rate is fixed at 0.01% for this symbol (see https://www.bitmex.com/app/contract/ONDOUSDT) so we can calculate the funding payment at this payment time as F = -0.00184 + clamp(0.00185, 0.0005, -0.0005) = -0.00184 + 0.0005 = -0.00134
This matches the value shown in the GUI:
The 8H value is calculated from the arithmetic mean of the .ONDOUSDTPI index over the previous 8-hour window, which is printed minutely and can be obtained from the API here: https://www.bitmex.com/api/v1/trade?symbol=.ONDOUSDTPI&count=500&reverse=false&startTime=2025-01-13T12%3A01%3A00.000Z&endTime=2025-01-13T20%3A00%3A00.000Z
Taking the mean of the “price” fields from this output gives -0.001839566, which matches the .ONDOUSDTPI8H after rounding.
We calculate the Premium Index for each symbol at the end of each minute, using data from the instrument table, according to the formula given in the link above, Premium Index (P) = (Max(0, Impact Bid Price - Mark Price) - Max(0, Mark Price - Impact Ask Price)) / Spot Price + Fair Basis used in Mark Price. This is then inserted into the Premium Index for that minute in the index history.
The Instrument Table record for the symbol: https://www.bitmex.com/api/v1/instrument?symbol=ONDOUSDT
The most recent Premium Index value for the symbol: https://www.bitmex.com/api/v1/trade?symbol=.ONDOUSDTPI&count=1&reverse=true
To replicate the Premium Index, take the following 5 fields from the Instrument Table record:
- impactBidPrice
- impactAskPrice
- fairPrice
- indicativeSettlePrice
- fundingRate
The Premium Index value for that minute is calculated as follows:
PI = ((min(0, impactBidPrice - fairPrice) - min(0, price - impactAskPrice)) / indicativeSettlePrice) + fairPrice
When evaluated at the end of each minute, the calculated PI will match the record in the Premium Index history for that minute. Note however that the calculated value can drift in between the minute snaps due to market activity - these values are only snapped one time per minute and should only be expected to line up at the end of each minute.
ONDOUSDT data was snapped at 2025-01-14 02:06:00 UTC to demonstrate this calculation. The instrument table at this time returned (PI is calculated using the expression above):
symbol | impactBidPrice | impactAskPrice | fairPrice | indicativeSettlePrice | fundingRate | PI (calculated) |
ONDOUSDT | 0.541969 | 1.190485 | 1.19192 | 1.1923 | -0.00134 | -0.002543 |
The ONDOUSDT PI data matched (“price” field below) this calculation:
timestamp | symbol | side | size | price | tickDirection | trdType |
2025-01-14T02:06:00.000Z | .ONDOUSDTPI | Buy | 0 | -0.002543 | MinusTick | Referential |
Which matches precisely.
Code examples:
Getting the 8-hour Premium Index:
import requests
import pandas as pd
symbol = "ONDOUSDT"
start_time = "2025-01-13T12:01:00.000Z"
end_time = "2025-01-13T20:00:00.000Z"
url = f"https://www.bitmex.com/api/v1/trade?symbol=.{symbol}PI8H&count=1&startTime={start_time}&endTime={end_time}"
resp = requests.get(url)
df_pi8h = pd.DataFrame(resp.json())
Getting the minutely Premium Index and averaging to match the 8-hour PI:
url = f"https://www.bitmex.com/api/v1/trade?symbol=.{symbol}PI&count=500&startTime={start_time}&endTime={end_time}"
resp = requests.get(url)
df_pi = pd.DataFrame(resp.json())
pi8h = df_pi["price"].mean()
Getting the current Instrument Table values and calculating the PI:
url = f"https://www.bitmex.com/api/v1/instrument?symbol={symbol}"
resp = requests.get(url)
df = pd.DataFrame(resp.json())
df["bid"] = (df["impactBidPrice"] - df["fairPrice"]).clip(lower=0)
df["ask"] = (df["fairPrice"] - df["impactAskPrice"]).clip(lower=0)
df["PI"] = ((df["bid"] - df["ask"]) / df["indicativeSettlePrice"]).fillna(0.0) + df["fundingRate"]
df = df[["impactBidPrice", "impactAskPrice", "fairPrice", "indicativeSettlePrice", "fundingRate", "PI"]]
Comparing to the most recent value of the PI index:
url = f"https://www.bitmex.com/api/v1/trade?symbol=.{symbol}PI&count=1&reverse=true"
resp = requests.get(url)
df_pi = pd.DataFrame(resp.json())