funding_anchor
funding_anchor is a master switch wired to what it costs to hold a leveraged crypto bet. Every thirty minutes we ask OKX for the funding rate on the Bitcoin, Ether and Solana perpetual swaps — the fee that leveraged longs pay leveraged shorts every eight hours, or the other way round when it goes negative — and you pick a condition off it: "only when BTC funding is below 0", "only when it's above 0.05". While your condition holds the bot trades; while it doesn't, the bot places no trades at all that cycle, in any market.
- Bots using it
- 0 live or out-of-sample — 1 archived, which really traded
- Data source
- OKX /api/v5/public/funding-rate
- Refresh
- Every 30 minutes (*/30 * * * *) — but see catch #1
- Rows stored
- 4,182 readings 3 coins × 1,394 · 30 days
- Backtest-replayable
- No — see catch #5
- Anchor module
- quants/funding_anchor.py
What a funding rate actually is
A perpetual swap is a futures contract with no expiry date. That creates a problem: with nothing forcing it to converge on anything, its price can drift away from the actual spot price of the coin forever. The fix is a payment. Every eight hours — at 00:00, 08:00 and 16:00 UTC on the contracts we poll — everyone holding a position pays or receives a small percentage of their position's value. If the perp is trading above spot, longs pay shorts, which makes being long more expensive and nudges the price back down. If it's below, shorts pay longs. That payment rate is the funding rate, and it is the number this signal reads.
The rates are small and the units matter. Our column is stored as percent per eight hours, so 0.01 means one hundredth of one percent of your position, three times a day — about 11% a year if it held. Everything on this page is in those units.
The reason a trader cares is that funding is a price nobody quotes deliberately. It falls out of how badly one side wants leverage. The module's own docstring puts the usual reading like this: "Positive funding = longs pay shorts (crowded/over-leveraged longs → reversal risk); negative = crowded shorts." That reading is popular, it is roughly the reason the signal exists — and, as catch #3 gets into, at the levels this feed actually produces it is mostly wrong.
Where the data comes from
One vendor, one endpoint, no key, no account: https://www.okx.com/api/v5/public/funding-rate?instId=BTC-USDT-SWAP. Called live from the production server while writing this page it returned HTTP 200 in 0.12 seconds. The poller asks it three times, once each for BTC-USDT-SWAP, ETH-USDT-SWAP and SOL-USDT-SWAP, with a tenth of a second between calls.
OKX is not the obvious choice and the poller says why in its own docstring: "Binance & Bybit are geo-blocked from the Hetzner datacenter; OKX works." That is a deployment fact rather than a view about which exchange is best, and it is worth knowing — the funding rate you see here is one venue's funding rate, not the market's.
The fetcher is predictive/funding_poller.py on cron */30 * * * * — 48 cycles a day, 144 rows. It takes the fundingRate field, multiplies by 100, rounds to five decimals and writes a row into crypto_funding in predictive.db along with the raw decimal and the timestamp of the settlement the rate applies to. Rows older than 30 days are deleted on each run. The gate function in quants/funding_anchor.py then reads the single most recent row for your chosen coin and compares it to your number.
OKX has never once failed us. Across the poller's entire log — 6,723 lines — the string fetch failed appears zero times. Every failure in that log, and there are 217 of them, is our own database refusing to be written to. Catch #1 is about that.
What it means for your bot
It is a switch on the whole bot, not a filter on crypto markets. The gate runs once per cycle, before any market is looked at. If BTC funding doesn't satisfy your condition, your bot places no trades that cycle — in weather, in baseball, in Fed markets, anywhere. Nothing in the code restricts it to crypto. If you want "trade crypto only when…", you need the category filter as well; this signal on its own will happily stand your weather bot down over the cost of leverage on a Solana swap.
The realistic use is the one our builder actually offers: below 0. Negative funding means shorts are paying longs, which is the unusual state — it happened on 19.7% of the 279 settlements OKX will serve, and on 12.3% of our BTC readings. As a way of saying "only when the leverage crowd is leaning short", it is a real, reachable, meaningful condition. Whether leaning against it makes money is a separate question, and the one bot that tried it is in the record below.
The catch
1. The feed has been dead for 52 hours, and it is our fault, not OKX's
The newest row in crypto_funding is from 2026-08-09T22:00:04Z. At the time of writing that is 52.5 hours ago. The poller has fired about 105 times since and inserted nothing. Every one of those runs fetched the data from OKX successfully and then died writing it, with sqlite3.OperationalError: database is locked — 104 consecutive failures since the last success.
The cause is predictive.ticker_index --refresh, our backtest lookup index. It runs */10 over 543,000 tickers and holds the write lock on predictive.db for 119 seconds — 118.4, 118.6, 118.9, 119.1, 120.6 and 124.2 seconds on the last six runs. funding_poller opens its connection with timeout=10 and fires at :00 and :30 — both of which are ticker_index minutes. It waits ten seconds for a lock that will not be free for another 109, and gives up. It is not unlucky; at that cadence it is never going to win.
This is not one broken feed. Running signal_health.py shows seven stale feeds — Fear & Greed (74.5 h), perp funding (52.5 h), MLB model edge (52.5 h), tropical formation (53.5 h), launch schedule (53.5 h), GDPNow (69 h) and Wikipedia attention (53.3 h). One rule sorts every poller on the box correctly: a poller survives if the seconds from its start to the end of the lock are fewer than its busy timeout. Band 4 of the diagram draws it. The clearest case is a pair that both run at :41, 60 seconds into the lock: ensemble_poller waits 60 seconds and lives; mlb_bullpen_poller waits 20 and dies.
Not fixed here — this page's job is the dictionary, and the loop that writes it is not allowed to touch pollers or crontabs. It has been reported. What is worth saying on a page about the signal is that the data was never the problem: OKX answered every single time.
2. The staleness check that should have caught this does not work
The gate is meant to fail closed. It asks for the newest row AND fetched_at >= datetime('now', '-90 minutes'), and if nothing comes back it returns False. The idea is right. The comparison is broken.
We store timestamps in Python's ISO format — 2026-08-12T00:00:02.111+00:00, with a T between the date and the time. SQLite's datetime() returns 2026-08-12 00:56:43, with a space. Comparing them compares text, and "T" sorts after " ". So the date matches, then the T wins, and the row passes. Run on production while writing this page:
SELECT '2026-08-12T00:00:02.111+00:00' >= datetime('now','-90 minutes'); → 1
The reading was 2 hours 26 minutes old. The window is 90 minutes. It passed. In practice any row stamped with today's UTC date passes the freshness check, however old it is — up to almost 24 hours just before midnight. The gate only genuinely fails closed once the data is stale enough to be from a previous date, which is why it happens to be failing closed today. Same defect as finance_anchor and tropical_anchor; the four signals on crypto_poller get it right. Unfixed.
For what it is worth, the 90-minute window was needed before all this: across the 1,394 healthy cycles there were 6 gaps longer than 90 minutes and 44 longer than an hour, the largest being 150 minutes.
3. Our own documented example has never been reachable — and “positive means crowded longs” is wrong at these levels
The module's docstring offers this as its worked example: {"symbol":"BTC", "above": 0.05}, annotated "crowded longs". Across our 4,182 readings, the number of times any coin reached 0.05 is zero. Across all 279 settlements OKX will serve — three months — the number of times BTC funding reached 0.05 is also zero. The highest value in either data set is 0.01.
In fairness, 0.05 is not a silly number, and this is where a second opinion changed the page. OKX returns its own ceiling in the same payload: maxFundingRate is 0.375 for the BTC contract and 0.75 for ETH and SOL — thirty-seven and seventy-five times higher than anything we have observed. 0.05% per 8 h is roughly 55% a year in carry; that is a hot, over-leveraged market, and it is a normal thing for funding to do. It is a hot-market threshold that has not seen a hot market. That is a different failure from onchain_fees_anchor, whose shipped 10 sat/vB is a number the chain last touched twenty months ago.
What is genuinely strange is the 0.01. Of 279 settlements, 21 came in at exactly 0.01 and not one exceeded it. A continuous quantity does not land on the same five-decimal value twenty-one times by chance, so 0.01 is structural, not a calm-market coincidence — and note that it is exactly the interestRate field OKX returns alongside the rate (0.0001). The standard account of this is a dead-band: while the perp's premium over spot stays inside a band, the funding rate collapses to precisely the interest rate. We are not asserting that formula, because we tested it against a live payload and it did not reproduce OKX's number — although that may simply be because the published formula uses a time-weighted average premium and the API exposes only an instantaneous one. Verified: the pin is real and it is at 0.01. Unverified: exactly why.
The consequence for the reader is the part that matters. If 0.01 is where funding sits when nothing is happening, then a positive funding rate is the default state of the world, not evidence of crowded longs. Our docstring's gloss — positive means over-leveraged longs and reversal risk — describes genuinely elevated funding, which this feed has not seen. Read the 0.01 readings as "normal", not "bullish".
4. You are thresholding a forecast, not a settled fact
/api/v5/public/funding-rate returns the rate for the settlement that has not happened yet, and it keeps moving until it does. Our poller stores that number — 16 times per 8-hour window — and the gate compares your threshold against whichever version happened to be captured last.
How much does it move? Within a single settlement window, the estimate for BTC wanders an average of 0.0047 and as much as 0.0103. The entire 30-day range of the series is about 0.021 wide. The number moves roughly a quarter of its whole monthly range before it settles, and in the worst window it moved half of it. And for the one threshold anybody actually uses — the builder's below 0 — the estimate was on both sides of zero in 106 of the 279 settlement windows we captured across the three coins, 38%. In more than a third of windows, "is funding negative" had two different answers depending on when you asked.
The settled rate is available and we throw it away. The same payload carries settFundingRate; we confirmed it matches the realized rate for the previous settlement exactly (0.0000495294037917 in both the live response and the history endpoint). We store the forecast and discard the fact.
5. The backtester cannot replay it
funding_anchor is listed in _UNSUPPORTED_ENTRY in quants/backtest.py, added 2026-06-04 with the note that these anchors "need a forward-captured series we don't have retroactively". So a backtest of a bot with this gate runs without the gate and still returns an equity curve. It flags unsupported_rules; you have to look.
Here the stated excuse is only half true, and worth correcting. We do now have a retroactive series: OKX's funding-rate-history endpoint is free, keyless, and served three months of realized eight-hourly settlements in 0.12 seconds when we called it. That is not the same series the live gate reads — it is the settled rate, not the forward estimate — but it is a defensible one to replay against, and arguably a better one. What is real is the depth: pagination stopped at 279 settlements, so three months is all OKX will give, against price history that starts 22 June anyway.
6. Thirty days of memory — and it deleted the only evidence we had
Each run deletes rows older than 30 days. That prune has the same string-comparison quirk as catch #2 in reverse: because the ISO T sorts high, rows survive to the end of their final day, so real retention is 742 hours rather than 720. Harmless, and much milder than the seven-day version on the crypto_poller signals.
What is not harmless is the consequence. The one bot that ever used this signal traded on 11–12 June. The funding readings it traded on no longer exist. We could only reconstruct band 3 of the diagram — the thing that shows the gate actually worked — by going back to OKX's public history, because our own copy had been thrown away. A year of this series would be about 52,000 rows.
7. A live page is showing you a 52-hour-old number, and its labels have never once fired
The Opportunity scanner has a "crowded leverage" panel that reads this same table. Its query is SELECT funding_pct … ORDER BY fetched_at DESC LIMIT 1 — with no freshness filter at all, not even the broken one. Right now it is presenting Saturday night's funding rate as the current state of the market.
And its thresholds cannot fire. It labels a coin "crowded longs" at ≥ 0.03 and "crowded shorts" at ≤ −0.03. Across all 4,182 readings we hold, the number that met either condition is zero. Every reading in the table's entire history has been labelled "neutral", and given the 0.01 pin in catch #3, that will stay true until the market changes character.
8. Two smaller things: the registry mis-reads the poller, and the validator lets you write nonsense
- The registry cannot find this signal's poller. signal_registry.py reports poller: "db" and cron: null, which would suggest nothing fetches this data. It does. The cause is one regex: the registry looks for predictive\.(\w+) in the docstring, and the docstring's second sentence begins "Reads predictive.db.crypto_funding (funding_poller, OKX)". It matches predictive.db, captures db, and never reaches the fallback that would have found funding_poller.py sitting right there. The same artefact hits five signals — this one plus energy, launch, sports and tropical. Everything else the registry says is correct.
- There is no bound on the threshold. validate_config checks only that your number is a number. {"symbol":"BTC","above":100} validates happily — 100% per 8 h, roughly 266 times OKX's own cap.
- A contradictory config validates, and then hides half of itself. {"symbol":"BTC","above":0.05,"below":-0.02} passes validation and can never be satisfied, because the gate requires both. Worse than the equivalent bug elsewhere in the library: to_english checks above first and returns, so the builder tells you "only when BTC funding ≥ 0.05% / 8h (crowded longs)" and never mentions the second condition at all. Elsewhere the two clauses at least both get printed.
The case for the defence
- It is the steadiest gate in the crypto shelf, by a wide margin. At the builder's below 0 setting the gate changes state 1.4 times a day, with a median run of 17 polls — 8.5 hours, almost exactly one settlement period. onchain_fees_anchor flips 20.9 times a day at its usable threshold. Funding is a genuine regime variable rather than a strobe, and it is that way for a structural reason: the underlying thing only settles three times a day.
- The builder's default is honest. The field ships at below 0 and the one-click recipe, "Funding mean-reversion", uses the same. That condition is open 12.3% of the time on BTC and 23.6% across the three coins. The unreachable 0.05 is in the docstring only — the number a user is actually handed is a good one. That is the opposite of the pattern on three other pages in this dictionary.
- The gate demonstrably did its job. Reconstructing June from OKX's history: BTC funding settled negative at every settlement from 11 June 00:00 through 12 June 00:00, and turned positive at 12 June 08:00. The bot's twelve trades run from 11 June 03:45 to 12 June 05:20 — all inside the negative stretch, stopping before the flip. The signal fired when it said it would.
- The vendor choice was right and the vendor is flawless. Zero fetch failures in the entire log, 0.12-second responses, no key, no account, and a free three-month history endpoint most paid providers charge for. Every failure this signal has ever had was ours.
- It is genuinely independent. Nothing else in the signal library reads leverage positioning. It is not a price, not a poll, not a forecast, and not derived from anything else on the shelf — which is exactly what you want from a gate you intend to combine with others.
The real record
One bot has ever used this signal, and unlike the four Tier-2 signals written up before it, this one actually traded. Of the 76 strategy configurations on the platform — 21 live, 15 out-of-sample, 36 archived, 4 draft — exactly one mentions funding_anchor: Crypto Fear Reversion (crypto-fear-reversion, id 31), now archived. Its own description says what it was for: "buy mid-band crypto dips ONLY at crowd-capitulation (Fear&Greed<20 + funding<0 + 8c drop)… May fire rarely; drop funding gate first if <2 fills/wk."
It was created at 03:42 UTC on 11 June 2026 and it fired twelve times in the next 25 hours 35 minutes. All twelve were Polymarket, all YES, all $5 flat.
| Market | Entry | Result | P&L |
|---|---|---|---|
| Will Bitcoin dip to $60,000 June 8–14? | 37¢ | loss | −$5.00 |
| Will Ethereum dip to $1,600 June 8–14? | 51¢ | loss | −$5.00 |
| Will BTC be between $60,000 and $62,000 on June 11? | 25¢ | loss | −$5.00 |
| Ethereum Up or Down on June 11? | 44¢ | loss | −$5.00 |
| Solana Up or Down on June 11? | 37¢ | win | +$8.51 |
| Bitcoin Up or Down on June 11? | 53¢ | win | +$4.43 |
| Bitcoin Up or Down — June 11, 8PM ET | 53¢ | win | +$4.43 |
| Bitcoin Up or Down — June 11, 4–8PM ET | 34¢ | win | +$9.71 |
| Will Bitcoin reach $64,000 on June 11? | 37¢ | loss | −$5.00 |
| Will BTC be above $64,000 on June 12? | 33¢ | loss | −$5.00 |
| Bitcoin Up or Down — June 11, 8PM–12AM ET | 36¢ | loss | −$5.00 |
| Will BTC be between $64,000 and $66,000 on June 12? | 26¢ | loss | −$5.00 |
| 12 trades · 4 wins, 8 losses · $60.00 staked | — | 33.3% | −$12.92 |
That is the whole track record of this signal, and the lesson in it is the useful one. The gate was not broken, was not stale, and was not mis-set. It opened when BTC funding was negative, exactly as designed, and it shut the bot down when funding turned. It did its job, and the bot lost 21.5% of the money it staked in a day. A gate that works is not an edge; it only decides when you are allowed to be wrong.
Since that bot was archived: nothing. No live bot, no out-of-sample bot, and none of the 88 saved strategy versions except the one belonging to this bot. We have polled OKX 48 times a day ever since — or would, if the writes were landing.
How to actually use it
- Right now, don't — it is stale and it will stand your bot down. Until the writes land again the newest reading is from 9 August, and once its date is not today's date the gate correctly returns False for everything. A bot with this gate is a bot that does not trade.
- Use below 0, not above 0.05. The builder default is the good one. The docstring's example has not been reachable in the entire history OKX will serve, and if funding ever does reach 0.05 you will have much bigger signals telling you so.
- Don't read 0.01 as bullish. It is the pin, not a crowd. Elevated funding starts somewhere well above it, and this feed has never been there.
- Add the category filter. This gates everything. On its own it will stop your weather bot because of leverage costs on a crypto swap, which is not a relationship that exists.
- Don't trust a backtest of it. The gate is skipped in replay and the curve still appears. Check for unsupported_rules.
- Treat it as a cost-of-leverage reading, not a direction. Funding tells you what it costs to hold a levered position on one exchange over the next eight hours. It is not a forecast of a coin's price on a date, which is what the markets you are betting on actually resolve against. The one bot that combined it with an extreme-fear gate and a price-drop trigger still went 4–8.
Where to go next
For the crowd-sentiment gate this bot was paired with, see sentiment_anchor — which is also one of the seven feeds currently stale. For the other signals on the crypto shelf, none of which has ever had a bot at all, see crypto_momentum_anchor, btc_dominance_anchor, stablecoin_share_anchor and onchain_fees_anchor, whose page first measured the lock contention described in catch #1.
For the same broken string-comparison in a freshness check, see finance_anchor. For a signal built on our own history rather than a vendor's, see resolution_anchor. The full list is in the Trade Bot Dictionary.
Every number on this page was measured on 12 August 2026 from the production database, the module source, the live crontab, or a live call to the named endpoint. Where a figure comes from a different series than the one our gate reads — OKX's settled rates rather than our stored forecasts — the page says so. Where we could not establish a mechanism, the page says that too. Nothing here is estimated or carried over from memory. If you find a figure you can't reproduce, tell us — that's a bug in the page.