System Processes
Active Positions 0
Open Positions 0
Running Bots 0
Ranking funnel 0
How symbols are ranked — and what to expect

One list, four 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 3 slots in. A symbol opens a bot only if all four pass; it is then ranked purely on funding.

  1. 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 at all (per-position APY 6.9% → 7.0% → 7.4% going 0 → 1.0 → 2.0pp) while slot utilisation collapsed 100% → 86% → 48%. It looks backward, and mostly re-measures what the score already sorts on. A floor of 0 keeps only the useful part: the symbol must have paid something net positive.
  2. Funding still alive today (latest ≥ 0.02%/8h) — the real funding gate now, so a symbol that paid well then died cannot coast in on history. 0.01 is Gate's baseline rate — the house default when the market is balanced, which 65% of candidates sit at exactly — so a floor above it is what separates genuine demand from "not zero". It also matches the bot's own live open gate (CARRY_MIN_FUNDING_OPEN): below it the bot refuses to open, so anything qualified between 0.01 and 0.02 was a launched bot that could never take a position.
  3. The spot leg lends ≥ 0.005 pp/day, measured on rates actually PAID, not advertised.
  4. The entry is reachableopenP95 ≥ 0.05%. The bot only opens above that bar, so if the symbol's basis never gets there the entry simply never prints and the slot idles. This is an upper-tail question, not a typical-day one: measured on 54 real fills, the bot's actual entries sit above openP75 on 93% of positions (mean gap +0.388) — it waits for the good print, so the tail is what decides reachability.

Score = realized funding × persistence. Lending is not in the score — it only gates.

Why lending is a gate and not a score

A coin lends richly because a crowd wants to borrow it — to short it. That same crowd shorts the perp, which pushes funding against us. Measured corr(lending, funding) = −0.759. Ranking on net income therefore selects precisely the symbols where the two cancel: over 27 backtested dates those picks earned +0.2675 pp/d lending and paid −0.2617 funding — a wash. That was the RLC trade.

The gate is mostly a junk filter, not an income source

It lifts income from −1.608 to +0.406pp. A coin nobody will lend against has no borrow market, which usually means thin or newly listed: its trailing funding spike is noise that reverts, and its book is wide. Filtering on book width alone does not work (−2.159pp), so lendability carries information the book does not.

Why a rejected symbol can show HIGHER income than a ranked one

It can, and that is the gate doing its job. Today AI shows the highest total income of any candidate (0.194 pp/d) and is rejected because it lends 0.0% — no borrow market. The bet is that such a symbol's funding is a spike that reverts, not a rate you will still be collecting in two weeks. Over 27 backtested dates that bet paid: unfiltered picks realized −1.608pp of income, filtered picks +0.406pp. The Total income column is trailing history; the gate is a forecast about which of it survives.

What to expect — median P&L per position, 4 slots, basis and fees priced in:

rank funding, require lending (this rule)+0.930ppwin 59%
rank net income (the old second door)−0.183ppwin 48%
rank funding, no lending gate−2.062ppwin 33%

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%). Expect few trades held long — churn is the main way this loses money.

Caveats: 27 decision dates over May–Jul 2026, one regime. The backtest holds blind — no death clock, funding-collapse stop or time stop — so its mean is worse than the live system; the median is the honest figure. Harnesses: earn_ranking_backtest_pnl.js (P&L) and earn_ranking_backtest.js (income only).

Loading…
Continuous portfolio simulation
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.

Loading…
Effective leverage through the period · a 3× run, so RULE 5 can be seen working
Each line is one position's 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.
Loading…
Threshold-crossing durations — how long spreads sit beyond the open / close bar
Loading…
Side: Group: Window:
Loading…
“% ≥ floor” = share of crossings that outlast the bot’s sustain floor (open 100 ms / close 200 ms) — i.e. actionable opportunities. “fired” = episodes that triggered a real order.
Burst confirmation — does a fresh crossing PERSIST? (cross_sampler re-fetches ~0.6 s; phantom vs durable)
Loading…
Side: Group: Window:
Loading…
“confirm %” = share of bursts that persisted (≥50 % of samples still over threshold). On the Open side, a row highlighted 🚫 is phantom-gated by the scheduler: ≥4 bursts of evidence and confirm-rate < 25 % → its bot launch is suppressed. Depegs confirm high (they persist) and are filtered separately.
Entry unwinds — aborted one-leg entries (fees you paid without a trade)
Loading…
Closed Positions (last 50)
Scheduler
Start or restart botScheduler.js. Safe to use — the scheduler will resume its normal 15-min tick cycle.
Spread Thresholds — edit spreadThresholds.json
Search for a symbol, adjust the open/close thresholds, then type the confirmation phrase. The file is written atomically with rolling backups (last 5 kept as .bak.N). Changes are picked up by bots on their next hot-reload cycle (hourly).
Stop a Single Bot
SIGINT + delete from PM2 and RunningBot collection. Refused if the bot has an open position — use Force Close Position below for that case.
⚠ Force Close Position
Stops the bot, then places market orders to close the position (spot sell + futures close). Use when a position is stuck (thresholds unreachable, bot unresponsive). Review the preview carefully — the estimate is rough; actual P&L is computed from fill prices.
Recent Actions (audit log)
Last 50 destructive actions. Stored in dashboard_audit.jsonl.
⛔ HALT — stop everything and keep it stopped
Stops every bot and writes a halt flag the scheduler honours on every tick, so nothing comes back — not after 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.
Scheduler process ⚠️ Stopping the scheduler also stops safeguard — nothing will watch margin, liquidations or the death clock on open positions. HALT alone is the safer stop; use this only when you need the process itself down.
⚠ Danger Zone — Stop All Bots (does NOT stay stopped — prefer HALT above)
This will gracefully SIGINT every running bot (waits 8s each for order cancellation), then stop the scheduler. ⚠️ It leaves no flag: anything that starts the scheduler again relaunches the whole fleet. Type STOP ALL BOTS to confirm.
RAM Usage
CPU Usage
Bot Count
Event Loop Lag
Bot Health
Per-Bot Memory