---
name: regime-tracker
description: RETIRED 21 Sep 2026. The runner-regime tracker Ben commissioned 14 Sep 2026 - design, layers, backtest findings and dead sources. Historical record only; do not rebuild or reference as live.
sources: [cowork]
aliases: [regime, regime tracker, foot off the gas, runner regime, environment tracker]
---

# STATUS
- [stated] 21 Sep 2026: Ben retired the regime tracker ("will not be needed going forward") and asked for it to be stopped and removed from the droplet. Crons removed, /opt/regime moved to quarantine for deletion.

# The ask, 14 Sep 2026
- [stated] Ben: "One component that I want to add to our trend tools is the regime tool. Inflows and outflows from certain chains are going to be quite important in order to know if the environment is good to have runners or not." And: "how many runners we had yesterday that went to 1 million, 5 million, 10 million, 20 million... That's going to probably peak on some day, and then we might get a significant decline. Maybe one day we have many fewer runners than the day before. That might be a weekend thing."
- [stated] Purpose in his words: "tell us when we need to take the foot off the gas and maybe take some profits and relax for a couple of days until things come back."
- [stated] He asked me to add what he had missed.

# THE PUSHBACK, and it is the design
**Both of his inputs lag.** Bridge net flow is the slowest signal available and runner count is a scoreboard: counting how many tokens hit $1M yesterday tells you yesterday was good, and by the time the count falls you have already paid for the information. So a third layer was added in front of them and given the largest weight.

| layer | weight | what it asks | timing |
|---|---|---|---|
| FUEL | 20% | is money arriving on the chain | days ahead, very slow |
| **APPETITE** | **45%** | of everything that launched in the last hours, what share got looked at, and what share of those got smart money | **same day, leads** |
| SCOREBOARD | 35% | runners by tier, conversion between tiers, survival | lags, calibrates the others |

Appetite is the relay's stage-1 and stage-2 rates from [[trench-model-3]], aggregated per day instead of per token. That is the whole trick: a minute-level signal we already validated becomes a same-day regime reading, and it is measurable before any token has run.

## What Ben missed, added
1. **Conversion beats count.** Twenty tokens at $1M and none at $10M is a worse tape than five at $1M and three at $10M. Breadth without depth is chop: you get filled and then bled. Scores the 100k->1M conversion, not only the totals.
2. **Survival.** Of the tokens that crossed $1M yesterday, how many are still above $1M. A day with twelve runners that all round-tripped is a losing day a raw count scores as a win.
3. **Day-of-week normalisation.** He raised the weekend himself. Every metric is z-scored against the SAME WEEKDAY over the trailing four weeks, falling back to trailing days and saying so. Without it every Saturday fires a false alarm.
4. **Two layers minimum or NO CALL.** The first build printed FULL SIZE off the fuel layer alone, because it was the only layer with a baseline. That is precisely the error the design exists to prevent, so it is now a hard gate.

## Output: one action word, per Ben's standing no-invented-vocabulary rule
`FULL SIZE` (>= +0.50) / `NORMAL` (-0.50 to +0.50) / `HALF SIZE` (-1.20 to -0.50) / `STAND DOWN` (< -1.20) / `NO CALL`.

# WHAT IS BUILT, 14 Sep 2026
- **`/opt/regime/regime.py`** on the kookwatch droplet. `collect` / `score` / `all`. SQLite at `/opt/regime/regime.db`, one row per day per chain.
- **`/opt/regime/regime_notify.py`** pushes one Telegram message, reusing linkscan's bot token and chat id 357464289.
- **cron**: `25 0 * * *` collect, `45 0 * * *` notify. 00:45 UTC is 07:45 Bangkok.
- Chains: robinhood and solana, reading `/opt/trench/trench.db` and `/opt/trench-sol/trench.db`.
- Dashboard artifact "Runner Regime" published 14 Sep 2026.

## First live numbers, 14 Sep 2026
Both chains **NO CALL**, correctly, because only fuel has a baseline.
- Robinhood: stablecoins **+$83.8M / +8.5% on 7d**, TVL +2.4%, DEX $1.379B (-8.3% d1, +13.4% 7v7), 398 launches watched, stage-1 15.3%, stage-2 4.8%.
- Solana: stablecoins **-$388.3M / -2.3% on 7d**, TVL -2.0%, DEX $1.637B, 2,129 launches, stage-1 24.3%, stage-2 **1.1%**.
- **Robinhood Chain is the only chain of six gaining stablecoins this week while Solana bleeds.** Base +$58.7M, Ethereum +$51.8M, BSC -$66.2M, Hyperliquid -$195.1M.
- Solana's stage-2 rate being a fifth of Robinhood's, on fourteen times the launches, is the first cross-chain appetite reading either lane has produced. One day of data, so it is an observation, not a finding.

