The first funding-rate scanner I built lied to me for about a week before I noticed. It was flagging a coin as paying some enormous rate on one venue and nothing on another, and I got briefly excited about a free spread. Then I looked closer and realized the two exchanges just funded on different clocks. One settled every hour, the other every eight hours, and I was comparing the raw numbers straight off the API as if they meant the same thing. They did not. That is the whole reason this problem is annoying, and it is worth walking through, because the math is trivial and the plumbing is where everyone quietly gets it wrong.
If you have not built one of these before, the goal is simple to state. Pull the current funding rate for the same asset across a handful of perpetual venues, put every rate onto one comparable scale, and alert yourself when something is at an extreme or when two venues disagree by a lot. The value is not in any single reading. It is in the history, so you can say a rate is high relative to where it usually sits rather than high in some absolute sense you made up.
Symbol mapping is the boring part that breaks everything
Before you touch funding math you have to agree with yourself on what a symbol even is. Every venue names things slightly differently. One lists a perp as BTCUSDT, another as BTC-USD, another as BTC-PERP, and a fourth quietly settles in a different collateral asset so its BTC-USD is not really pricing the same thing as the other BTC-USD. If you key your database off the raw exchange symbol, you will end up with the same asset scattered across four rows that never line up, and your cross-venue divergence alert will never fire because it has nothing to compare.
The fix is a mapping layer you own. Maintain your own canonical symbol, something like BTC-USD-PERP, and a lookup table from each venue's native symbol to that canonical id. Do not try to derive it with string parsing. Tickers collide, and some venues reuse a ticker for a wrapped or bridged version of a token. A boring explicit table that a human checked is worth more than any regex. When a venue adds a new listing, your scanner should skip anything it does not recognize and log it, so you add the mapping on purpose instead of silently ingesting garbage.
Normalize the interval before you compare anything
Here is the part that fooled me early on. The number an exchange hands you is the rate charged for one funding period, not an annual or even a daily figure. If a venue funds every eight hours, that raw rate applies three times a day. If it funds every hour, it applies twenty four times a day. Comparing the raw periodic rates across venues with different intervals is comparing nothing.
So you pick one scale and convert everything to it. I annualize, because it is the number my brain reads fastest, but daily works fine too as long as you are consistent. Figure out how many funding events happen per year on that venue and multiply. Roughly:
- Read the funding interval the venue actually uses. Do not assume eight hours. Some venues run one hour, some four, and a few adjust dynamically, which you have to handle as a real case and not an edge case.
- Compute periods per year. For an eight hour interval that is three per day times about three hundred sixty five days, so roughly a thousand and change.
- Multiply the periodic rate by periods per year to get the annualized figure. Store both the raw periodic rate and the annualized one, because you will want the raw number later for sanity checks.
The trap inside the trap is that some venues present funding as a daily or annualized rate in their UI even though the API returns the periodic value, or the reverse. Read the field documentation once, write down what each field means in a comment next to your parser, and never trust the label alone. A field called fundingRate on one exchange and fundingRate on another are frequently not the same unit.
Store history, then alert on percentiles
A single funding reading tells you almost nothing. Positive funding on a perp just means longs are paying shorts, which is the normal state of a market where people want leverage on the way up. What you actually care about is whether the current rate is unusual for this asset. That requires history.
So the scanner writes a row on every poll: canonical symbol, venue, timestamp, raw periodic rate, annualized rate, and the interval you used to convert. Poll on a sane cadence. Funding does not need to be sampled every few seconds, and hammering the APIs just gets you rate limited. Every few minutes is plenty, and you can align closer to settlement times if you want the actual charged rate rather than the running prediction.
Once you have a few weeks of history, the alert logic gets honest. Instead of alerting when annualized funding crosses some fixed threshold you guessed, alert when the current rate sits above, say, the ninety fifth percentile of that asset's own trailing distribution. Percentile alerts adapt per asset, so a coin that always runs hot does not spam you, and a normally quiet one that suddenly spikes actually reaches you. Keep the trailing window rolling, something like thirty days, so the baseline drifts with the market instead of anchoring to ancient conditions.
For cross-venue divergence, compare the annualized rates for the same canonical symbol at the same timestamp and alert when the spread is wide relative to its own history. A two point spread might be noise for one asset and a screaming outlier for another, and only the history tells you which.
The failure modes worth guarding against
A few things will bite you, so bake in the guards from the start. Stale data is the big one. If a venue's API goes quiet or returns the last cached value, your scanner will happily keep alerting on a number that stopped updating. Stamp every reading with the source timestamp, not your ingest time, and refuse to compare readings that are too far apart. Also decide early whether you are storing the predicted funding rate that updates continuously or the settled rate that gets locked in at each interval. They are different numbers and mixing them in one column will quietly poison your percentiles.
The other one is thin or newly listed markets. A perp that launched a few days ago has no meaningful history, so any percentile you compute is noise dressed up as signal. Gate alerts behind a minimum history length. Below some sample count for an asset, hold the alert and just collect data.
None of this is exotic. It is a poller, a mapping table, one unit conversion, a time series store, and a percentile query. I run this kind of monitoring feeding into Blockcircle so funding shows up next to the rest of the market picture rather than in a separate tab I forget to check. The thing that makes it useful is not the alerting, it is that you spent the boring hours getting symbols and intervals right, so when the scanner does say something, you can trust that it is comparing like with like.