stale_samples is evaluated outside the
ranked universe, so the skipped total can exceed the universe count.
The alive door above is a separate population (a top-N diagnostic list), never summed with these.
How symbols are ranked — and what to expect
One list, three conditions, one score. This table is the whole decision: every symbol the optimizer considered, the stage it failed at, and — for the survivors — the order the scheduler fills its 8 bot slots in. A symbol opens a bot only if all three pass; it is then ranked purely on funding. Carry only — spread-mean-reversion ranking was removed on 2026-08-17: it was never simulated, and on these books the executable close spread (VWAP at leg size) runs ~1% against a −0.20 target, so a basis round trip cannot pay for itself.
- Funding paid ≥ 0pp over 14 days — the thesis, and the income we are actually buying. v16.4 retired the old 1.0pp bar: tested alone it did not improve pick quality (per-position APY 6.9% → 7.0% → 7.4% going 0 → 1.0 → 2.0pp) while slot utilisation collapsed 100% → 86% → 48%. A floor of 0 keeps only the useful part: the symbol must have paid something net positive.
- Funding still alive today (latest ≥ 0.02%/8h), so a symbol that paid well then died cannot coast in on history. Read as an EMA (α=0.05), not a spot value — changed 2026-08-17. Gate's quoted rate is a FORECAST of the next settlement and it resets at every boundary, rebuilding from the premium index over the interval. Measured over 3,644 settlements on 287 symbols, predicting the rate actually paid: EMA r=0.984 · interval mean r=0.953 · single sample r=0.878 · last settlement r=0.850. A single sample's own accuracy swings 0.84→0.98 depending where in the interval it lands, and it disagreed with the settled rate on 14% of 916k samples across 77% of symbols — which flipped marginal names in and out every settlement and cycled their bots.
- Expected value must be positive — realized funding × persistence, minus the 0.30pp round trip amortised over the hold. A negative score literally means "expected to lose at these thresholds". Added 2026-08-17 after two symbols ranked at −0.029 and −0.041/day (breakeven 6.9d and 27.8d, one with negative mean funding and persistence 0.20) — both qualified on sporadic spikes rather than steady carry.
Score = realized funding × persistence, net of amortised fees. Lending is not in it, and no longer gates either.
THREE EXIT RULES REMOVED on 2026-08-23 — min-hold, dead-thesis, RULE 7
Measured on research/bot_sim_v16.js, Jan–Jul 2026 (212d, 30s books, 4 slots, 340 capital,
entry bar 0.30, 2×). The three rules were tested together, because they interact:
| config | net USDT | APY total | trades | basis | fees | util | win |
|---|---|---|---|---|---|---|---|
| all three OFF (now live) | 65.40 | 33.12% | 87 | +27.43 | −14.54 | 92.8% | 95% |
| + dead-thesis only | 60.23 | 30.50% | 149 | +27.90 | −25.26 | 78.4% | 75% |
| + RULE 7 only | 57.44 | 29.09% | 109 | +15.47 | −18.41 | 88.0% | 70% |
| + min-hold only | 50.72 | 25.68% | 53 | +11.85 | −8.97 | 93.5% | 92% |
| all three ON (previous live) | 47.20 | 23.90% | 132 | +8.85 | −22.30 | 77.9% | 61% |
+18.20 USDT (+38.6%) on the same capital, with 87 trades instead of 132 — fewer, more profitable,
higher occupancy. Min-hold was the most expensive single rule (−14.68), almost entirely basis (+27.43 → +11.85):
it forbids a spread_target exit for 75–120h, so positions that reach their basis target are held through the compression
and give it back. It was also what made the dead-thesis exit look necessary — with min-hold on, the dead exit is the only way out
of a locked position, so removing only the dead exit is worse than removing neither. They had to go together.
RULE 7 (−7.96) recycles into fresher payers but pays the round trip twice.
Restore with MIN_HOLD=1, CARRY_DEAD_EXIT=1, RECYCLE_ENABLED=1.
⚠ REGIME CAVEAT — WITHDRAWN 2026-08-24. It was a DATA ARTEFACT, not a regime.
The original caveat said the gain was concentrated in Jan–May and the sign flipped late (Jan01–Mar15 +10.54,
Mar15–May22 +8.38, May22–Jul31 −1.84, Apr15–Jul31 −4.89), and warned the change
“optimises for a regime that may have passed”.
That split was measuring the data source. The simulator's runtime funding gates read FundingHistoryDaily,
which changes schema mid-window — true daily (dailySum) through April, 3-day smoothed (mean)
from May — so the two halves ran on different data, and the flip sits exactly on that boundary.
Re-measured with --true-daily (gates rebuilt from the settlement CSVs, one method across the whole window):
| period | delta, smoothed | delta, TRUE daily |
|---|---|---|
| Jan01–Mar15 | +10.54 | +11.78 |
| Mar15–May22 | +8.38 | +10.64 |
| May22–Jul31 | −1.84 | −0.23 |
| Apr15–Jul31 | −4.89 | +19.52 ← sign flips |
Removing the three rules wins in every period once the data is honest.
Mechanism: the dead-exit floor is 0.0110 and Gate's house minimum is a point mass at 0.0100. The smoothed mean sat above
0.0110 far more often than the truth did, so it MASKED how constantly the dead clock fires — with true rates the
all-rules-on arm churns down to 12.24 on Apr15–Jul31 where the smoothed run flattered it to 31.74.
Do not re-derive a regime conclusion from any run without --true-daily.
⚠ The published 38.91% baseline overstates the live system.
research/bot_sim_v16_result.json was generated with --no-rule7 --no-minhold, i.e. without two rules that were live.
Live-faithful before this change was 30.68% deployed / 23.90% total, not 38.91%. Regenerate via
research/export_sim_result.js at the live config.
Two gates were REMOVED on 2026-08-17 — and why
The lending gate (spot leg must lend ≥ 0.005 pp/day) is gone. Its evidence was real but
unrepeatable: EarnRateDaily spans 21 days and EarnAprHistory stops at 2026-08-04, so it could not be re-tested on the 7-month set the
rest of the strategy was validated on. Against it: PTB lends 0.59% APR — far under the floor — and is the best-paying position in the book at
109%/yr; and the reading itself was broken, forcing ppDay to 0 when history was thin, so AIO reported "5.7% APR = 0.0000pp/d" and was rejected
as a non-lender on stale data. Restore with EARN_GATE=1 if live trades show it earning its keep.
The reachability gate (openP95 ≥ 0.05) is gone — it was costing money.
Backtested Jan–Jul on 30s order-book data, 4 slots / 300 capital: no gate 34.28% APY vs p95≥0.05 21.62%, removing only 7 of 128 trades.
The mechanism: p95 measures the body of the basis distribution, but entries happen in the tail — the last 10 live entries were
0.309–0.319 against a 0.30 bar. So it discarded symbols that sit flat and spike occasionally, which is exactly the profitable carry entry.
Restore with CARRY_REACH_GATE=1.
Why a rejected symbol can show HIGHER income than a ranked one
Trailing income is history; the ranking is a forecast about which of it survives. A symbol can show strong 14-day funding and still be rejected because its EV is negative once fees are amortised, or because its funding is already dead today. The Total income column looks backward; the score does not.
Hold length dominates. Friction is ~0.5–0.6pp fixed per round trip while income scales with time: median P&L is +0.165pp at 7d, +0.930pp at 14d, +1.339pp at 21d, +2.372pp at 28d (win 92%). Churn is the main way this loses money — which is also why the alive gate reads a smoothed rate rather than a spot one.
Caveats: the removals above rest on a Jan–Jul 30s backtest (4 slots, 300 capital, entry bar 0.30) and on
live evidence; the lending gate could not be re-tested at all for want of data. Reachability magnitude is fragile — 12.7pp rests on a 7-trade
difference — though its direction held across every setting tried. Harnesses: research/bot_sim_v16.js,
carry_backtest_v15.js.
Archived — August 2026 portfolio simulation (older harness: Jan–Jul, 4 slots / 300 USDT, no clip execution — superseded by the section above)
What this is — and why it disagrees with the older numbers
The earlier harness asked “if I opened on day X, what happened over the next N days?” for every X independently. Two problems: starting Monday and starting Tuesday share N−1 of their N days, so 64 dates were really about 5 independent periods; and every date re-picked the top N from scratch, so a held position never actually occupied a slot a better symbol then couldn’t use.
This walks the period once, holding real positions in real slots until a real exit fires. Hold length is an outcome, not an input. The honest sample is therefore the trade count below — not the number of dates.
Max rise per trade is the worst upward price excursion during the hold. For a cash-and-carry the pair is delta-neutral in P&L, but the SHORT leg’s margin is eaten by a price rise with no offset — Gate gives these symbols a 0 collateral discount — so that column is what a liquidation would have come from. “Liq at” lists the leverages that excursion would have blown, before safeguard RULE 5 deleveraging.
Which close statistic the exit uses — and why it changed
The bot watches continuously and fires the instant the close spread prints below its
threshold (sustained ~120 ms). So the right daily question is “did it ever GET there?”, not “where did it sit?”.
With 2398 samples/day from the 30-second order-book archive, closeP25 is a level the spread holds for a
quarter of the day — far too conservative. Using it forced windowLevel down to an artificial
−0.750 just to make exits fire at a realistic rate. Switching to a deep statistic lets the level be the
bot’s real −0.20 threshold. The difference is not small — see the sweep below.
Why leverage does not add value here
It is not RULE 5 over-reacting: most trades take zero or one shed of ~10%, and only a symbol that ran +41.85% needed four. Leverage fails because one position in seven moves so violently that no amount of deleveraging saves it (LAB ran +127%, IN +265%). The extra notional is worth ~50%; a single liquidation in six-to-eight trades costs more than that. Every exit model tested gives the same ordering.
notional ÷ equity as its own price moved. It starts at 3×, rises when price runs
against the short, and drops each time RULE 5 sheds. The dashed line is the trip level (3 × 1.35 = 4.05×);
the red line is where the short dies.
Research loop
checking……
botScheduler.js. Safe to use — the scheduler will resume its normal 15-min tick cycle.dashboard_audit.jsonl.
halt.flag with mode:"no-new". The scheduler keeps
open-position bots running, so they still take their basis exit, time stop,
patient time stop and safeguard rules — it simply launches nothing new.This is the switch for a wind-down or a capital change. HALT below is not: it stops every bot, which leaves an open position with no bot at all and only safeguard's 5-minute margin tick watching it. Nothing is closed here — use force-close for that.
pm2 start botScheduler.js, not after pm2 resurrect,
not after a reboot. This is the difference from “Stop All” below, which the scheduler undoes on its
next tick.The scheduler is deliberately left running so
safeguard.js keeps
protecting open positions — halting stops trading, it does not abandon your money.
Open positions are not closed; use force-close for that.Type HALT to confirm.