# HONEST STATE: THE RUNNER SERIES CANNOT BE BACKFILLED
Fuel backfilled free and deep (Robinhood 70 days, Solana 2,008). **The runner scoreboard starts at zero and needs ten days.** Every backfill route was probed and every one is dead, so do not re-attempt these:
- **DefiLlama bridges API is PAYWALLED.** `bridges.llama.fi/netflows/{day,week}`, `/bridges`, `/bridgevolume/{chain}`, `/bridgedaystats/...` all return **402 "Upgrade to the paid API plan"**. `defillama.com/bridges` returns **403** to the container and carries no `__NEXT_DATA__`. This is the exact chart Ben screenshotted.
- **THE SUBSTITUTE, and it is arguably better: net stablecoin supply change per chain**, `stablecoins.llama.fi/stablecoincharts/{Chain}`, free, daily, years of history. Stablecoins sitting on a chain are dry powder that can buy; bridge flow also counts ETH arriving to farm.
- **The Dune key on the droplet is OUT OF CREDITS.** The key itself is valid (GET on query 8613013 returns 200) but **POST execute returns 402 Payment Required**. So the historical runner-count query is not runnable. This supersedes the "Dune works from the container" note in [[data-access]] until Ben tops it up.
- **pump.fun `/coins` does not paginate.** `?sort=created_timestamp&order=DESC` returns about 70 rows and **offset 5000, 50000 and 200000 all return an empty list**. No historical enumeration of Solana launches from it.
- **`sort=ath_market_cap` is garbage**: values come back in raw token units (1.9e23) with every `ath_market_cap_timestamp` stamped 12-19, a migration artifact. `ath_market_cap` is only trustworthy on RECENT coins, where it agrees with `usd_market_cap`.
- Our own lanes hold nothing older: `obs` is pruned to a **13 hour window on Robinhood and 2 hours on Solana**, and `lab` covers 12 to 14 Sep only.

# TRAPS CAUGHT DURING THE BUILD, all the same shape
1. **GeckoTerminal `fdv_usd` is wrong on Robinhood Chain**: a fresh pool read **$9,633M**. Confirms Ben's standing rule. GT is used only to FIND the fresh high-volume pools; the market cap is then taken from DexScreener `tokens/v1/{chain}/{addrs}`.
2. **DexScreener `tokens/v1` returns PAIRS and caps the response**, so 61 Solana addresses in chunks of 30 came back priced for 12, which would have read as a quiet tape. Chunks are 12 now, and **priced under 60% of found writes NULL, never a zero**.
3. **A cross-check that answered no pages wrote a clean 0/0/0** on the first run. Now writes NULL. Third instance in this project of a silent zero standing in for an outage.
4. **`launches` was counted off the `lab` join**, so the one-day-old Solana lane reported 9 launches instead of 2,036. Launch counts come from `tok`, tier counts from `lab`, and `lab_cov` records the gap.
5. `tok.late` is **NULL or 1, never 0**, so every `late=0` filter must be `coalesce(late,0)=0`.
6. The droplet has **no `sqlite3` CLI**; use python3. Desktop Commander's device calls **time out at 60 seconds**, so any droplet job over a minute must be `nohup`ed and polled.

# NEXT, in order
1. Ten days of collection, then the appetite and scoreboard layers switch on and the word starts printing.
2. Add **median time-to-peak** (shortening means faster rugs and faster exits needed) and **buy/sell imbalance on fresh pairs**.
3. Add **Ben's own realised hit rate** from [[screen-log]] and [[base-rates]] as a fourth layer, since that is the most direct measure that the edge is working.
4. Score the tracker against outcomes once ten days exist: does a HALF SIZE day actually precede a bad week.
5. Consider topping up Dune credits, which would make the whole runner history backfillable in one query.

# THREE REVIEW PASSES, 14 Sep 2026. v4 -> v8. Ben asked for the day boundary and for three iterations.
- [stated] Ben: "What would be the best time to track this and to then cut off so that the next day is reliable on that data? And also, do three more iterations on this approach before we start going further. Make sure there are no mistakes."

## THE ANSWER ON TIMING
| | |
|---|---|
| regime day | **06:00 UTC to 06:00 UTC, labelled by the day it STARTS** (13:00 to 13:00 Bangkok) |
| maturation | **+4 hours** |
| push | **10:30 UTC = 17:30 Bangkok**, which is 3 hours before the next US open at 13:30 UTC |
| cron | harvest hourly at :08, fuel 07:12, settle 10:15, gate + notify 10:30, all UTC |

