Perpetuals Heat Index
How the per-asset percentile and the equal-weight global score are computed, what the breadth counters mean, and where the data does not yet reach.
See the live widget at /perpetuals/positioning.
Data sources
The Heat Index reads one upstream file: data/research/heat_index.json, written by the PPI shadow-log cron at every hourly :05 UTC firing. That cron also maintains the per-asset ppi_log.jsonlfiles. So the index inherits PPI's 8-hour data cadence (one new datum per Binance funding settle), even though the FE polls every five minutes.
PPI itself is a rolling 180-cycle z-score of Binance USDT-margined funding rates per asset (3 cycles/day × 60 days ≈ 60 days of context). The shadow logger has captured roughly 2,049 settlements per asset since 2024-07-07, which is the historical distribution the percentile maps against.
Per-asset percentile
For each tracked asset the index ranks the current PPI value against that asset's own 22-month history. The formula is straight bisect_right: count how many historical PPI samples are less than or equal to the current value, divide by the total count, multiply by 100.
percentile_pct = bisect_right(sorted(history), current_ppi)
/ len(history) × 100A BTC score of 92 means today's BTC funding-z sits higher than 92% of every BTC funding-z observed since 2024-07-07. Each asset is graded on its own curve, so a BTC 92 and a DOGE 12 on the same day is a statement about position-within-history per asset, not absolute funding rates being equal.
Global aggregate
The market-wide score is the equal-weight average of the six per-asset current PPI values, mapped to the percentile of the same equal-weight aggregate computed historically over every timestamp where all six assets have a value. The historical window has roughly 2,049 such timestamps, same as the per-asset depth.
current_agg = mean([ppi.btc, ppi.eth, ppi.sol, ppi.bnb,
ppi.xrp, ppi.doge])
historical_agg = [mean(per_asset_ppi(t)) for t in shared_timestamps]
global_pct = bisect_right(sorted(historical_agg), current_agg)
/ len(historical_agg) × 100Equal weight, not OI weight. BTC dominates open interest at around 60% of total perp OI, so an OI-weighted composite would track BTC closely. Equal weight surfaces breadth instead. When five of six venues run hot together the score climbs, even if BTC alone hasn't broken out.
Breadth counters
Alongside the global percentile, the payload exposes two counters: how many of the six assets currently sit in Hot or Euphoric bands, and how many sit in Cold or Panic. The widget renders these as “1 of 6 perps in hot zone”.
Breadth separates a broad regime shift from a single-asset blowout. Global score 75 with breadth_hot=5 means most of the market is warm at once. Global score 75 with breadth_hot=1 means one venue is at an extreme high and is pulling the average up by itself. Same headline number, very different microstructure.
Caching and freshness
The endpoint reads heat_index.json on every request and returns 503 if the file is older than two hours. Beyond that something is wrong upstream (timer dead, PPI cron failing, disk full) and serving stale heat values would mislead the FE more than an honest error.
Cache-Control sets 5-minute max-age plus 10-minute stale-while-revalidate, so a CDN can absorb most traffic without ever stale-serving past the file's actual age. FE polling matches: SWR refresh every five minutes. Since the file updates hourly, 11 of every 12 client requests return identical bytes from the CDN.
Limitations
- Descriptive, not predictive. A Heat Index of 90 does not forecast a dump. It states that current funding rates sit in the top 10% of observed conditions across the tracked period. Decisions are on the reader.
- 22 months is not a full crypto cycle.The 2024-07-07 start date means the historical distribution does not include the 2018-2020 bear, the 2021 mania, or the 2022 contagion. Today's “99th percentile” is the 99th percentile of a sample that does not cover the full range of funding behavior crypto has produced.
- BNB runs narrower than the others. 58.7% of BNB cycles since 2024-07-07 landed in
Neutral, against 33-43% on the other five assets. So a BNB percentile of 80 represents a smaller deviation in absolute funding-rate terms than a BTC percentile of 80. The equal-weight aggregate dilutes this asymmetry but does not fix it. - Equal weight gives DOGE the same vote as BTC. Capital-weighted readers should read the per-asset bars, not the composite, since the composite shows breadth and not capital-weighted exposure.
- Funding settles every 8 hours, not continuously. The PPI value used by the Heat Index updates with funding, so the score can sit on a stale figure for up to 8 hours during a fast intra-cycle move. The hourly cron picks up the new settlement within an hour of it printing.
Versioning
Initial release v1.0.0 on 2026-05-21. Material changes to the compute path (new weighting, threshold tweaks, additional inputs) bump the version and update the published date. Wording fixes do not.