On 26 February 2026 we shipped an upgrade to the on-chain factor in TokenIntel's Bitcoin signal. It read three metrics from the wrong JSON fields. For the next 144 days it parsed nothing, scored a clean zero on every single run, and carried 12% of the model's weight while doing it. Nothing alarmed, because the failure looked exactly like a neutral reading.
While investigating, we found two more factors contributing nothing. Together, three of eleven factors representing 19% of the model's weight had been silently absent. We fixed all of it today. Bitcoin's signal moved from sell to buy on the first corrected run.
We publish a signal track record. That only means something if we also publish the times the machine producing it was broken. This is one of those times, and it is not a small one.
TokenIntel's Bitcoin signal is an eleven-factor weighted model. One of those factors, worth 12% of total weight, is a holder-behaviour composite built from three on-chain metrics: MVRV Z-Score, Net Unrealised Profit/Loss, and the Puell Multiple. We source all three from bitcoin-data.com.
That API names each value field after its own metric. A request for NUPL returns a field called nupl. A request for the Puell Multiple returns puellMultiple. Our parser asked for neither. It asked for currentRatio and value, two fields that exist on none of the three endpoints.
Every observation parsed to null. Every level check was skipped. The trend detector, which needs two valid readings to compare, returned "flat" on every run because it never had one. The composite summed nothing and returned zero.
The metrics themselves were fine the entire time. They arrived over the wire, correctly, every four hours, for 144 days. We simply never read them.
This is the part that matters more than the bug.
The HTTP request succeeded. Only the parse failed. So the function returned an object that looked entirely healthy, containing a score of zero, and our monitoring recorded the on-chain source as available. A total parse failure and a genuine "on-chain conditions are neutral right now" reading produced byte-identical output.
A failure that returns a plausible value is worse than a failure that throws. One gets investigated. The other gets averaged into your model for five months.
We had the evidence in front of us the whole time. Our own factor-audit endpoint reported onchain: 0 on 42 consecutive calculations in a single week. It also reported funding: 42 and whales: 42 in its data-source failure counts, which is to say those two factors failed on every calculation we made. Nothing alarmed on any of it, because nothing was watching the failure counts. The real defect was never the parser. It was that a factor could contribute nothing for months without anyone being told.
Investigating the on-chain factor turned up two more dead inputs.
Funding (6% of weight). We read the BTC perpetual funding rate from Binance's futures API. It failed on every scheduled run. The code comment guessed the cause was a US IP block, and that guess was wrong in a way that kept it alive: the endpoint answers normally from a US residential connection, which is to say from any developer's laptop. What it blocks is datacenter ranges, so it failed every time from our AWS-hosted scheduler and worked every time anyone checked it by hand. It now falls through to Bybit and then OKX, both of which publish the same eight-hour funding convention.
Whales (1% of weight). This one required a Whale Alert API key that was never configured. The endpoint returned a 503 on every call for as long as it has existed. To its credit it refused to invent data rather than returning a fabricated number, which is the correct failure mode. But the front end still rendered a "Whales" card on the signals page for Bitcoin and Ethereum, showing a hardcoded "+0" badge and empty placeholders. Investors were looking at a factor that had never once worked. We have deleted it rather than paying for a data feed to justify 1% of a model.
We also found that our front-end copy of the algorithm weighted whales at 4% while the backend used 1%. The two implementations had drifted apart without anyone noticing. Both are now gone.
A reasonable assumption is that a dead factor is harmless: it contributes nothing, and the other factors carry on. That is not what happens.
When a factor's data is missing, our model omits its term from the weighted sum rather than redistributing that weight across the survivors. The score is still compared against the same fixed thresholds for buy and sell. So 19% of missing weight does not vote zero. It shrinks the range the score can reach, compressing every reading toward the middle and away from the thresholds that produce a signal.
The model was not indecisive. It was being squeezed toward neutral by inputs that were not there.
That is visible in the record. In the 30 days before the fix, the Bitcoin signal printed the same value on 182 consecutive calculations, with half of them flagged as fragile by our own margin check. Since the factor upgrade shipped in February, the model produced exactly two signal changes.
Fixing the parser exposed a second problem underneath it. Once real values reached the scorer, the bullish thresholds worked. The bearish ones were unreachable.
Those thresholds were inherited from an era when Bitcoin's on-chain metrics ran far hotter. We measured them against every day since 1 January 2022:
| Metric | Bearish trigger | Highest reading since 2022 | Days triggered |
|---|---|---|---|
| MVRV Z-Score | > 5.00 | 3.11 | 0 of 1,661 |
| NUPL | > 0.75 | 0.64 | 0 of 1,661 |
| Puell Multiple | > 4.00 | 2.43 | 0 of 1,661 |
Zero days. Across four and a half years that contain a full cycle top. The factor was structurally incapable of warning about overheating, including through October 2025, when Bitcoin peaked at $124,798. Across 2023 to 2026 the composite's observed range was -0.5 to +3. It was a bullish-only indicator wearing the costume of a two-sided one.
We replaced those levels with full-history percentiles: MVRV Z-Score above 2.14 and 2.90, NUPL above 0.59, Puell above 1.71. We first tried a trailing four-year window and rejected it, because in a trending series it flags mid-2023, with Bitcoin near $31,000 and about to quadruple, as maximally overheated. The trailing window is still full of bear-market values, so everything looks extreme. Full history does not have that failure mode.
We reconstructed the corrected factor across 1,292 days, from January 2023 to July 2026, and measured forward Bitcoin returns against it. The headline looked excellent. Strong bullish readings preceded 60-day returns of roughly 19% against a 7.5% baseline.
We do not believe that result, and we are not going to present it as if we do.
Daily observations with 60-day forward returns overlap by 98%. The effective sample is not 1,292 independent readings. In the strong-bullish bucket it is eight, with a 20% standard deviation. That places the result roughly one standard error from the baseline, which is not a detectable edge. The apparent performance also concentrates almost entirely in 2023, the single recovery off the FTX low. One episode is not a repeatable pattern.
What the backtest does establish is narrower and more useful: the recalibrated bearish side works. The factor's directional spread between bullish and bearish readings went from inverted to correctly signed at every horizon.
| Forward window | Bullish minus bearish, before | After recalibration | Out-of-sample check |
|---|---|---|---|
| 30 days | -4.9% | +4.0% | +4.0% |
| 60 days | -0.7% | +9.4% | +10.2% |
| 90 days | +2.1% | +15.7% | +16.7% |
Because we derived those thresholds from history that includes the test window, we re-derived them using only data through 2022 and re-ran everything on 2023 to 2026. The out-of-sample cutoffs land within about 10% of the ones we shipped and reproduce the result. That is genuine evidence, and it is why we changed the bearish side.
We deliberately left the bullish thresholds alone. They fire correctly. The evidence that they are good is too thin to tune on, and thin evidence is a reason to leave something alone, not a licence to adjust it until it looks better.
On the first corrected run, Bitcoin's weighted score moved from 0.153 to 0.252 against a buy threshold of 0.20. The signal changed from sell to buy. The on-chain factor scored +2 out of a possible 3, driven by an MVRV Z-Score of 0.41, NUPL of 0.19, and a Puell Multiple of 0.84.
We want to be precise about what that is and is not.
Bitcoin's signal turned bullish on the same day we fixed the model. We are not going to pretend that is a market call.
It is partly a bugfix artifact. The underlying on-chain conditions did not change today. Our ability to read them did. A model that has been structurally compressed toward neutral for five months will move when you decompress it, and some of that move is arithmetic rather than insight. We have paused subscriber alerts through 27 July so the corrected model can settle for a cycle before it starts sending anyone notifications, and so that nobody receives a "buy" alert whose real cause was a JSON field name.
What we can say is that the reading is now built on inputs we have verified against two independent sources. Our production values from bitcoin-data.com (MVRV Z-Score 0.4121, NUPL 0.1892, Puell 0.8387) match figures we derived separately from CoinMetrics (0.369, 0.184, 0.845). They also sit close to the on-chain picture in Coinbase Institutional and Glassnode's Q3 2026 Charting Crypto, which reads the same metrics as an accumulation zone. Reading that report is what prompted us to check our own.
Fixing three factors is the small part. The failure that matters is that a 12%-weight input could go dark for 144 days inside a system that was logging the evidence and telling nobody.
So the on-chain composite now reports how many of its three metrics actually parsed, and a count of zero is treated as a failed data source rather than a neutral score. That distinction did not exist before, and its absence is the entire reason this lasted as long as it did. A factor that cannot tell you it is broken will eventually be broken without you knowing.
We are also adding a monitor for the condition that should have caught all three: a factor contributing exactly zero, or a data source failing, across consecutive runs. If you run a quantitative model, we would suggest checking whether yours can distinguish "this input is neutral" from "this input is not there." Ours could not.
Our published Bitcoin track record shows 12 signals and a 42% hit rate. Some of that record was generated by a model missing a fifth of its weight. We are not going to quietly restate it, and we are not going to defend it either. It is what our system produced, and now you know the conditions it produced it under.
The honest summary is that our signal was less informed than our own interface implied for five months, and the thing that eventually caught it was reading somebody else's research and checking our numbers against theirs.