**Why 06:00 and not midnight:** a midnight UTC cut splits the US session in half, so one good night lands in two calendar days and both read mediocre. **Measured, 8 days of hourly DEX volume on Robinhood Chain's top 3 pools: volume peaks 12:00-15:00 UTC at $275-293M an hour (the US open) and bottoms 04:00-07:00 at $100-110M.** The first cut of 10:00 UTC came from launch counts and was WRONG because that sample only covered part of each hour; it was thrown out.
**Why 4 hours:** time from birth to peak on Robinhood is p50 **1.1 min**, p90 19.8 min, p95 87.9 min. These things peak almost immediately, so maturation is cheap.
**Solana's own curve is NOT measurable yet** (GeckoTerminal returns hourly candles for only two hours a day on its top pools). It inherits 06:00 and gets re-measured at 14 full days.

## FOUR BUGS FOUND, EVERY ONE WOULD HAVE PRODUCED A WRONG VERDICT
1. **SOLANA WAS COUNTING ESTABLISHED TOKENS AS TODAY'S LAUNCHES.** The biggest "runner" read mc5 **$371,390,803** and peak $372,424,774, and **DexScreener agreed at $372,286,665**. The number was right; the token was not a launch. GMGN's board carries its near-complete and completed columns and the lane's `late` flag was NULL on all of them. Runners over $1M fell **40 -> 5** on 1,820 launches, against the 0.53 the measured pyramid predicts.
   **THE RULE, not a patch: A LAUNCH IS ONLY A LAUNCH IF WE SAW IT SMALL.** `born_small` = observed under **$60,000 within 150 seconds**. Everything else is the late population and is excluded and counted separately. Seventh time this project has been bitten by pooling two populations.
2. **SINGLE BAD TICKS READ AS PEAKS.** Six Solana tokens carried peaks within **0.03%** of each other around $793.8M. One poll cycle wrote a corrupted value across a batch. Fixes: peak = max of a **rolling median of three**; any tick above **25x the token's own median** dropped unless 5 observations corroborate; a peak two tokens share within 0.1% is discarded from both.
3. **CHAIN VOLUME READ -64.1% WHILE IT WAS UP.** DefiLlama backfills for MORE THAN ONE DAY: Solana's chart showed 1,041M and 1,061M for the 13th and 14th against a 2,741M weekly mean. Dropping only "today" was not enough. **Fix: 7-day mean vs prior 7-day mean, dropping the last TWO days.** Reads +29.5% against the API's own +7.8%.
4. **ABSOLUTE MARKET-CAP TIERS REWARD LAUNCHING BIG**, per [[trench-model-4]]. The score now rides on **peak / price at minute five** (3x, 5x, 10x, 25x). Ben's $1M/$5M/$10M counts are still reported, they just no longer drive the word. Floor `MC5_MIN = $2,000`, because 55,146 of 158,346 Solana observations sit under $500 (the pre-curve price) and produced multiples of 8,934,049x.

## OTHER CORRECTIONS
- **Robust z-scores**: log1p then median/MAD, because runner counts are heavy tailed and one huge day would mute a plain z for a month.
- **A day is only scored once COMPLETE**, and baselines read complete days only, so a dead collector can no longer plant a fake quiet day.
- **New FUEL input: BTC and SOL 7d from CoinGecko.** v4 was blind to the majors, which is the single biggest external driver.
- **New APPETITE inputs**: graduation rate within the first hour (obs `prog >= 0.99`), and **median holders at minute one**, which [[trench-model-4]] measured as the cleanest separator there is (60 for runners, 10 for smart-money tokens, 2 for dead).
- **The LIVE read**: last 6 hours against the same 6 hours on the prior 3 days, because the settled day is hours old when it lands and smart-money arrival is known within minutes.
- Peak provenance recorded. **On Solana `lab` overlaps the watched set by exactly ZERO rows** (it holds only late-discovered tokens), so Solana has no lab peak and **survival is unmeasurable there and must read NULL, never 0.**
- Time to peak only from lab peaks; a peak from a 60-minute window cannot be later than 60 minutes.

## THE GATE: `/opt/regime/regime_gate.py`, 12 checks, BLOCKS the Telegram push on failure
Per Ben's standing rule that a repeated mistake gets code and a gate, not another paragraph. Currently 12 pass, 0 fail, 1 warning.
**Check 3 is the one that caught the Solana bug: the day's runner count against that chain's measured pyramid** (Robinhood 1 in 409 to $1M, Solana 1 in 3,457). More than 20x expected blocks the push. Also checks: the cut sits in a measured quiet hour, no complete day without data, shared peaks, a cross-check that failed and wrote a clean zero, the multiple p99, every collector's heartbeat inside 26 hours, no chain metric moving over 300% in a week, no word on one layer or before 10 complete days, and Solana survival printing null.

