This tool scans all funding rates across Hyperliquid - both regular crypto perpetuals and HIP-3 tokenized assets (oil, gold, stocks). It displays the top 30 highest and top 30 most negative funding rates in formatted tables, filtering out small markets.
The scanner loops every 5 minutes and shows you:
In perpetual futures, there's no expiry date. To keep the futures price close to the real price, exchanges use a funding rate - a periodic fee paid between traders.
Why it matters: Extreme funding rates can signal overcrowded trades and potential reversals. They also affect your P&L - high positive funding eats into long profits.
The total dollar value of all open (unclosed) positions. The scanner filters for MIN_OI_VALUE = $5,000,000 to only show markets with enough liquidity to trade.
The funding rate extrapolated to a yearly percentage. If the 8-hour funding rate is 0.01%, the annualized rate would be about 10.95% (0.01% x 3 payments/day x 365 days). This helps you compare funding rates to traditional interest rates.
def display_top_bottom(df, label):
# Step 1: Clean and standardize the dataframe
df = _standardize_df(df.copy())
# Step 2: Check we have a Rate % column
if "Rate %" not in df.columns:
return
# Step 3: Sort by rate, highest first
df = df.dropna(subset=["Rate %"]).sort_values("Rate %", ascending=False)
# Step 4: Filter out small markets (OI < $5M)
if "OI Value" in df.columns:
df = df[df["OI Value"] >= MIN_OI_VALUE]
# Step 5: Show top 30 highest and top 30 lowest
top_pos = df.head(30)
top_neg = df.tail(30).sort_values("Rate %", ascending=True)
Line by line:
_standardize_df() - Makes sure all data has the same column names and formats, regardless of which API it came fromdropna(subset=["Rate %"]) - Removes any entries that don't have a funding ratesort_values("Rate %", ascending=False) - Sorts from highest to lowest ratedf["OI Value"] >= MIN_OI_VALUE - Only keeps markets with $5M+ in open positionsdf.head(30) - Gets the top 30 entries (highest rates)df.tail(30) - Gets the bottom 30 entries (lowest/most negative rates)This function queries the Hyperliquid API for funding rates on standard crypto perpetuals (BTC, ETH, SOL, etc.). It returns raw data that gets standardized later.
Same idea, but for HIP-3 markets - these are real-world assets like crude oil (CL), gold, and stocks that trade as perpetual futures on Hyperliquid.
def scan():
while True:
crypto_df = fetch_crypto_funding()
hip3_df = fetch_hip3_funding()
display_top_bottom(crypto_df, "Crypto Perps")
display_top_bottom(hip3_df, "HIP-3 Assets")
time.sleep(300) # Wait 5 minutes
The infinite loop runs the scan every 5 minutes (300 seconds). It fetches both crypto and HIP-3 rates, then displays them.
pip install colorama pandas requests python-dotenv
| Term | Meaning |
|---|---|
| Funding Rate | A periodic fee paid between long and short traders to keep perp prices close to spot |
| Open Interest (OI) | Total value of all outstanding positions in a market |
| Annualized Rate | The funding rate expressed as a yearly percentage |
| Perpetual (Perp) | A futures contract with no expiry date |
| Spot Price | The current real price of the asset |
| Liquidity | How easy it is to buy/sell without moving the price much |
# --- PYTHON ---
BASE_URL = "https://api.moondev.com"
LOOP_INTERVAL_SECONDS = 5 * 60
MIN_OI_VALUE = 5_000_000
# --- PSEUDO-CODE ---
SET BASE_URL to Moon Dev's API address
SET LOOP_INTERVAL_SECONDS to 300 (5 minutes between each scan)
SET MIN_OI_VALUE to $5,000,000 (minimum open interest to show an asset)
# --- PYTHON ---
def fetch_crypto_funding():
response = requests.get(f"{BASE_URL}/api/hlp/funding",
headers=_auth_headers(), timeout=30)
response.raise_for_status()
return response.json()
# --- PSEUDO-CODE ---
FUNCTION fetch_crypto_funding():
SEND a GET request to Moon Dev API endpoint "/api/hlp/funding"
INCLUDE our API key in the request headers
WAIT up to 30 seconds for a response
IF the request failed: raise an error
PARSE the response as JSON and RETURN it (a list of funding rates for all crypto perps)
# --- PYTHON ---
def fetch_hip3_funding():
response = requests.get(f"{BASE_URL}/api/hlp/funding/hip3",
headers=_auth_headers(), timeout=30)
response.raise_for_status()
return response.json()
# --- PSEUDO-CODE ---
FUNCTION fetch_hip3_funding():
SEND a GET request to Moon Dev API endpoint "/api/hlp/funding/hip3"
INCLUDE our API key in the request headers
WAIT up to 30 seconds for a response
IF the request failed: raise an error
PARSE the response as JSON and RETURN it (a list of funding rates for HIP-3 tokenized assets)
# --- PYTHON ---
def _standardize_df(df):
col_map = {}
for col in df.columns:
lower = col.lower()
if "coin" in lower or "symbol" in lower:
col_map[col] = "Symbol"
elif "rate_pct" in lower:
col_map[col] = "Rate %"
elif "annual" in lower:
col_map[col] = "Annualized %"
elif "oi" in lower:
col_map[col] = "OI Value"
df = df.rename(columns=col_map)
for c in ["Rate %", "Annualized %"]:
if c in df.columns:
df[c] = pd.to_numeric(df[c], errors="coerce")
return df
# --- PSEUDO-CODE ---
FUNCTION _standardize_df(dataframe):
CREATE an empty mapping of old column names to new standard names
FOR EACH column name in the dataframe:
CONVERT to lowercase for comparison
IF the column name contains "coin" or "symbol": map it to "Symbol"
IF the column name contains "rate_pct": map it to "Rate %"
IF the column name contains "annual": map it to "Annualized %"
IF the column name contains "oi": map it to "OI Value"
RENAME all columns using the mapping
CONVERT the Rate % and Annualized % columns to numbers
(any text that can't be converted becomes blank/NaN)
RETURN the cleaned-up dataframe
# --- PYTHON ---
def display_top_bottom(df, label):
df = _standardize_df(df.copy())
if "Rate %" not in df.columns:
return
df = df.dropna(subset=["Rate %"]).sort_values("Rate %", ascending=False)
if "OI Value" in df.columns:
df["OI Value"] = pd.to_numeric(df["OI Value"], errors="coerce")
df = df[df["OI Value"] >= MIN_OI_VALUE]
top_pos = df.head(30)
top_neg = df.tail(30).sort_values("Rate %", ascending=True)
print(top_pos[display_cols].to_string(index=False))
print(top_neg[display_cols].to_string(index=False))
# --- PSEUDO-CODE ---
FUNCTION display_top_bottom(dataframe, label):
CLEAN UP the dataframe: standardize column names, convert types
IF there's no "Rate %" column: give up and return
REMOVE any rows that have blank/missing rate values
SORT the entire dataframe from HIGHEST rate to LOWEST rate
IF there's an "OI Value" column:
CONVERT it to numbers
REMOVE any assets with less than $5 million in open interest
(This filters out tiny, illiquid markets)
TAKE the top 30 rows = the 30 highest funding rates
TAKE the bottom 30 rows and reverse them = the 30 most negative rates
PRINT a formatted table showing the top 30 highest rates
PRINT a formatted table showing the top 30 most negative rates
# --- PYTHON ---
def scan():
crypto_data = fetch_crypto_funding()
crypto_df = parse_rates_to_df(crypto_data, "crypto")
hip3_data = fetch_hip3_funding()
hip3_df = parse_rates_to_df(hip3_data, "hip3")
for label, df in [("Crypto Perps", crypto_df), ("HIP3 Tokenized", hip3_df)]:
if df.empty:
continue
display_top_bottom(df, label)
if __name__ == "__main__":
scan()
while True:
time.sleep(LOOP_INTERVAL_SECONDS)
scan()
# --- PSEUDO-CODE ---
FUNCTION scan():
FETCH all crypto perpetual funding rates from Moon Dev API
PARSE the raw data into a clean pandas dataframe
FETCH all HIP-3 tokenized asset funding rates from Moon Dev API
PARSE the raw data into a clean pandas dataframe
FOR EACH category ("Crypto Perps" and "HIP3 Tokenized"):
IF the dataframe is not empty:
DISPLAY the top 30 highest and top 30 most negative rates
MAIN PROGRAM:
Run one scan immediately
THEN loop forever:
Wait 5 minutes
Run another scan