## FILES
`/opt/regime/regime5.py` (harvest / fuel / settle / score / live), `regime_gate.py`, `regime_notify2.py`. `regime.py` and `regime_notify.py` are the retired v4 and are out of cron. Patches 6, 7 and 8 are kept on disk as the record of what each fix was for.

# THE BACKTEST, 14 Sep 2026. IT SAYS THE PREMISE IS UNPROVEN. v9 -> v10.
- [stated] Ben: "double check the approach 3 times, check if you would have been correct on past days etc"

## THE TARGET I BUILT TO MAKE A BACKTEST POSSIBLE AT ALL
Runner counts cannot be backfilled (Dune credits spent, pump.fun will not paginate). But **CHAIN BREADTH can**: GeckoTerminal serves 180 daily candles per pool, so for the top 100 pools per chain I rebuilt, per day, **the share of already-trading tokens that printed a +50% or a 2x day from the open**. That is the tape a fresh launch has to run in, and it is the only "was today good" measure with real history. **55 days on Robinhood, 29 on Solana.**

## THE RESULT, AND IT IS THE OPPOSITE OF WHAT THE TRACKER ASSUMED
| test | Robinhood (n=55, se 0.13) | Solana (n=29, se 0.19) |
|---|---|---|
| next-day persistence, 2x breadth | +0.23 | -0.01 |
| next-day persistence, +50% breadth | **-0.02** | **-0.15** |
| week to week, NON-overlapping | -0.53 | +0.33 |
| after hottest 20% of weeks, next 3 days | **0.061 vs 0.096 average** | 0.049 vs 0.029 average |
| after coldest 20% of weeks, next 3 days | 0.093 vs 0.096 average | 0.000 |
| yesterday's 7d stablecoin change -> today's breadth | **-0.13** | +0.27 |

1. **A hot tape does NOT predict a hot tomorrow on either chain.** Next-day persistence is inside the error bars on both.
2. **On Robinhood the hot side GIVES BACK**: the hottest weeks were followed by below-average days. A tracker that presses when hot and eases off when cold **would have been backwards** over these eight weeks.
3. **Solana says the opposite at the weekly horizon.** One of the two is noise and 84 days cannot say which.
4. **The fuel layer does not lead.** Both fuel tests are inside their error bars, with opposite signs.
5. **Ben's own instinct was closer than my design**: he said it "will probably peak on some day and then we might get a significant decline". That is the mean-reverting shape, and it is what Robinhood shows.

**SURVIVORSHIP CONTROL, run because the universe is today's top 100 pools:** the series has **no meaningful trend (-0.1% of the mean per day)** and detrending moves the autocorrelations by 0.02 (lag1 -0.02 -> -0.03, lag7 -0.31 -> -0.31). **The finding is not a survivorship artifact.** Caveat kept attached: breadth measures EXISTING tokens, not fresh launches, so it is evidence about the same tape rather than a direct test of the runner count.

## WHAT THIS CHANGED IN THE BUILD (patch10)
- **`DIRECTION = None`. The tracker no longer ASSUMES momentum.** While it is None it reports every number and prints NO CALL whatever the score. It is set only when `regime_breadth.py direction` reports a usable direction **on our own outcomes**. Flipping the sign to mean reversion on 84 days of one measure would be the same overfitting that has already cost this project a model and three features.
- **`/opt/regime/regime_breadth.py`, cron 09:40 UTC**: harvests the candles, writes a `breadth` table, and `link_outcomes()` writes `fwd1` and `fwd3` onto every regime day. **Every layer was an input; now there is an outcome column and the tracker can be scored rather than believed.**
- **CONFIDENCE SHRINKAGE**: the composite is multiplied by `min(1, (complete_days-9)/20)`. Twelve metrics z-scored against a ten-day baseline throw extremes by chance; at day 10 the word can barely leave NORMAL and only at day 30 does it reach full range.

## THREE MATHS ERRORS FOUND IN THE SAME PASS (patch9)
1. **THE WEIGHTS WERE NEVER APPLIED.** Each layer multiplied its metrics by a weight then called `statistics.mean()`, which divides by the COUNT not the sum of weights. Appetite's weights sum to 5.4 over 5 metrics and the scoreboard's to 5.6 over 5, so both layers read 8-12% hot and a metric's weight barely changed its influence. Replaced with a real `wmean()`.
2. **FUEL WAS ON A DIFFERENT SCALE FROM THE OTHER TWO LAYERS** (a percentage over a hand-picked divisor, averaged against z-scores). Fuel metrics are now z-scored against their own multi-month history, so the magic numbers are gone. A `majors` table with 365 days of BTC and SOL from CoinGecko was added so the majors term can be z-scored too.
3. **LIVE AND SETTLED DISAGREED ON WHAT STAGE 2 MEANS** (live required only smart money, settle required relative eyes AND smart money), so two numbers in the same message were not comparable. Same definition now, plus a loud flag when the live 6h read runs at 2x or 0.5x the prior days.

## THE WEEKEND QUESTION, ANSWERED
Robinhood by weekday (up50, n=7-8 each): Mon .078, Tue .108, Wed .091, Thu .100, Fri .116, **Sat .071 (lowest), Sun .128 (highest)**. The spread is smaller than the series' own standard deviation, so **the weekend effect Ben expected is not clearly there**. Saturday is the quietest day, but Sunday is the best. Day-of-week normalisation stays in as cheap insurance, not because it was proven.

## NEXT
1. Ten days of collection, then `direction()` on our own data decides the sign.
2. Re-measure the 06:00 UTC cut once each lane has 14 full days, and measure Solana's own quiet hour (GeckoTerminal's hourly candles are too sparse on Solana's top pools to do it today).
3. Once the direction is set, fit the four bands from the outcome column instead of using the current guessed thresholds (+0.5 / -0.5 / -1.2), which are unvalidated.

# THE LEADING-INDICATOR HUNT, 14 Sep 2026. Ben: "what actually predicts? Find them."
Searched 24 chain-day features x 2 chains x 4 horizons (next 1, 3, 5, 7 days) against "did the day pay" (share of trading tokens printing +50% or 2x), then a **train/test split: first 60% of days to fit the sign, last 40% to check it.**

## THE HEADLINE, AND IT IS THE USEFUL PART
| question | day-to-day persistence | forecastable? |
|---|---|---|
| **will tomorrow PAY** (breadth) | **+0.06 RH, -0.11 SOL** | **no** |
| **will tomorrow be BUSY** (chain volume, detrended) | **+0.73 RH** | **yes** |
| how many new launches | +0.18 RH, +0.70 SOL detrended | partly |
**Activity persists, payout does not.** That is the classic result in every market and it holds here. So the tracker now prints TWO lines: a BUSY / NORMAL / QUIET call, which is forecastable, and the payout call, which stays NO CALL. Blending them into one word was the design error.

## WHAT DIED OUT OF SAMPLE, after looking strong in sample
| feature | train (RH / SOL) | test (RH / SOL) |
|---|---|---|
| **BTC 7d realised volatility** (the best in-sample find) | +0.43 / +0.20 | **-0.35 / -0.27, flipped on BOTH** |
| chain TVL growth | +0.12 / -0.43 | -0.34 / -0.43 |
| volume vs its 7d mean | +0.08 / +0.43 | +0.42 / **-0.45** |
| capitulation, share down 20%+ | +0.39 / -0.16 | +0.18 / -0.10, wrong sign on Solana |
| dispersion of returns | +0.31 / -0.14 | -0.29 / +0.08 |

## THE ONE SURVIVOR
**AMIHUD ILLIQUIDITY = median(|daily return| / dollar volume).** The only feature that held its sign on BOTH chains out of sample: **RH train +0.39 -> test +0.16, Solana train +0.13 -> test +0.22.**
Read it as: on a day when price moves a lot per dollar traded, the tape is thin and easy to push, and the next three days pay better. Mechanically that is exactly the condition a runner needs. **One survivor out of 24 features is roughly what chance alone produces**, so it is collected and watched, never traded. Now a column in the `breadth` table.

## EVERYTHING ELSE TESTED AND NULL
Yesterday's payout, stablecoin flow (-0.13 / +0.27), DEX volume change, BTC/SOL/ETH returns at 1d/3d/7d, volume concentration (HHI), share of tokens above their 7-day high, launches per day, day of week. Longer horizons do not help: today's breadth against the next 3, 5 and 7 days reads +0.05, +0.03, -0.20 on Robinhood.

## TWO DEAD ENDS WORTH RECORDING SO THEY ARE NOT RE-TRIED
1. **NEW-WALLET ARRIVALS ARE UNUSABLE as a history.** `wal.entry_ts` reaches back to 3 Jul on Robinhood, which looked like two months of "is new money arriving". It is not: 764 wallets/day on 11 Sep, **14,394 on 13 Sep** when the trench lane went live, 15,726 on 14 Sep, with watched tokens going 91 -> 907 over the same days. **The series measures our own collector scaling up.** Normalising per token does not fix it because the token mix changed completely. Same population error, new costume.
2. **X ATTENTION IS NULL at the daily level.** Built `/opt/regime/xattn2.py`: a post RATE, not a count, because **the twitterapi.io search caps out** (240 tweets and still `has_next_page`, so a busy day and a quiet day both read 240). The fix is to take the first 60 tweets of a day in Latest order and measure how far back they reach: posts/hour. Real variation (13 to 60 posts/hr on "pump.fun" across 25 days) and **no predictive power**: +0.07 against tomorrow's payout, -0.04 against tomorrow's volume, 2se = 0.41. The coincident correlation is -0.27, also inside the bar.

## WHERE THE SIGNAL PROBABLY IS, and why it cannot be tested yet
**Not at chain-day resolution.** The one thing in this whole stack with a proven 35x lift is the RELAY from [[trench-model-3]] (eyes -> smart money -> social), and it is minute-level and per-token. The regime version of it is the **daily stage-2 handoff RATE**, which started collecting 14 Sep and needs ten days. That is the next real test, and it is the reason the tracker keeps collecting rather than being abandoned.

## BUILD CHANGES
- `regime_breadth.py` v2: `amihud`, `dn20` and `disp` columns, plus `activity()` which returns BUSY / NORMAL / QUIET from chain volume against its trailing week, and a depth read from the Amihud z-score.
- Partial-day bug caught immediately: `activity()` first printed QUIET at 0.40x purely because today's GeckoTerminal candle is half a day. It now reads the last COMPLETE day only.
- The droplet's own 39-day series reproduces the finding: up50 next-day persistence +0.02, and the weekly view says "take profit when hot" while the up2 measure says "no usable direction". **The two measures disagree on the same chain, which is the clearest argument for leaving DIRECTION unset.**

# PREDICTING THE PEAK, 14 Sep 2026. Ben: "what else could we collect to help us predict peaks before it drops?"

## THE REFRAME, and it is the whole answer
**A peak is not a price event, it is an OWNERSHIP event.** Every number collected so far (breadth, volume, launches, stage rates) is a symptom. The mechanism of a top is two things and the tracker measured neither:
1. **The pile of unrealised profit gets large against the liquidity that would have to absorb it.**
2. **New buyers stop arriving**, so that pile has nobody to sell to.
Price and volume are what happen AFTER those two line up, which is why the backtest found nothing: we were measuring the symptom and asking it to predict itself.

## BUILT AND LIVE TODAY: `/opt/regime/regime_top.py`, cron 09:50 UTC
### 1. THE OVERHANG AND THE FRAGILITY RATIO
Per chain: what every roster wallet paid, took out, and is still sitting on. `overhang = held + sold - bought`, and **fragility = overhang / pooled liquidity of tokens launched in the last 3 days**, i.e. how many times over the paper gains exceed the bid.
**First reading, 14 Sep: Robinhood bought $1.60M, sold $1.42M, still holding $390k, overhang $210k against $2.29M of liquidity = fragility 0.1x, 61% of holders in profit, 13% fresh wallets. Solana fragility 0.0x, 40% in profit, 20% fresh.** Nothing stretched right now.
**THE born_small FILTER IS WHAT MAKES IT MEAN ANYTHING.** Raw, Robinhood reads $463M of overhang and Solana $672M, almost all of it established tokens the GMGN board surfaced (one Solana token: bought $544k, holding $274M). Watched-from-small only: $210k and $433k. Same population rule, eighth application.

### 2. CROWDING: PERP FUNDING AND OPEN INTEREST
The tracker was completely blind to leverage, which is the cleanest euphoria gauge in crypto and leads spot tops rather than following them. **46 days backfilled** for BTC, SOL, ETH, HYPE, FARTCOIN and PUMP.
- **Hyperliquid `POST api.hyperliquid.xyz/info` works from the container**: `{"type":"metaAndAssetCtxs"}` gives live funding, open interest and mark; `{"type":"fundingHistory","coin":X,"startTime":ms}` gives 500 rows a call and paginates on the last timestamp.
- **OKX `www.okx.com/api/v5/public/funding-rate-history` also works** as a second source.
- **Binance `fapi.binance.com` returns HTTP 451 and Bybit returns CloudFront 403 from the container** (geo-blocked). Do not plan on either.
- Reading 14 Sep: everything ordinary. BTC +11.0%/yr against a 10.2% median, SOL +6.6% against 8.0%, PUMP +4.4% against 11.3%. Flag fires at 2x the 30-day median and above +20%/yr.

## THE QUEUE, ranked by mechanism strength x can-we-actually-get-it
1. **Realised profit taken per day against the paper pile.** The pile shrinking while price holds is a quiet exit in progress, which is the single most direct distribution tell. `sell_usd` and timestamps are already stored; it needs a daily delta.
2. **Fresh-wallet share of buyers as a time series** (already computed at 13% and 20%; needs history). A flood of brand-new wallets is late-cycle, a collapse is exhaustion.
3. **Time-to-fill**: minutes for a new launch to reach $50k. Lengthening means demand is thinning before any price shows it. Computable from `obs` today.
4. **Repeat-buyer share**: what fraction of today's buyers already bought something this week. Rising means the same money circulating and no new blood.
5. **Median buy size**: a falling ticket means the marginal buyer is weaker.
6. **Age of the profit**: gains made in the last 24 hours are itchy, month-old gains are sticky. Same table, one extra column.
7. **Dev and sniper net flow chain-wide**: the people who know selling across all launches at once.
8. **LP removals chain-wide**: liquidity pulled before the drop rather than after.
9. **Stablecoin mint and redeem** (Tether, Circle) from DefiLlama: redemptions lead risk-off and it is free.
10. **Open interest divided by market cap** for the majors, which is leverage relative to size and better than raw OI.
**Not obtainable**: bridge netflow (DefiLlama paid), CEX flows (paid), Binance and Bybit (geo-blocked from both boxes).

# THE MODEL THAT ALMOST SHIPPED, AND THE ONE TEST THAT KILLED IT. 14 Sep 2026.
- [stated] Ben: "if there's a way you can improve the current model, then go ahead and execute that. Test it against the past to make sure that you have a good model."

## WHAT LOOKED LIKE THE BEST RESULT OF THE PROJECT
A five-feature composite (capitulation, volume against yesterday, Amihud illiquidity, dispersion, volume against its week), **walk-forward so every z-score used only data before the day it scored**, signs fixed by mechanism rather than fitted, predicting the next 3 days of breadth:
- Robinhood **lift 1.33x** out of sample (top third 0.1238 against a 0.0932 average), rho +0.52.
- Non-overlapping windows: **1.48x**.
- **Permutation test: 0 of 300 random 5-feature composites reached it.** Median random 1.00x, 95th pct 1.24x, so p < 0.003.
- Then it "replicated": **Base 3.04x on n=146, Solana 2.06x on n=30**, chains it was never fitted on.

## IT WAS FAKE. THE DECAY TEST IS WHAT PROVED IT.
Push the forward window later and see whether the lift fades:

| chain | days 1-3 | 2-4 | 3-5 | 4-6 | 6-8 | reads as |
|---|---|---|---|---|---|---|
| **Base** | 3.02x | 3.00x | 3.04x | 3.02x | **3.04x** | **level artifact** |
| **Solana** | 2.16x | 2.06x | 2.02x | 2.00x | 1.78x | level artifact |
| **Robinhood** | 1.21x | 1.12x | 1.01x | 0.96x | **0.82x** | real, and it decays |

**A SIGNAL THAT IS EXACTLY AS STRONG SIX DAYS LATER AS ONE DAY LATER IS NOT A SIGNAL, IT IS A LEVEL DIFFERENCE.** On Base, breadth is **zero on 158 of 179 days** and capitulation is zero on most days too; the non-zero days of the two sparse series happen to sit in the same stretch of the sample, so the "lift" was measuring that they overlap, not that one predicts the other. **Restricting to a matched small-cap universe did not fix it: still 3.02x flat at every horizon.**
Only **Robinhood** has a series alive enough to test (zero on 7 of 56 days), and there the lift decays to nothing by day four and goes below 1 after, which is the shape of a genuine short-lived effect.

## THE HONEST STATE OF THE MODEL
- **On Robinhood only: a washout day (high share of tokens down 20%+, z-scored against its own history) is followed by about 1.2x the average breadth over the next one to three days, gone by day four.**
- Effective sample is about **eleven independent observations** (32 overlapping days at a 3-day horizon). That is a hint worth watching, not a model worth trading.
- The five-feature composite adds nothing over capitulation alone (1.33x against 1.32x), so the extra features are decoration.
- Multi-chain replication is NOT available: the +50% breadth measure is dead on every chain whose top-100-by-volume list is dominated by majors.

## TWO NEW GATE CHECKS, because this class of error has now cost a whole model
- **Check 13: a breadth series that is zero on more than half its days cannot carry a signal.** Live: Robinhood passes at zero on 14 of 48 days.
- **Check 14: any lift must DECAY with the horizon.** Fails when days 1-3 shows more than 1.1x and days 6-8 is still above 75% of it. Prints "LEVEL ARTIFACT" when it fires.

## AND A PROCESS BUG WORTH REMEMBERING
`regime_patch13.py` inserted its block BEFORE an anchor that then survived, so when the device relay dropped a reply and the patch was retried, **the block landed twice and the gate printed every check twice.** Fixed by `regime_dedupe.py`. **Every patch script from now on must be idempotent**: either check for its own marker before inserting, or replace the anchor rather than prepending to it.

# 17 Sep 2026 17:30 BKK. "REGIME BLOCKED" every day. Ben pasted the card. ONE of the two failures was a real gate doing its job, the other was a STALE ASSERTION, and there were two display bugs on top.

- [stated] Ben pasted the 17:30 card: `REGIME BLOCKED &mdash; 2026-09-16`, FAIL 12 and FAIL 13, each printed twice.

## ⚠ FIRST, THE THING I HAD TO RULE OUT: I killed `regime_top.py` at 10:26 UTC that morning (see [[alert-bot-fleet-10]], it had starved the trench WAL checkpoint for 37 minutes). The cron log shows `Terminated` immediately before this card, so the obvious read is that I caused it. **I did not, and the proof is `gate.log.1.gz`: the SAME two checks failed on 16 Sep at 10:30 UTC, before I touched anything** (FAIL 12 at "2 rows" then, "3 rows" now; FAIL 13 at "149 of 169 days" then, "158 of 180" now). Both are older than my kill and both grow by one day per day. **STANDING: when an alarm lands right after you changed something, read the PREVIOUS run's log before explaining it. The rotated log settled this in one command.**

## FAIL 12 WAS A STALE ASSERTION, AND IT HAD BEEN BLOCKING THE VERDICT DAILY. FIXED.
The check was `select count(*) from rday where chain='solana' and surv_den is not null` must be 0, on the reasoning recorded above: *"on Solana `lab` overlaps the watched set by exactly ZERO rows, so survival is unmeasurable there and must read NULL, never 0."* **That was true when it was written on 14 Sep and stopped being true the same week.** Solana now produces real survival rows: **14 Sep den 5 num 1, 15 Sep den 16 num 2, 16 Sep den 25 num 0** — and the gate's own check 3 counts 26, 24 and 42 Solana tokens over $1M on those days. Robinhood has 2 such rows and was never checked at all.
**Replaced with what actually needs guarding: the ratio must be sane wherever it exists** (`surv_den > 0`, `surv_num` present, `0 <= surv_num <= surv_den`), on BOTH chains. It passes. **RULE: a gate that asserts a CAPABILITY ("this chain can never supply X") expires the moment the lane improves. Gates should assert INVARIANTS ("a numerator never exceeds its denominator"), which do not expire.**

## FAIL 13 IS CORRECT AND MUST NOT BE SILENCED. It is the check that saved the project from shipping a fake model.
Solana breadth `up50` reads zero on **158 of 180 days**. Investigated properly before touching it: the series is **genuine, not backfilled zeros** — `regime_breadth.py` skips any day with fewer than 12 pools rather than writing a zero, and the `n` column shows a real universe of **12 to 43 pools, median 20**, back to 22 Mar 2026. So a 20-pool universe of established Solana pools simply almost never has a member up 50% in a day. **That is exactly the Base pattern that produced the fake 3.04x "replication" documented above (Base: zero on 158 of 179 days). Check 13 exists to catch precisely this and it is working.** Robinhood is fine: zero on 16 of 56 days, PASS.
**THE DESIGN FLAW IS NOT THE CHECK, IT IS ITS BLAST RADIUS: one unusable input on one chain blocks the ENTIRE verdict for BOTH chains.** Robinhood's checks all pass and Ben still gets nothing. **RECOMMENDED TO BEN, not done unilaterally because it changes the model's semantics: demote check 13 from a blocking FAIL to a per-chain disqualification, so the chain whose breadth is dead loses that layer (and check 14's decay test) while the other chain still prints its word.** Awaiting his call.

## TWO DISPLAY BUGS, BOTH FIXED
1. **Every failure printed TWICE.** `regime_gate.py` prints each failure in the body and again in its summary block with a `::` separator; `regime_notify2.py` grepped every line starting with `FAIL` and caught both. Now deduped on the check id, keeping one line. ⚠ The first dedupe attempt keyed on `l.split("::")[0]`, which does not collapse them because the body line has no `::` and keeps its detail column. Correct key is `re.split(r"\s{2,}|::", l)[0]`.
2. **`&mdash;` rendered as literal text.** Telegram's HTML mode decodes only `&lt; &gt; &amp;`, not arbitrary entities. Removed entirely rather than replaced, per Ben's standing no-dashes rule. Three occurrences.

**After the fixes the card is one clean line:** `REGIME BLOCKED 2026-09-16 / FAIL 13 sol breadth is alive, not mostly zeros  zero on 158 of 180 days`. Verified with `regime_notify2.py --dry`, which sends nothing.
