walid@portfolio:~/lab/jev-grok-memecoin-desk$
cd../lab
01ideaOct 2026

Jev picks the coin, the Grok Bots trade it

A memecoin desk where every judgement is a typed number. A collector pulls fresh launches on Solana, BSC and Robinhood Chain, TypeSafe’s Jev answers yes-or-no, pick-one and score questions about each, and the Grok Bot seats size, fill and exit the one it picks. Every file and prompt from the guide, the overnight $100 run from its video, and what the checks found

A memecoin trading desk rebuilt so nothing on it reasons in prose. A collector pulls fresh launches on Solana, BSC and Robinhood Chain and kills most of them with free arithmetic; TypeSafe’s Jev answers typed questions about the survivors (is the float concentrated, is this X account really the project’s, has the move already happened) and then picks at most one; and the Grok Bot seats size it, fill it on the FOMO app and exit on a single rule. Every file and prompt from the guide is here, run end to end against the real SDK. As published the code can’t trade a single Solana token, so the bugs that stop it are fixed in place and marked. The companion video of a $100 overnight run to $2,197.67 is here too: its coin’s chart checks out, but the run is a replay of candle closes that bets 54 to 81% of the bank on each trade, where the guide’s own rules allow 6%.

JevGrok BotMemecoinsTypeSafePythonRiskTypeSafe ↗TypeSafe docs ↗TypeSafe console ↗typesafe-sdk on PyPI ↗Grok Bot docs ↗FOMO ↗FOMO’s terms ↗FOMO’s trading fees ↗GeckoTerminal API ↗DexScreener API ↗Etherscan API V2 ↗Cloudflare quick tunnels ↗Jev, in the Lab ↗
The guide’s cover art.
i
What this is

A layer that sits on top of an existing desk of xAI Grok Bot seats that trade memecoins. It changes one thing: every judgement on the desk is now a typed number with a probability on it, instead of prose the code has to parse. A collector pulls every fresh token from the FOMO trading app with its metrics, the chain data for whichever chain it lives on, and the project’s X account. TypeSafe’s Jev, a model that answers typed questions instead of writing text, judges each token and then makes one final call: which of these, if any, do we trade. The Grok Bot seats trade the token Jev picked.

!
Separate the system from the story

The guide is sold on its results: “six figures” from the earlier version of this desk, $1,000 turned into $101,607.14 in ten days, and in the companion video $100 turned into $2,197.67 overnight. No wallet or trade history backs any of them, and the earlier guide itself reported $89 turned into $7,769. The video’s coin and chart are real, but every one of its 18 fills sits exactly on a five-minute candle close, the signature of a replay rather than live orders, and every trade bets 54–81% of the bank where the guide’s SIZE prompt caps a ticket at 6%. The useful part is the judging layer, and it stands on its own. This entry keeps all of the guide and checks everything around it.

!
Before you build it

FOMO’s terms forbid both halves of this design: reading its private API with your session, and letting bots execute trades. As published, the code also can’t trade a single Solana token, and the RISK seat can’t free the book after its first close, so the desk would never scan again. Those bugs are fixed below and marked “fixed” in the code; the design problems that remain are listed after the files. Memecoins are among the riskiest things you can trade, and nothing here is financial advice.

Checked, not copied

What held up, on 2 October 2026

Checked against TypeSafe’s docs and SDK source, live GeckoTerminal, DexScreener, Solana, Robinhood Chain and Etherscan responses, FOMO’s help centre, terms and web app, xAI’s Grok Bot docs and Cloudflare’s, and by running every file in the guide end to end.

ClaimWhat the source says
TypeSafe and JevAs stated. TypeSafe came out of stealth on 15 September 2026 with a $40 million seed round led by DCVC, and its chief executive, Diogo Almeida, co-wrote the InstructGPT paper. Jev returns typed answers, never text; TypeSafe quotes 70 to 500 milliseconds end to end.
Price and limitsThe price holds: $0.042 per million input tokens, output free, and 64,000 tokens a request with 32,000 for the state plus the longest question. The rate limits have changed since the guide: TypeSafe now lists 100,000 tokens and 40 requests a second, and says they can change without notice.
The three question typesCorrect, including up to 255 options for a choice and 10 levels for a score. A score comes back as a probability-weighted level counted from 0, so a four-level rubric scores 0 to 3. Only choices and scores carry a confidence.
The SDKquestions.py builds and sends every set as written on typesafe-sdk 0.7.2. judge.py needs 0.7 or later, since earlier releases have no model_dump(). The sample reply under the key test is TypeSafe’s own documentation example for a different question.
GeckoTerminalFree and keyless, at about 10 calls a minute, a figure it says fluctuates with traffic. But three dossiers a cycle is a choice, not a limit: the code spends its calls in one burst every 15 minutes. Cutting pages to 1 frees three calls, not six.
The chain tableOnly partly right. On tokens a few minutes old, holder counts were null on every chain, not just Robinhood Chain. Solana reports mint and freeze authority as the strings "yes" and "no", and is_honeypot as "unknown". Solana handles can be post URLs too.
Solana’s top wallet, “free”No. The public RPC refuses getTokenLargestAccounts outright, and the largest account of a launchpad coin is its bonding curve or pool, which held 36% to 76% of supply in tests.
Robinhood Chain and EtherscanChain id 4663 and rpc.mainnet.chain.robinhood.com are right. A browser User-Agent isn’t needed: most clients got through, though Python’s default urllib one was refused. Etherscan’s V2 key covers 63 chain ids and its three holder endpoints are PRO-only, as stated, and BSC itself is a paid-tier chain there. It doesn’t cover Solana, but it does cover Robinhood Chain, free until 15 October 2026.
FOMOA memecoin trading app from FOMO Labs, Inc., with non-custodial wallets on Solana, Base, BNB Chain, Monad, Ethereum and Robinhood Chain. It has no public API: filterTokens is its own web app’s private backend, which batches 100 tokens a call, not 20.
FOMO’s termsThey forbid this design. Section 16 bars accessing non-public areas of the service, collecting data with bots or scrapers, and using “automated scripts, bots, software, or any other automated means to … execute trades.”
FOMO’s feesNot the guide’s formula. On Solana FOMO charges $0.10 under $5, 2% from $5 to $47.50, a flat $0.95 up to $190, then 0.5%; elsewhere 0.5% plus network fees. 0.45% is the rate after a referral discount, and the guide’s FOMO links were referral links that pay their owner 25% of your fees. This page links FOMO directly.
“4.75% round trip”That is one leg. Under the guide’s own formula a $20 round trip costs $1.90, or 9.5%.
Grok BotThe seats run on hosted cloud computers (Cursor’s, per xAI’s docs), skills are shared across all of an account’s bots, and access comes with a paid Cursor plan or a linked SuperGrok, SuperGrok Plus, SuperGrok Heavy or X Premium+ subscription, with a weekly allowance. The X plugin isn’t built in: it is a Cursor Marketplace plugin that signs in as you and bills X API credits per read.
“Six Grok Bots”Seven, counted the guide’s way. The original desk is six seats plus CHIEF, its chief of staff, and SOCIAL took BOOK’s seat, so the desk runs seven bots: SCAN, VET, SOCIAL, SIZE, FILLS, RISK and CHIEF. A Grok Bot group chat holds two to six bots.
“Unchanged from the original”The three seat prompts did change. SIZE gains the 2% liquidity cap and drops the original’s add-on-a-retest and reserve-the-bills rules, FILLS turns the original’s approximate fees into a fixed formula, and RISK gains the release step.
The bill5.6 cents a day, not 5.7, and only the judge’s share. SOCIAL’s X reads cost about 6 cents each in X API credits, up to about $17 a day at three reads a cycle, and FOMO’s fees and Grok Bot usage come on top.
The quick tunnelWorks as written. Cloudflare calls quick tunnels testing-only, with no uptime guarantee and a new hostname on every restart, so every seat’s JUDGE_URL breaks whenever cloudflared restarts.
The earlier resultsUnverifiable, and inconsistent. The original guide behind “six figures” reported $89 turned into $7,769.14 in seven days; $1,000 to $101,607.14 in ten days appears only in the later video post. Neither publishes a wallet or account history.
The idea

Code fetches, the model judges, code decides

This guide assumes the Grok Bot desk from its author’s earlier guide and doesn’t repeat it; the three seat prompts that matter here, SIZE, FILLS and RISK, are restated below. What it adds is the judging. Before, the seats reasoned about each token in prose and the code had to read that prose. Now a collector gathers the facts, Jev answers fixed questions about them with probabilities, a threshold in code turns each probability into a decision, and only then does a bot act.

The desk as its author shows it, on day 1 of an earlier $1,000 run: Jev’s analysis on the left, the seats in the middle, the balance below. The checks on its Jev panel aren’t the questions in the guide’s questions.py, and its figures can’t be checked.
i
How the author says to use it

Paste the entire guide into a coding agent and tell it to build the desk. Every file and prompt is on this page, ready to paste, in the order to build them.

What you need first

Six things

Each one says what it is, what you do with it, and why the desk needs it.

Need 1

A Jev key

What it is. Jev is TypeSafe’s model, and it doesn’t generate text at all. You send it a state and typed questions; it sends back typed answers with probabilities, in 70 to 500 milliseconds. TypeSafe came out of stealth on 15 September 2026 with a $40 million seed round led by DCVC; its chief executive co-wrote the InstructGPT paper.

What you do. Sign in at console.typesafe.ai, open Keys and create one. Copy it the moment it appears, because you won’t see it again, and put it in your environment.

Store the key

environment5 lines
# mac / linux
export TYPESAFE_API_KEY="ts-..."

# windows, then open a new terminal
setx TYPESAFE_API_KEY "ts-..."

Why. One service on the desk holds this key and calls this endpoint. No bot ever sees it.

The endpoint

jev endpoint7 lines
POST https://api.typesafe.ai/v1/systemone
Authorization: Bearer $TYPESAFE_API_KEY
model: jev-latest
input: $0.042 / Mtok
output: FREE
limits: 250k tok/sec, 1200 req/min
context: 64k, 32k for state + longest question

Prove the key works first

One curl before anything else, and you’ll never again confuse a bad key with a bad question.

test the key8 lines
curl -X POST https://api.typesafe.ai/v1/systemone \
  -H "Authorization: Bearer $TYPESAFE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"state":"payouts have been failing for 3 days","model":"jev-latest",
       "questions":{"urgent":{"type":"noul","instructions":"This conveys urgency"}}}'

# -> {"model":"jev-1.13.0","answers":{"urgent":{"type":"noul","noul":0.95}},
#     "usage":{"input_tokens":296,"output_tokens":20}}
Need 2

The three question types

The entire API. Every question the desk asks is one of these three, and they appear in every file below.

TypeAsksReturns
noulAsksIs this trueReturnsnoul, from 0 to 1
choiceAsksWhich one, up to 255 optionsReturnschoice, probabilities, confidence
scoreAsksRate it on my rubric, up to 10 levelsReturnsscore, probabilities, legend, confidence

What you do. Nothing yet, and nothing by hand once it runs. The playground in the TypeSafe console is a one-time tool while you write questions: drop any token in as the state, ask your question, and watch the probabilities move as you reword it. A question is a prompt and has the same failure modes as a prompt. Once the wording is right it goes into questions.py and you don’t open the playground again.

In production nothing is pasted anywhere. collect.py pulls fresh pools from GeckoTerminal, metrics from FOMO twenty at a time, trade counts from DexScreener and contract data from the chain, then builds the state and sends it. You log into FOMO in Chrome once, and that is the only manual step in the whole desk.

Why. A noul is an if, a choice is a switch, a score is a sort. Your code reads the number, the threshold decides, and nobody reads a paragraph.

The split to hold onto: Jev takes the judgements, code takes the arithmetic. Every number on the desk is computed before the call and passed in as a field.

Need 3

The SDK

What it is. The Python client for that endpoint. What you do: one command.

Install

install the sdk1 lines
pip install "typesafe-sdk>=0.7"   # needs python 3.10 or newer; judge.py needs 0.7+

Why. judge.py imports AsyncTypeSafeClient from it and questions.py imports Choice, Noul and Score. Without it both files fail at import.

Need 4

A FOMO session

What it is. Where the token universe comes from. FOMO has no public API, so the collector reads your own logged-in session.

What you do. Log into FOMO in Chrome and leave that profile alone. The bearer token lives about an hour, and the client refreshes it from the browser on its own. Chrome 136 and later ignore remote debugging on the default profile, so “that profile” has to be a separate one, started with --user-data-dir.

Why. This one call returns every metric the free pass needs, twenty tokens at a time, so hundreds of candidates cost one request instead of hundreds.

The one FOMO call

fomo filterTokens6 lines
POST prod-api.fomo.family/proxy/filterTokens
body: ["<address>:<netId>", ...]      20 at a time
-> marketCap, liquidity, volume24, holders, priceUSD,
   change5m / change1 / change4 / change12 / change24, createdAt

netIds: Solana 1399811149 · Robinhood 4663 · BSC 56 · Base 8453 · ETH 1 · Monad 143
!
FOMO’s terms forbid this

There is no public API: filterTokens is the private backend of FOMO’s own web app, and the collector reaches it with your logged-in session token. FOMO’s Terms of Service, section 16, prohibit exactly that, accessing non-public areas and collecting data with bots or scrapers, and also using “automated scripts, bots, software, or any other automated means to … execute trades.” Building this desk on FOMO breaks its terms and can cost you the account. A documented data source such as Codex’s filterTokens API, which uses the same network ids and field names, removes the scraping half; the trading half has no sanctioned automated route.

Need 5

Chain data

What it is. GeckoTerminal’s public API: free, no signup, no key.

What you do. Nothing to set up. Just hold onto one number: about 10 calls a minute, which GeckoTerminal says fluctuates with traffic.

Why. That limit is the shape of the entire funnel. Six calls list fresh pools across three chains, which leaves three dossiers per cycle, which is why everything above the dossier has to kill hard.

The two GeckoTerminal calls

geckoterminal4 lines
GET api.geckoterminal.com/api/v2/networks/{net}/new_pools
GET api.geckoterminal.com/api/v2/networks/{net}/tokens/{addr}/info

GT_NET = {1399811149:"solana", 4663:"robinhood", 56:"bsc", 8453:"base", 1:"eth"}

Read this before you write a single question

The desk trades Solana, BSC and Robinhood Chain, and they don’t hand back the same fields. The author called all three on 23 September 2026.

FieldSolanaBSCRobinhood
holders.count + distributionSolanaYesBSCYesRobinhoodNull on fresh tokens
mint_authority / freeze_authoritySolanaYes, and it decidesBSCNullRobinhoodNull
is_honeypotSolanan/aBSCtrue / falseRobinhood“unknown”
twitter_handleSolanaCleanBSCCleanRobinhoodSometimes a post URL
Exact top walletSolanaSolana RPC, freeBSCEtherscan PRORobinhoodNo free path
Holder-count fallbackSolanaNot neededBSCNot neededRobinhoodFOMO filterTokens

So each chain gets its own question set, routed in code. For Solana the guide reads the top wallet off the public RPC with getTokenLargestAccounts and getTokenSupply. In testing the public RPC refused the first of those outright, so you need a keyed Solana RPC; the collector below reads it from SOLANA_RPC_URL.

Robinhood Chain’s public RPC is rpc.mainnet.chain.robinhood.com. The guide says it answers 403 without a browser User-Agent; in testing most clients got through, though Python’s default urllib User-Agent was refused. Etherscan’s V2 API is one key for 63 chain ids via ?chainid=, and its topholders, tokenholdercount and tokeninfo endpoints are PRO-only. It doesn’t cover Solana, but contrary to the guide it does cover Robinhood Chain. The guide calls it worth buying if you trade mostly BSC (a paid-tier chain on Etherscan), since it drops into the dossier step without changing a single question.

Need 6

The desk itself

What it is. The Grok Bot desk from the original guide, plus the venue it fills on: six seats and a chief of staff, CHIEF, so seven bots, with SOCIAL now in the old BOOK seat.

What you do. Nothing new. The seats already exist; they just get their token from a different place now. The project’s X account is read by Grok Bot’s own X plugin, and the handle comes off the chain rather than from a search, so no bot goes hunting for “the project’s Twitter” and finds a fan account.

Why. Fills go through FOMO and nowhere else. One venue and one execution path means a bad fill is always traceable to one place.

The funnel

Each pass is more expensive than the one above it, so each has to kill harder than the one below. The free cut touches no network at all: everything it reads arrived with the FOMO batch. Only what survives it is worth a DexScreener call, and only what survives that is worth one of the dossier slots.

the funnel7 lines
0. UNIVERSE  GeckoTerminal new_pools, 3 chains          -> fresh launches
1. LIST      FOMO filterTokens, 20 per call             -> hundreds, one batch
2. FREE CUT  age, liquidity, volume, mcap. No network   -> tens
3. TRADE CUT DexScreener buys and sells, one per token  -> a handful
4. DOSSIER   GeckoTerminal info + chain RPC + X         -> three per cycle
5. JUDGE     market + chain + social per token          -> scored shortlist
6. PICK      one choice over the shortlist              -> one token, or none
The files

Build them in this order, top to bottom

Nine Python files and five prompts, built in this order, top to bottom. The guide counts seven files and four prompts. One more file is yours to write: fomo_api.py, the FOMO client that collect.py and main.py both import, is never given. It has to return FOMO’s rows for a list of “<address>:<netId>” ids and refresh the session token.

The code is the guide’s, run end to end against the real SDK. Where it broke, the smallest fix is in place, marked “fixed” in a comment, and every change is listed after the files.

0 · JUDGE

judge.py

It runs on one machine, holds the only Jev key, and makes no trading decision of any kind. The bots get a desk secret, never the key itself.

judge.py49 lines
import os
from fastapi import FastAPI, Header, HTTPException
from pydantic import BaseModel, ValidationError
from typesafe_sdk import AsyncTypeSafeClient, TypeSafeAPIError, TypeSafeError
from questions import SETS
import book

DESK_SECRET = os.environ["DESK_SECRET"]     # for the bots. NOT the TypeSafe key.
client = AsyncTypeSafeClient()              # reads TYPESAFE_API_KEY itself
app = FastAPI()


class Ask(BaseModel):
    question_set: str
    state: dict


@app.post("/")                  # fixed: the bare tunnel URL works too, so JUDGE_URL needs no path
@app.post("/judge")
async def judge(ask: Ask, authorization: str = Header("")):
    if authorization != f"Bearer {DESK_SECRET}":
        raise HTTPException(401, "bad desk secret")
    if ask.question_set not in SETS:
        raise HTTPException(422, f"unknown question set {ask.question_set}")

    qs = SETS[ask.question_set]
    # fixed: Jev's errors reached the bots as a bare 500. Pass a real status on instead.
    try:
        qs = qs(ask.state) if callable(qs) else qs    # pick builds options at call time
        r = await client.system_one(state=ask.state, questions=qs)
    except (ValidationError, KeyError) as e:          # a question that could not be built
        raise HTTPException(422, f"bad question set {ask.question_set}: {e}")
    except TypeSafeAPIError as e:                     # Jev answered with an error, after retries
        raise HTTPException(422 if e.status == 422 else 502, f"jev {e.status}: {e}")
    except TypeSafeError as e:                        # Jev unreachable or timed out, after retries
        raise HTTPException(504, f"jev unreachable: {e}")

    # raw answers out. never flattened, never thresholded here.
    return {"model": r.model,
            "answers": {k: v.model_dump() for k, v in r.answers.items()},
            "usage": r.usage.model_dump()}


@app.post("/release")           # fixed: RISK runs in the cloud and can't reach book.py on
async def release(authorization: str = Header("")):  # this machine, so it frees the book here
    if authorization != f"Bearer {DESK_SECRET}":
        raise HTTPException(401, "bad desk secret")
    book.release()
    return {"released": True}

Run it, and open a tunnel

The bots run on hosted cloud computers, so the judge needs a public address; cloudflared’s quick tunnel gives it one. Cloudflare calls quick tunnels testing-only, with no uptime guarantee and a new hostname every time they start.

run the judge6 lines
pip install fastapi uvicorn
export DESK_SECRET="$(openssl rand -hex 24)"
uvicorn judge:app --host 127.0.0.1 --port 8080     # fixed: was 0.0.0.0, open to the network
cloudflared tunnel --url http://localhost:8080     # bots run in the cloud, not on your box
# every seat's JUDGE_URL is the https://...trycloudflare.com address this prints.
# It changes each time cloudflared restarts; a named Cloudflare Tunnel keeps one.

The rules baked into judge.py

judge rules11 lines
- ONE CALL PER TOKEN, never one per question. State is tokenized once. Splitting a set
  into five calls multiplies the bill by five for identical answers.
- STATE IS A NAMED OBJECT, never a blob. Send only fields the questions read. Accuracy
  falls as the state fills with material unrelated to the decision.
- ANSWERS GO BACK RAW. The caller owns the threshold, the judge does not.
- THRESHOLDS DO NOT TRANSFER between primitives. A choice is relative and settles which.
  A noul is absolute and can be low for all of them.
- LOG THE MODEL ID from the response. Aliases move when a release ships.
- ARITHMETIC STAYS IN CODE. Compute it, pass the result in as a field.
- 401 bad key, 422 bad question, 429 rate limit, 529 overloaded. Back off on 429 and 529.
  Never retry a 422, the question is wrong and will stay wrong.
1 · SCAN and VET

collect.py, the collector

Jev fetches nothing: it is a function, so this is the code that goes and gets everything. SCAN runs universe and shortlist; VET runs the dossier.

collect.py161 lines
import os, re, time, requests
from fomo_api import Fomo                      # Privy bearer out of Chrome over CDP

GT  = "https://api.geckoterminal.com/api/v2"
DEX = "https://api.dexscreener.com/latest/dex/tokens"

# the three chains the desk trades, plus Base which shares the BSC question set
GT_NET   = {1399811149: "solana", 4663: "robinhood", 56: "bsc", 8453: "base"}
FOMO_NET = {v: k for k, v in GT_NET.items()}


def age_minutes(created) -> float:
    """createdAt comes back as epoch seconds or milliseconds depending on the row."""
    if not created:
        return 0.0
    c = float(created)
    if c > 1e11:                                # milliseconds
        c /= 1000
    return max(0.0, (time.time() - c) / 60)


def universe(nets=("solana", "bsc", "robinhood"), pages=2) -> list[str]:
    """Where the whole thing starts. Fresh pools per chain -> ['<addr>:<netId>', ...].
       Costs one GeckoTerminal slot per chain per page, so keep pages small."""
    ids, seen = [], set()
    for net in nets:
        for page in range(1, pages + 1):
            try:
                r = requests.get(f"{GT}/networks/{net}/new_pools",
                                 params={"page": page}, timeout=20).json()
            except Exception:
                break
            if "data" not in r:                  # fixed: a 429 body has no "data" and
                break                            # used to read as a page with no pools
            for pool in r["data"]:
                base = ((pool.get("relationships") or {}).get("base_token") or {})
                gid  = (base.get("data") or {}).get("id")      # 'solana_<addr>'
                if not gid:
                    continue
                addr = gid.split("_", 1)[1]
                tid  = f"{addr}:{FOMO_NET[net]}"
                if tid not in seen:
                    seen.add(tid)
                    ids.append(tid)
    return ids


def normalise(tid: str, m: dict) -> dict:
    """FOMO's field names become the desk's field names, once, here.
       Every file downstream reads these names and only these."""
    addr, net = tid.split(":")
    # fixed: read the names filterTokens returns (section 4), not ones it doesn't
    return {"addr": addr, "net": int(net), "tid": tid, "ticker": m["symbol"],
            "mcap_usd": m["marketCap"], "liquidity_usd": m["liquidity"],
            "volume_h24": m["volume24"], "price_usd": m["priceUSD"],
            "holder_count": m["holders"] or None,
            "change": {"5m": m.get("change5m"), "1h": m.get("change1"),
                       "4h": m.get("change4"), "24h": m.get("change24")},
            "age_minutes": age_minutes(m["createdAt"])}


def shortlist(fomo: Fomo, ids: list[str]) -> list[dict]:
    """Pass one over everything FOMO knows. No network beyond FOMO itself:
       one call per twenty tokens, and not a single request per token."""
    out = []
    for tid, m in fomo.tokens(ids).items():             # 20 per call
        t = normalise(tid, m)
        if t["net"] in GT_NET:
            out.append(t)
    # turnover ranks the queue. It orders work, it does not decide anything
    # fixed: a null volume or market cap used to crash the whole cycle here
    out.sort(key=lambda t: (t["volume_h24"] or 0) / max(t["mcap_usd"] or 0, 1), reverse=True)
    return out


def trade_counts(t: dict) -> dict:
    """buys and sells per window. FOMO does not return them, DexScreener does.
       Called ONLY for tokens that already cleared the free checks. One per token,
       so this runs on tens, never on the whole universe."""
    try:
        pairs = requests.get(f"{DEX}/{t['addr']}", timeout=20).json().get("pairs") or []
    except Exception:
        return {"buys_h1": None, "sells_h1": None, "trades_h24": None}
    if not pairs:
        return {"buys_h1": None, "sells_h1": None, "trades_h24": None}
    # fixed: take the busiest pair. Bonding-curve pairs carry no liquidity figure, so
    # ranking by liquidity picked a dust pool and its handful of trades instead
    x = max(pairs, key=lambda p: sum(((p.get("txns") or {}).get("h24") or {}).values()))["txns"]
    return {"buys_h1": x["h1"]["buys"], "sells_h1": x["h1"]["sells"],
            "buys_h6": x["h6"]["buys"], "sells_h6": x["h6"]["sells"],
            "trades_h24": x["h24"]["buys"] + x["h24"]["sells"],
            "pair_addresses": [p.get("pairAddress") for p in pairs]}   # fixed: for the top wallet


def dossier(t: dict) -> dict:
    """One GT call per token. Fills what the chain actually has, null where it does not."""
    net = GT_NET[t["net"]]
    r = requests.get(f"{GT}/networks/{net}/tokens/{t['addr']}/info", timeout=20)
    r.raise_for_status()                         # fixed: main tells our 429 from a bad token
    a = r.json()["data"]["attributes"]
    pools = set(t.get("pair_addresses") or ())

    d = {**{k: v for k, v in t.items() if k != "pair_addresses"}, "chain": net,
         # GT first, FOMO as the fallback. On Robinhood GT is null and FOMO is all you get.
         "holder_count": (a.get("holders") or {}).get("count") or t["holder_count"],
         "top_10_percent": ((a.get("holders") or {}).get("distribution_percentage")
                            or {}).get("top_10"),
         "developer_holding_percentage": a.get("developer_holding_percentage"),
         "gt_score_details": a.get("gt_score_details"),
         "is_honeypot": a.get("is_honeypot"),
         "mint_authority": a.get("mint_authority"),       # "yes" / "no", null if not indexed
         "freeze_authority": a.get("freeze_authority"),
         "description": a.get("description"),
         "x_handle": clean_handle(a.get("twitter_handle")),
         "top_wallet_percent": None}           # fixed: null off Solana, not missing

    # Solana only: exact top wallet share, off a Solana RPC
    if t["net"] == 1399811149:
        d["top_wallet_percent"] = sol_top_wallet(t["addr"], pools)

    return d


def clean_handle(h):
    """GT returned 'LuffyX100X/status/2102659581109272876' on a Robinhood token.
       Take the first path segment, or treat the account as missing."""
    if not h:
        return None
    # fixed: strip a full x.com URL, refuse X's own paths ('i/status/...' came back as
    # the handle "i"), and accept only the characters an X handle can contain
    h = re.sub(r"^(https?://)?(www\.)?(x|twitter)\.com/", "", h.strip())
    h = h.lstrip("@").split("?")[0].split("/")[0]
    if h.lower() in {"i", "intent", "home", "search", "share", "hashtag", "explore"}:
        return None
    return h if re.fullmatch(r"[A-Za-z0-9_]{1,15}", h) else None


def sol_top_wallet(mint: str, pools=()):
    # fixed: the largest token account is normally the pool's vault or the bonding curve,
    # so skip accounts the token's pools own. The public RPC refuses getTokenLargestAccounts,
    # so point SOLANA_RPC_URL at a keyed one. A failed lookup is null, not a dead dossier.
    rpc = os.environ.get("SOLANA_RPC_URL", "https://api.mainnet-beta.solana.com")
    q = lambda m, p: requests.post(rpc, json={"jsonrpc": "2.0", "id": 1,
                                              "method": m, "params": p},
                                   timeout=20).json()["result"]
    try:
        supply = float(q("getTokenSupply", [mint])["value"]["amount"])
        top = q("getTokenLargestAccounts", [mint])["value"]
        owners = q("getMultipleAccounts", [[a["address"] for a in top],
                                           {"encoding": "jsonParsed"}])["value"]
        held = [float(a["amount"]) for a, o in zip(top, owners)
                if o and o["data"]["parsed"]["info"]["owner"] not in pools]
    except (KeyError, TypeError, ValueError, requests.RequestException):
        return None
    return held[0] / supply if supply and held else None


def social_state(d: dict) -> dict:
    """What SOCIAL hands the judge. The X block is filled by the bot's X plugin."""
    return {"x_account": d["x_account"],                 # collected by SOCIAL, not here
            "token": {"ticker": d["ticker"], "narrative": d.get("description")}}

judge_client.py

The client every seat uses to reach the judge. A few lines, and the only place a bot touches the network for a judgement. JUDGE_URL is the address the tunnel prints.

judge_client.py14 lines
# judge_client.py
import os, requests

URL, SECRET = os.environ["JUDGE_URL"], os.environ["DESK_SECRET"]


def judge(question_set: str, state: dict) -> dict:
    r = requests.post(URL, timeout=30,
                      headers={"Authorization": f"Bearer {SECRET}"},
                      json={"question_set": question_set, "state": state})
    if r.status_code == 422:
        raise RuntimeError(f"malformed question set {question_set}: {r.text}")
    r.raise_for_status()
    return r.json()

Three rules for the collector

collector rules3 lines
NEVER invent a number. A field that came back null stays null and the question sees it.
NEVER let null mean fine. Missing data gets its own option and its own consequence.
NEVER pull a dossier for a token stage 2 killed. That is a wasted rate limit slot.
2 · QUESTIONS

questions.py

judge.py imports SETS from it, and this is the one file you will actually reread later. Every question the desk can ask lives here and nowhere else.

questions.py136 lines
from typesafe_sdk import Choice, Noul, Score

MARKET = {
    "shape": Choice(
        instructions="Classify the shape of this launch from the fields in `state`.",
        criteria={
            "crowd": "Holders growing faster than price. Buys outnumber sells across both "
                     "recent windows. Volume spread rather than spiking once.",
            "one_buyer": "Price climbing faster than holders. Holder growth flat while "
                         "price rises. One wallet walking the price up.",
            "fading": "Recent volume is a small fraction of the daily average, or sells "
                      "outnumber buys in both recent windows.",
            "too_early": "Too few data points to tell any of the above apart yet.",
        }),
    "liquidity_fits_ticket": Noul(
        instructions="A position of `intended_ticket_usd` could be exited into "
                     "`liquidity_usd` without moving the price more than a few percent."),
    "momentum_already_spent": Noul(
        instructions="The move in `change` has already happened, so entering now means "
                     "buying after the information is public.",
        criteria={"true": "The largest change sits in the older windows.",
                  "false": "The recent windows carry the move."}),
}

CONCENTRATION = Noul(
    instructions="Holding this token means being exit liquidity, based on "
                 "`top_10_percent`, `top_wallet_percent` and `holder_count`.",
    criteria={"true": "A few wallets can end the market by selling.",
              "false": "The float is spread widely enough to absorb a large holder."})

DEV_LOADED = Noul(
    instructions="`developer_holding_percentage` is large enough that the creator "
                 "selling would meaningfully move the price.")

CHAIN_SOLANA = {                      # 1399811149
    "authority_risk": Choice(
        instructions="Judge contract control risk from `mint_authority` and "
                     "`freeze_authority`.",
        # fixed: GeckoTerminal reports "yes" / "no", not null / set, and null means
        # not indexed yet, which is not the same as renounced
        criteria={
            "renounced": "Both are \"no\". Supply cannot be inflated, balances cannot "
                         "be frozen.",
            "mint_open": "mint_authority is \"yes\". Supply can be inflated at will.",
            "freeze_open": "freeze_authority is \"yes\". Balances can be frozen at will.",
            "both_open": "Both are \"yes\".",
            "unknown": "Either one is null: the authorities have not been indexed yet.",
        }),
    "concentration_is_exit_risk": CONCENTRATION,
    "dev_still_loaded": DEV_LOADED,
}

CHAIN_BSC = {                         # 56, same set works for Base
    "sell_side_risk": Choice(
        instructions="Judge whether a position here can be sold, from `is_honeypot`, "
                     "`gt_score_details` and the buy and sell counts.",
        criteria={
            "clean": "Not flagged, and sells are going through in the data.",
            "flagged": "Explicitly flagged as a honeypot.",
            "suspicious": "Not flagged, but sells are absent or vanishingly rare while "
                          "buys are plentiful.",
            "unknown": "The honeypot field is missing or unknown and trade counts are "
                       "too thin to stand in for it.",
        }),
    "concentration_is_exit_risk": CONCENTRATION,
    "pool_quality": Score(
        instructions="Rate the pool from `gt_score_details` and `liquidity_usd`.",
        criteria=["Thin and new. One withdrawal ends the market.",
                  "Usable, but a large ticket would move it.",
                  "Deep enough that normal desk size is invisible."]),
}

CHAIN_ROBINHOOD = {                   # 4663, the one with holes in the data
    "data_coverage": Choice(
        instructions="Judge how much of this token is visible, from which fields in "
                     "`state` carry values and which are null or unknown.",
        criteria={
            "indexed": "Holder count and distribution present, honeypot field is a real "
                       "answer.",
            "partial": "Holder count present from the venue, but distribution or the "
                       "honeypot field is missing.",
            "dark": "Neither distribution nor honeypot available. Only price, volume and "
                    "pool age are known.",
        }),
    "sellable_by_evidence": Noul(
        instructions="Sells are going through on this token, judged from the buy and sell "
                     "counts rather than from any honeypot flag.",
        criteria={"true": "Sells appear across recent windows in a normal ratio.",
                  "false": "Buys with almost no sells, or no trades at all."}),
    "concentration_is_exit_risk": CONCENTRATION,
    "dev_still_loaded": DEV_LOADED,
}

SOCIAL = {
    "account_is_the_project": Noul(
        instructions="The account in `x_account` is the token's official account, not a "
                     "fan account, an impersonator, or an unrelated similar name.",
        criteria={"true": "Handle matches the one published on chain and the content is "
                          "about this token.",
                  "false": "Similar name, different subject, or no link back."}),
    "audience_is_real": Noul(
        instructions="Engagement in `x_account` is consistent with its follower count, "
                     "rather than a large follower number with almost no replies or "
                     "reposts on recent posts."),
    "recycled_account": Noul(
        instructions="`x_account` shows signs of being repurposed: far older than the "
                     "token, with a handle or content history belonging to a different "
                     "project."),
    "effort": Score(
        instructions="Rate how much work is visibly behind this project from `x_account`.",
        criteria=["One post, one image, nothing else.",
                  "A handful of posts, all promotional.",
                  "Regular posting with substance beyond price.",
                  "A visible team shipping visible things."]),
}


def PICK(state):
    """Options built from the shortlist at call time. Choice takes up to 255."""
    return {
        "best": Choice(
            instructions="Choose the single token in `candidates` that is the best entry "
                         "right now. Weigh crowd shape, contract risk, concentration and "
                         "the project account together. Prefer a clean unspent setup over "
                         "a larger move that already happened.",
            criteria={c["ticker"]: c["summary"] for c in state["candidates"]}),
        "worth_trading_at_all": Noul(
            instructions="At least one token in `candidates` is worth a position today, "
                         "rather than all of them being mediocre.",
            criteria={"true": "At least one is a clean setup.",
                      "false": "Every candidate has a disqualifying weakness."}),
    }


SETS = {"market": MARKET, "social": SOCIAL, "pick": PICK,
        "solana": CHAIN_SOLANA, "bsc": CHAIN_BSC, "robinhood": CHAIN_ROBINHOOD}

Four rules that make this file work

question rules14 lines
ROUTE THE CHAIN SET IN CODE. mint_authority and freeze_authority carry real values on
  Solana and come back null on every EVM chain, so each chain gets the set written for
  its own evidence. CHAIN_SET does the routing.

data_coverage IS A REAL QUESTION. On Robinhood the newest tokens are the ones
  GeckoTerminal has not indexed yet, so how much you can see is itself an input to size.
  dark does not mean skip, it means cut the ticket.

THE PICK NEEDS worth_trading_at_all NEXT TO IT. A choice is relative and settles which
  of these. The noul is the absolute gate that says whether today is a day at all.

THE SUMMARY IN `candidates` IS TWO LINES, assembled in code from answers Jev already gave.
  My first pick call carried all ten full dossiers and the confidence sagged on every run.
  A fat state costs accuracy.
3 · THE FILTER

thresholds.py

Two files: thresholds.py holds every number and filter.py holds the order they fire in. When you retune the desk, you edit thresholds.py and nothing else.

thresholds.py31 lines
# thresholds.py
HARD = {                        # stage 2, arithmetic, runs before anything costs money
    "min_age_minutes":   15,    # younger than this and the data is noise
    "max_age_hours":     72,    # older than this and it is not a launch any more
    "min_liquidity_usd": 12_000,
    "min_volume_h24":    40_000,
    "min_mcap_usd":      60_000,
    "max_mcap_usd":      8_000_000,
    "min_trades_h24":    150,
    "max_top_wallet":    0.05,  # solana only, exact, from RPC
    "max_top_10":        0.60,  # where distribution exists
    "min_holders":       80,
}

SOFT = {                        # applied to Jev's answers, per token
    "concentration_is_exit_risk": ("max", 0.55),
    "momentum_already_spent":     ("max", 0.60),
    "liquidity_fits_ticket":      ("min", 0.60),
    "account_is_the_project":     ("min", 0.70),
    "recycled_account":           ("max", 0.50),
    "audience_is_real":           ("min", 0.45),
    "effort":                     ("min", 1.0),
    "dev_still_loaded":           ("max", 0.55),
    "sellable_by_evidence":       ("min", 0.60),   # robinhood
}

SHAPE_MIN_CROWD = 0.55          # probabilities["crowd"], not the winning label
PICK_MIN_WORTH  = 0.60
PICK_MIN_CONF   = 0.55
DARK_TICKET_CUT = 0.40          # robinhood, data_coverage == dark
NO_SOCIAL_CUT   = 0.60          # no usable X handle: trade smaller, do not skip

filter.py

filter.py64 lines
from thresholds import HARD, SOFT, SHAPE_MIN_CROWD


def free_kill(t) -> str | None:
    """Pass one. Runs on the whole universe, costs nothing, touches no network.
       Everything it reads came back with the FOMO batch."""
    if not HARD["min_age_minutes"] <= t["age_minutes"] <= HARD["max_age_hours"] * 60:
        # fixed: too young can come back later (main re-feeds it), too old never does
        return "age" if t["age_minutes"] < HARD["min_age_minutes"] else "too_old"
    # fixed: a null used to crash the cycle; now it fails the check
    if (t["liquidity_usd"] or 0) < HARD["min_liquidity_usd"]:   return "liquidity"
    if (t["volume_h24"] or 0)    < HARD["min_volume_h24"]:      return "volume"
    if not HARD["min_mcap_usd"] <= (t["mcap_usd"] or 0) <= HARD["max_mcap_usd"]:
        return "mcap"
    return None


def trade_kill(t) -> str | None:
    """Pass two. One DexScreener call already spent on this token. Tens, not hundreds."""
    if t["trades_h24"] is None:                             return "no_pair"
    if t["trades_h24"] < HARD["min_trades_h24"]:            return "trades"
    if t["sells_h1"] == 0 and (t["buys_h1"] or 0) > 20:     return "no_sells"
    return None


def chain_kill(d) -> str | None:
    """After the dossier, still free. Facts, not judgements."""
    if d.get("top_wallet_percent") is not None and \
       d["top_wallet_percent"] > HARD["max_top_wallet"]:
        return "top_wallet"
    if d.get("top_10_percent") is not None and \
       float(d["top_10_percent"]) / 100 > HARD["max_top_10"]:
        return "top_10"
    if d.get("holder_count") is not None and d["holder_count"] < HARD["min_holders"]:
        return "holders"
    # fixed: GeckoTerminal answers "yes" / "no" (null if not indexed), and "no" used to
    # count as open, which killed every renounced Solana token
    if d["chain"] == "solana" and {d["mint_authority"], d["freeze_authority"]} - {None, "no"}:
        return "authority_open"          # a fact, no model needed
    if d.get("is_honeypot") is True:     # fixed: was bsc only, so base honeypots got through
        return "honeypot"                # also a fact
    return None


def soft_kill(ans) -> str | None:
    """Jev's answers against SOFT. First failure wins."""
    for name, (direction, limit) in SOFT.items():
        a = ans.get(name)
        if a is None:
            continue                     # question not asked for this chain
        v = a.get("noul", a.get("score"))
        if v is None:
            continue
        if direction == "max" and v > limit: return name
        if direction == "min" and v < limit: return name

    shape = ans.get("shape")
    if shape:
        if shape["choice"] in ("fading", "one_buyer"):        return "shape"
        if shape["probabilities"]["crowd"] < SHAPE_MIN_CROWD: return "shape_weak"

    chain = ans.get("sell_side_risk")
    if chain and chain["choice"] in ("flagged", "suspicious"): return "sell_side"
    return None

The order is the whole point

free_kill touches no network and runs on hundreds. trade_kill costs one DexScreener call and runs on tens. chain_kill costs one of the GeckoTerminal slots. soft_kill costs a judgement and runs on a handful. Facts kill before judgements do, which is why authority_open and honeypot are comparisons here and not questions in questions.py: you ask Jev how dangerous the concentration is, and the comparisons handle the plain facts.

Log every rejection with the check that fired. Under ten rejections a day and your filter is misconfigured, not your market.

4 · SOCIAL

The SOCIAL seat

This is a prompt, not a file: paste it into the SOCIAL bot. It took the BOOK seat’s chair, and it exists because Grok Bot can read X through an X plugin, a Cursor Marketplace plugin that signs in with your own X account and is billed per read. Grok reads, Jev judges.

The handle comes off the chain and is already normalised by the collector. Never let this seat go searching for “the project’s Twitter”: it will find a fan account and be confident about it. No handle means no social read, and the token carries that as a gap rather than a pass.

The SOCIAL prompt

social prompt20 lines
You are SOCIAL. You read one X account and you report what is there. You never decide
whether the token is good.

INPUT: x_handle from the dossier. If it is null, return null and stop.

WHAT YOU COLLECT, with your X plugin, into named fields:
{ "handle": ..., "created_at": ..., "followers": ..., "following": ...,
  "post_count": ..., "posts_last_7d": ...,
  "recent": [ {"text": ..., "posted": ..., "replies": ..., "reposts": ...}, ...x10 ],
  "handle_history": [...] | null,
  "bio": ..., "linked_site": ... }

Return that block. The desk sends it to the judge as question_set "social", with
token.ticker and token.narrative, so the model can tell whether the account is about
THIS token.

NEVER summarise the posts. Send them. A summary is your opinion and your opinion is not
part of this pipeline.
NEVER count anything yourself beyond what the plugin returns as a number.
NEVER substitute a similar handle when the exact one returns nothing. Missing is missing.

recycled_account is the highest-value question in the whole file. It holds two timelines side by side, the account’s and the token’s, and hands you one number for the gap.

5 · PICK

pick.py

CHIEF runs it, and it is the only call that ever sees more than one token at a time. A choice takes up to 255 options and the shortlist is never longer than ten, so the whole decision fits in one request.

pick.py69 lines
# pick.py
from thresholds import PICK_MIN_WORTH, PICK_MIN_CONF, DARK_TICKET_CUT, NO_SOCIAL_CUT


def summary(d, ans) -> str:
    """Two lines per candidate, built from answers Jev already gave.
       Never the raw dossier. A fat state costs accuracy."""
    # fixed: age rounded, and momentum added, since the pick prefers unspent setups
    bits = [f"{d['chain']}, {d['age_minutes']:.0f}m old, ${d['mcap_usd']:,.0f} mcap, "
            f"${d['liquidity_usd']:,.0f} liq, {d['holder_count'] or '?'} holders",
            f"crowd {ans['shape']['probabilities']['crowd']:.2f}, "
            f"momentum spent {ans['momentum_already_spent']['noul']:.2f}, "
            f"concentration risk {ans['concentration_is_exit_risk']['noul']:.2f}"]

    if "authority_risk" in ans:
        bits.append(f"authority {ans['authority_risk']['choice']}")
    if "sell_side_risk" in ans:
        bits.append(f"sell side {ans['sell_side_risk']['choice']}")
    if "data_coverage" in ans:
        bits.append(f"data {ans['data_coverage']['choice']}")
    if "account_is_the_project" in ans:
        bits.append(f"official account {ans['account_is_the_project']['noul']:.2f}, "
                    f"effort {ans['effort']['score']:.1f}")
    else:
        bits.append("no usable X account")
    return "; ".join(bits)


def pick(judge, survivors) -> dict | None:
    """survivors: [(dossier, answers), ...]. Returns the order, or None."""
    if not survivors:
        return None

    # fixed: options are keyed by label, so two coins sharing a ticker collapsed into one
    # and the desk could buy a token Jev never saw. Labels are now unique strings.
    tickers = [d["ticker"] or "?" for d, _ in survivors]
    labels = [t if tickers.count(t) == 1 else f"{t} {d['tid']}"
              for t, (d, _) in zip(tickers, survivors)]
    state = {"candidates": [{"ticker": l, "summary": summary(d, a)}
                            for l, (d, a) in zip(labels, survivors)]}
    r = judge("pick", state)
    best, worth = r["answers"]["best"], r["answers"]["worth_trading_at_all"]

    if worth["noul"] < PICK_MIN_WORTH:
        return None                      # every candidate is mediocre. Normal outcome.
    # fixed: a lone survivor now comes through here too. Its confidence proves nothing,
    # so only the worth gate and the size cuts apply to it.
    if len(survivors) > 1 and best["confidence"] < PICK_MIN_CONF:
        return None                      # flat over ten options means no favourite.

    d, ans = next((x for l, x in zip(labels, survivors) if l == best["choice"]), (None, None))
    if d is None:
        return None                      # the schema guarantees the option is in the
                                         # list, so log this one and stand down.

    size_factor = 1.0
    if ans.get("data_coverage", {}).get("choice") == "dark":
        size_factor *= DARK_TICKET_CUT   # less visibility, smaller ticket
    if "account_is_the_project" not in ans:
        size_factor *= NO_SOCIAL_CUT

    return {"model": r["model"],
            "token": {"ticker": d["ticker"], "address": d["addr"],
                      "network_id": d["net"], "chain": d["chain"]},
            "size_factor": round(size_factor, 2),
            "confidence": best["confidence"],
            "runner_up": sorted(best["probabilities"].items(),
                                key=lambda kv: -kv[1])[1:2],
            "why": {k: v for k, v in ans.items()}}

The pick’s three rules

pick rules7 lines
NEVER re-rank the winner. The choice settled it. If you disagree with the pick, you
  disagree with a threshold, and thresholds live in thresholds.py.
NEVER trust a one-option choice. A choice over a single option returns that option with
  high confidence, because it is the only thing there. A lone survivor still goes
  through pick for worth_trading_at_all and the size cuts; only its confidence is ignored.
NEVER skip worth_trading_at_all. A choice settles which of these. The noul is what
  settles whether today is a day at all, so the two always ship together.

No trade is a result. Log it with the reason, send it to Telegram, and stand down until the next run. A desk that trades every cycle is a desk with no filter.

6 · THE HANDOFF

Paste this above every seat that judges

Above the prompts of SCAN, VET, SOCIAL and CHIEF. It is what removes the bot’s own opinion from the loop.

handoff prompt32 lines
YOU DO NOT FORM OPINIONS ABOUT TOKENS. YOU CALL THE JUDGE.

You do not reason about a candidate, you do not weigh it, you do not write a paragraph
about it. You build a state, you call the judge once, you act on the numbers.

THE CALL
  POST $JUDGE_URL
  Authorization: Bearer $DESK_SECRET
  {"question_set": "<market|solana|bsc|robinhood|social|pick>", "state": {...}}

WHAT COMES BACK
  {"model":"jev-1.13.0",
   "answers":{"<name>":{"type":"noul","noul":0.72}, ...},
   "usage":{"input_tokens":1387,"output_tokens":54}}

HOW YOU USE IT
- Compare the numbers against the thresholds in your prompt. That comparison is the
  decision. You do not have a second opinion about it.
- A noul is a probability, not a yes. 0.49 and 0.51 are nearly the same reading and the
  threshold is what makes them different. Never narrate around a number near your line.
- confidence is a separate axis. Low confidence is not a no, it is a do not act alone.
- Log every answer with the model id, exactly as returned.

WHAT YOU NEVER DO
- Never call api.typesafe.ai directly. You do not have that key and will not be given it.
- Never ask for a question set that is not yours. Unknown sets return 422, that is the
  system working.
- Never retry a 422.
- Never substitute your own judgement when the judge is unreachable. A missing answer is
  missing, not neutral, and no token passes on your say so.
- Never put a number in a report that did not come from the judge or from your own
  arithmetic on desk data.

Record one judge call by hand in front of Grok Bot and save it as a skill. Skills are shared across every bot on the account, so you paste this once instead of once per seat. Recording a demonstration covers browser work, so for a terminal call it is simpler to ask the bot to write the skill, and the desk secret belongs in the bot’s Secrets, not in the recording.

Prove the link from a bot’s own terminal, not from your laptop. Passing on your machine and failing on theirs is a firewall problem, and it is how this setup dies quietly.

Test the link from a bot

answers.shape.choice must be one of your options, the probabilities must sum to 1 (to within rounding), and model must be a version string, not an alias. If any of the three is off, stop here.

test the link6 lines
curl -X POST $JUDGE_URL -H "Authorization: Bearer $DESK_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"question_set":"market","state":{"ticker":"TEST","age_minutes":42,
       "holder_count":310,"change":{"5m":0.04,"1h":0.22,"24h":0.61},
       "buys_h1":540,"sells_h1":120,"liquidity_usd":48000,"mcap_usd":310000,
       "volume_h24":610000,"intended_ticket_usd":900}}'

What CHIEF drops into the desk channel

When the pick comes back. This is the whole handoff, and the bots work it top to bottom.

order.json14 lines
{
  "order_id": "2026-09-23T10:15:00Z",
  "token": {"ticker": "...", "address": "...", "network_id": 1399811149,
            "chain": "solana"},
  "size_factor": 1.0,
  "confidence": 0.78,
  "why": {
    "shape": "crowd", "crowd_p": 0.91,
    "concentration_is_exit_risk": 0.14,
    "authority_risk": "renounced",
    "account_is_the_project": 0.97, "recycled_account": 0.06, "effort": 2.4
  },
  "model": "jev-1.13.0"
}

The order, worked in sequence

order sequence17 lines
THE ORDER, WORKED IN THIS SEQUENCE, NOBODY SKIPS AHEAD

1. SIZE   reads token + size_factor. Computes the ticket by the four steps in its
          prompt. Returns dollars, or 0 with a reason. A 0 ends the order here.
2. FILLS  reads the ticket. Checks the fee floor BEFORE sending. Sends one market
          order through FOMO. Reports filled, fill_price, slippage_bps, partial.
3. RISK   starts its timer the moment a fill is reported, not when the order was
          created. Polls every 5 minutes. Fires on its own authority.
4. CHIEF  logs the order id, the model id and every answer that produced it, then
          sends the line to Telegram.

NOBODY RE-READS `why`. It is there for the log and for you, not as an input. SIZE does
not size up because crowd_p was 0.91, and RISK does not hold longer because confidence
was high. The judgement is finished. What is left is arithmetic.

NO ORDER IS ALSO AN ORDER. When pick returns nothing, CHIEF sends one line saying so
with the reason, and the desk stands down until the next run.
7 · SIZE, FILLS, RISK

The three seats that act

Three prompts, one per seat, nothing to install, and no judge call or key between the three of them. The guide says they are unchanged from the original except that the ticker now arrives from the pick with a size_factor; in fact SIZE also gains the 2% liquidity cap, FILLS hard-codes the fee figures, and RISK gains the release step.

size fills risk30 lines
SIZE   1. ticket = kelly(edge) * bank, clamped at 6% of the book. Free cash only,
          never the locked bag.
       2. ticket *= size_factor from the pick. dark data cuts to 0.40, a missing X
          account cuts to 0.60, both stack.
       3. ticket = min(ticket, liquidity_usd * 0.02). If you are more than 2% of the
          pool you are the exit, not a participant.
       4. if ticket < fee floor viable size -> return 0 and log it. Never size below
          what pays its own fees.
       Not exitable inside the slippage budget means the size is wrong, whatever the
       pick confidence said.

FILLS  1. effective_fee = max(0.0045 * ticket, 0.95) / ticket
       2. over the max -> do not send, return FEE_FLOOR, let SIZE raise or drop it.
          A $20 entry against a $0.95 floor is 4.75% each way, 9.5% round trip, and
          no meme edge covers that.
       3. one market order through FOMO, no ladder, no waiting for a better price.
       4. slippage over max -> complete and flag loudly, never absorb it silently.
       5. never sell into a distributing whale. Hold and report.
       Fills go through FOMO and nowhere else. One venue, one path,
       so a bad fill is always traceable to one place.

RISK   One rule, no conversation, final authority, nobody overrules it.
         avg_6h = volume.h24 / 4
         ratio  = volume.h6 / avg_6h
         ratio < 0.20 -> CLOSE, fully, inside 60 seconds.
       Poll every 5 minutes. No answer, retry twice, then CLOSE anyway. A position you
       cannot measure is a position you do not hold.
       The moment the close is filled, POST $JUDGE_URL/release with the desk secret.
       Until you do, the desk does not scan, so a close you forgot to report is a desk
       that stopped working.

The exit is a single comparison on two numbers the desk already holds, so it runs the instant the data lands and runs the same way every time. That is what you want in the seat that closes.

Jev picks what to hold. Grok Bot decides how much and how long. The exit rule answers to neither of them.

8 · THE BOOK

book.py

It creates desk.db on first run, with nothing to set up. There are two things the desk has to remember between cycles, and skipping either one costs real money.

One position at a time. The cycle runs every fifteen minutes. Without a guard it picks a second token while the first is still open, then a third, and by evening RISK is managing a portfolio nobody sized. While a position is open, the scan doesn’t run at all.

A rejected token stays rejected for a while. A token that failed at 10:00 is still the same token at 10:15, and without a bench it burns a GeckoTerminal slot and three judge calls every cycle until it ages out. The bench isn’t one length either: a honeypot flag will never become false, but “too early to tell” becomes tellable within the hour.

book.py

book.py71 lines
# book.py
import os, sqlite3, time

# fixed: one file for every process that imports this, wherever it starts (main.py,
# and judge.py, which frees the book for RISK)
DB = sqlite3.connect(os.environ.get("DESK_DB", os.path.join(os.path.dirname(
                     os.path.abspath(__file__)), "desk.db")), check_same_thread=False)
DB.executescript("""
CREATE TABLE IF NOT EXISTS position(
  id INTEGER PRIMARY KEY CHECK (id = 1),
  ticker TEXT, addr TEXT, net INT, opened_at REAL);
CREATE TABLE IF NOT EXISTS bench(
  tid TEXT PRIMARY KEY, reason TEXT, until REAL);
""")

# how long a rejection stands, by what fired it
BENCH_MINUTES = {
    # facts that will not change while this token exists
    "honeypot": 100_000, "authority_open": 100_000,
    "top_wallet": 100_000, "sell_side": 100_000,
    "too_old": 100_000,                  # fixed: past max_age_hours, so never coming back
    # slow to change
    "recycled_account": 360, "account_is_the_project": 360,
    # can change as the float moves
    "top_10": 90, "holders": 90, "dev_still_loaded": 90,
    "concentration_is_exit_risk": 90,
    # can change inside the hour, keep it short or you miss the token maturing
    "shape": 25, "shape_weak": 25, "momentum_already_spent": 25,
    "liquidity_fits_ticket": 25, "liquidity": 25, "volume": 25,
    "trades": 25, "mcap": 25, "age": 20,
}
DEFAULT_BENCH = 45


def held():
    r = DB.execute("SELECT ticker, opened_at FROM position WHERE id=1").fetchone()
    return {"ticker": r[0], "minutes": (time.time() - r[1]) / 60} if r else None


def take(order):
    t = order["token"]
    # fixed: INSERT, not INSERT OR REPLACE, which let a second shift overwrite a held position
    DB.execute("INSERT INTO position VALUES (1,?,?,?,?)",
               (t["ticker"], t["address"], t["network_id"], time.time()))
    DB.commit()


def release():
    """RISK calls this the moment a close is filled. Nothing else calls it."""
    DB.execute("DELETE FROM position")
    DB.commit()


def benched(tid: str) -> bool:
    r = DB.execute("SELECT until FROM bench WHERE tid=?", (tid,)).fetchone()
    return bool(r and r[0] > time.time())


def sit(tid: str, reason: str):
    mins = BENCH_MINUTES.get(reason, DEFAULT_BENCH)
    DB.execute("INSERT OR REPLACE INTO bench VALUES (?,?,?)",
               (tid, reason, time.time() + mins * 60))
    DB.commit()


def due(reason: str) -> list[str]:
    """fixed: tokens whose bench for `reason` ran out in the last hour. new_pools has moved
       on by then, so without this a token that was five minutes too young never comes back."""
    now = time.time()
    return [r[0] for r in DB.execute("SELECT tid FROM bench WHERE reason=? AND until<=? "
                                     "AND until>?", (reason, now, now - 3600))]

Three rules for the book

book rules10 lines
RISK CALLS release() AND NOBODY ELSE DOES. Not CHIEF, not SIZE, not you from a console
  because the chart looks fine. The seat that closed the position is the seat that frees
  the book, and that is the only way the two can never disagree.

BENCH ON THE FAILED CHECK, NOT ON THE TOKEN. The reason is what sets the length. Bench
  everything for the same hour and you will keep re-paying for honeypots while missing
  the token that was simply five minutes too young.

A HELD POSITION MEANS NO SCAN AT ALL. Not a scan that ends in no trade, no scan. You
  save the rate limit and the judge calls for the cycle where you can actually act.
9 · THE SHIFT

main.py

Run this one. It is the process that never stops: it owns the cycle, calls everything above in order, and hands finished orders to the seats. Start it with shadow=True and leave it that way for a week.

main.py139 lines
# main.py
import time, logging, sqlite3
from fomo_api import Fomo
from collect import universe, shortlist, trade_counts, dossier, social_state
from filter import free_kill, trade_kill, chain_kill, soft_kill
from pick import pick
import book

CHAIN_SET     = {1399811149: "solana", 56: "bsc", 8453: "bsc", 4663: "robinhood"}
CYCLE_SECONDS = 900
GT_PER_MINUTE = 10          # free tier
GT_PAGES      = 2           # fixed: the budget now follows from these, instead of
GT_UNIVERSE   = 3 * GT_PAGES                       # numbers that were never read
GT_DOSSIER    = GT_PER_MINUTE - GT_UNIVERSE - 1    # 3, one spare. 6 at GT_PAGES = 1
DEX_BUDGET    = 25          # DexScreener calls per cycle, pass two only
log = logging.getLogger("desk")


def run_once(fomo, judge, desk, bank, shadow=True):
    if (h := book.held()):                       # RISK owns the desk right now
        log.info("holding %s for %.0f min, no scan this cycle",
                 h["ticker"], h["minutes"])
        return None, {"held": h["ticker"], "minutes": round(h["minutes"])}

    stats = {"seen": 0, "benched": 0, "free": {}, "trade": {},
             "chain": {}, "soft": {}}
    survivors = []
    gt_slots, dex_slots = GT_DOSSIER, DEX_BUDGET

    def ask(question_set, state):                # fixed: log the model id of every call
        r = judge(question_set, state)
        log.info("judge %s %s model=%s", question_set, state.get("tid", ""), r["model"])
        return r["answers"]

    ids = universe(pages=GT_PAGES)               # fresh pools, 3 chains, GT_UNIVERSE slots
    # fixed: new_pools covers only the last few minutes, so nearly every token is too young
    # on first sight and gone from the list by the time it qualifies. Look at those again.
    ids += [i for i in book.due("age") if i not in ids]
    for t in shortlist(fomo, ids):               # pass one: free, no per-token requests
        stats["seen"] += 1
        if book.benched(t["tid"]):               # already judged, still serving its time
            stats["benched"] += 1
            continue
        if (k := free_kill(t)):
            book.sit(t["tid"], k)
            stats["free"][k] = stats["free"].get(k, 0) + 1
            continue

        if dex_slots <= 0 or gt_slots <= 0:
            break                                # out of budget, not out of ideas

        t |= trade_counts(t)                     # pass two: one DexScreener call
        dex_slots -= 1
        if (k := trade_kill(t)):
            book.sit(t["tid"], k)
            stats["trade"][k] = stats["trade"].get(k, 0) + 1
            continue

        try:
            d = dossier(t)                       # pass three: one GeckoTerminal slot
            gt_slots -= 1
        except Exception as e:
            log.warning("dossier failed %s: %s", t["ticker"], e)
            gt_slots -= 1                        # a failed call still cost you the slot
            if getattr(getattr(e, "response", None), "status_code", None) == 429:
                break                            # fixed: our rate limit, not the token's fault
            book.sit(t["tid"], "dossier_failed")
            continue                             # missing is missing, not a pass

        if (k := chain_kill(d)):
            book.sit(t["tid"], k)                # facts bench longest
            stats["chain"][k] = stats["chain"].get(k, 0) + 1
            continue

        # fixed: the most SIZE could ever allow is also capped at 2% of liquidity
        d["intended_ticket_usd"] = min(bank * 0.06, (d["liquidity_usd"] or 0) * 0.02)

        ans = {}
        try:
            ans |= ask("market", d)                                 # pass four
            ans |= ask(CHAIN_SET[d["net"]], d)
            # fixed: read X after those two, so its posts aren't sent (and billed) as their state
            d["x_account"] = desk.read_x(d["x_handle"]) if d["x_handle"] else None
            if d["x_account"]:
                ans |= ask("social", social_state(d))
        except Exception as e:
            log.warning("judge failed %s: %s", d["ticker"], e)
            stats["judge"] = repr(e)             # fixed: stand down, as the failure table says,
            return None, stats                   # instead of spending the cycle on the next token

        if (k := soft_kill(ans)):
            book.sit(t["tid"], k)
            stats["soft"][k] = stats["soft"].get(k, 0) + 1
            continue

        survivors.append((d, ans))

    log.info("cycle: %(seen)s seen, %(benched)s benched, free %(free)s, "
             "trade %(trade)s, chain %(chain)s, soft %(soft)s", stats)

    if not survivors:
        return None, stats
    # fixed: a lone survivor skipped pick, so it was never asked worth_trading_at_all and
    # went out at full size whatever its data or X account looked like
    order = pick(judge, survivors)               # pass five

    if order is None:
        return None, stats
    order["order_id"] = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())   # fixed: CHIEF logs it
    if shadow:
        desk.log_shadow(order, stats)            # written, never sent
        return None, stats

    try:
        book.take(order)      # the desk is now held. No scan until RISK calls release().
    except sqlite3.IntegrityError:        # fixed: a second shift can't overwrite a held position
        log.warning("desk already held, %s not sent", order["token"]["ticker"])
        return None, stats
    return order, stats


def main(fomo, judge, desk, shadow=True):
    """desk is your Grok Bot side. It has to provide five things:
         bank()                 -> float, free cash right now
         read_x(handle)         -> the X block SOCIAL collects with its plugin, or None
         log_shadow(order, st)  -> append a row for the shadow week
         report(order, stats)   -> one line to Telegram, trade or no trade
         send_to_seats(order)   -> hand it to SIZE, then FILLS, then RISK, in that order
    """
    while True:
        try:
            fomo.token()                         # Privy bearer lives ~60 min, refresh it
            order, stats = run_once(fomo, judge, desk, desk.bank(), shadow)
            desk.report(order, stats)            # every cycle, trade or not
            if order:
                desk.send_to_seats(order)
        except Exception as e:
            log.exception("cycle blew up: %s", e)
        time.sleep(CYCLE_SECONDS)

The budget is the design

GeckoTerminal gives you about ten calls a minute. Six go to listing fresh pools across three chains, so three dossiers per cycle is what is left in that minute. That is why the free pass has to kill hard: by the time a token reaches a dossier it has already survived everything a comparison can do. Want more dossiers? Set GT_PAGES to 1 and you get six.

One thing the checks add: the limit is per minute and the cycle is fifteen, so spacing the calls out would allow many more dossiers. Doing everything in one burst is a choice, not a law.

Failure handling, because this runs unattended

failure handling13 lines
429 from GeckoTerminal  -> you exceeded 10/min. Back off a full minute, do not retry in
                           place. If it keeps happening, set GT_PAGES = 1: three
                           slots freed, three more dossiers per cycle.
429 from DexScreener    -> lower DEX_BUDGET. Pass two is the only place it is called and
                           it is capped per cycle for exactly this reason.
429 or 529 from Jev     -> the SDK retries with backoff on its own. Leave it alone.
422 from Jev            -> your question is malformed. Never retry. Log the field and
                           stop the cycle, because every token will hit the same wall.
dossier throws          -> skip that token. Not a pass, not a retry loop.
judge unreachable       -> skip the cycle entirely. A desk with no judge does not fall
                           back to guessing, it stands down.
FOMO token expired      -> refresh the Privy bearer out of Chrome and continue. It dies
                           roughly hourly and that is normal.

A week in shadow first

Run it with shadow=True for a week. It does everything except send the order and take the book, and desk.log_shadow writes one row per would-be trade: the ticker, every answer with the model id, the rejection counters, and what your old logic did with the same candidates. Read only the rows where the two disagree. That is twenty minutes each evening, and it is where your thresholds come from. Then flip the flag.

One more thing that bites unattended runs: the FOMO bearer lives about an hour. main calls fomo.token() at the top of every cycle so the client can refresh it out of Chrome before the batch goes out, rather than discovering it expired halfway through a funnel.

The bill

State is charged once per call; output is free

cost per call1 lines
cost_per_call = (state_tokens + question_tokens) / 1_000_000 * 0.042

What a day costs

The rate limit upstream caps this for you. Three dossiers a cycle means at most three tokens reach the judge, and each takes three calls (market, chain, social) plus one pick over the survivors: ten calls of roughly 1,400 input tokens each.

the daily bill3 lines
10 calls x 1,400 tokens = 14,000 tokens
14,000 / 1,000,000 x $0.042 = $0.00059 per cycle
96 cycles a day (every 15 min) = about 5.6 cents a day

Under six cents a day for every judgement the desk makes. The DexScreener and GeckoTerminal calls that feed it are free. The guide says the only other cost is the SuperGrok plan the bots already run on, but it isn’t: the SOCIAL seat’s X reads are billed per call in X API credits, FOMO takes a fee on every fill, and Grok Bot usage beyond its weekly allowance is billed on demand.

Split any set into one call per question and you pay several times as much for identical answers, because the state is charged again on every call. That is the whole reason questions.py groups the questions into sets instead of asking them one at a time.

After the files

What the checks changed in the code

Each change is marked “fixed” in the code above. With them, 41 of 48 end-to-end scenarios pass against the real SDK and a mock TypeSafe server, against 8 for the code as published; the other seven are deliberate (bench lengths left as the author set them) or informational. No live Jev key was used, so real answers and token counts weren’t measured.

WhereWhat was wrong, and the fix
Solana authorityGeckoTerminal answers "yes" or "no", and the published check read "no" as open, so every renounced Solana token was benched for 69 days. chain_kill now kills only on "yes", and the question has an “unknown” option for tokens not yet indexed.
Solana top walletIt reads a keyed RPC from SOLANA_RPC_URL, skips accounts owned by the token’s own pools, and returns null instead of killing the dossier when the RPC refuses.
GeckoTerminal rate limitsA rate-limited page read as “no new pools”, and a rate-limited dossier benched a healthy token. universe() now stops paging, and main.py stops dossiers for the cycle without benching anyone.
Young tokensThe two newest-pool pages cover a few minutes, so nearly every token was too young on first sight and gone from the list before it qualified. main.py now looks again at tokens whose “too young” bench has run out, and over-age tokens get their own permanent reason.
DexScreener pairThe busiest pair is used instead of the deepest: bonding-curve pairs carry no liquidity figure, so the old choice read a dust pool’s handful of trades.
X handles“i/status/…” came back as the handle “i”, and full x.com URLs were dropped. clean_handle now strips URLs, refuses X’s own paths and accepts only characters a handle can contain.
FOMO field namesnormalise() reads the names the guide documents for filterTokens. The FOMO client itself is still yours to write.
Nulls and BaseA null liquidity, volume or market cap crashed the whole cycle; now it fails the check. The honeypot fact check covered BSC only, so Base honeypots reached the judge; it now covers both.
The pickTwo coins sharing a ticker collapsed into one option, and the desk could buy the one Jev never saw; option labels are now unique. A lone survivor skipped the pick, so it was never asked worth_trading_at_all and went out at full size; it now goes through the pick with only its confidence ignored.
Judge errorsEvery Jev error reached the bots as a bare 500. The judge now answers 422 for a bad question, 502 for other Jev errors and 504 when Jev can’t be reached, and main.py stands down at the first failure, as the guide’s own failure table says.
Freeing the bookRISK runs in the cloud and can’t call book.release() on your machine, so after its first close the desk would stay held forever. judge.py has a /release endpoint behind the desk secret, and the RISK prompt calls it. book.py keeps one fixed file, and a second shift can no longer overwrite a held position.
The judge’s addressuvicorn bound to 0.0.0.0 opened the judge to the whole network; 127.0.0.1 is all the tunnel needs. The bare tunnel URL now works as JUDGE_URL.
Smaller fixesModel ids are logged for every judge call and orders carry an order_id. X posts no longer ride along in the market and chain states. Jev is asked about the ticket SIZE could actually send. The pick summary rounds the age and adds momentum. top_wallet_percent is null rather than missing off Solana. The SDK install asks for 0.7 or later.
The promptsFILLS: “4.75% round trip” is one leg. SOCIAL returns its block for the desk to send rather than also sending it. RISK frees the book through /release.

What still needs work before it trades

Design problems the checks found that a small fix can’t settle. Each has a suggested approach; none is applied above.

ProblemWhy it matters, and a way through
FOMO’s termsNo code change fixes this. A documented data source, such as Codex’s filterTokens API, removes the scraping; there is no sanctioned way for a bot to trade on FOMO.
RISK can’t exit a young coinUnder six hours old, a pair’s six-hour and 24-hour volumes are the same, so the ratio is pinned at 4.0 and the exit never fires, though the desk buys from 15 minutes old. Comparing the last hour with the five before it works: volume.h1 / ((volume.h6 − volume.h1) / 5) < 0.20.
kelly(edge)Underspecified. Neither this guide nor the original says what the edge is, no win probability or payoff reaches SIZE, and the pick’s confidence isn’t one. Estimate win rate and payoff from the shadow week, or size a flat fraction under the 6% cap.
top_10 counts poolsGeckoTerminal’s distribution includes pool vaults, bonding curves and exchange wallets, so the 60% cap kills most fresh tokens. Exclude those accounts, or relax the cap while a coin is on its curve.
The 2% liquidity capliquidity_usd counts both sides of the pool, so 2% of it is about 4% of the side you trade against. 1% matches the intent.
FILLS’s fee modelUse FOMO’s schedule for the chain instead of one formula, and put a number on “the max”, for example a 2% round trip.
SlippageFOMO’s slippage setting is a hard limit, but its Auto mode loosens it per token. Set Custom to the desk’s maximum, so an order that would slip further doesn’t fill, and have FILLS report that rather than complete it.
Numbers in questionsliquidity_fits_ticket and momentum_already_spent ask Jev to compare numbers, which TypeSafe calls a weak spot. Pass precomputed ratios as fields, as the guide’s own “arithmetic stays in code” rule says.
One call per tokenThe market and chain sets send the same dossier, so it is billed twice per token. Merging them follows the guide’s own rule and halves that part of the bill.
The project’s X accountThe handle comes from GeckoTerminal’s unvetted metadata, not the chain, so “matches the one published on chain” checks the handle against itself. Ask whether the bio or a pinned post names the contract instead, and pass gt_verified.
X fieldsX’s API has no handle history, so handle_history is always null, and posts_last_7d needs a separate billed call.
New pools’ base tokenuniverse() reads only the base token, and some new pools list the new coin as the quote, so those launches are missed. Read both and skip known quote assets.
Unitstop_10_percent is a percentage from 0 to 100 and top_wallet_percent a fraction from 0 to 1, side by side in one question. Normalise them.
The order messageThe JSON CHIEF posts doesn’t match what pick.py returns: “why” holds Jev’s raw answers, not the flattened figures shown.
Seats and approvalsThe bots need their own FOMO and X sign-ins in their cloud browser, and xAI advises keeping purchases behind an approval, which an unattended FILLS seat can’t wait for.
pool_qualityThe BSC set’s pool_quality score is asked on every call and read by nothing: not the filter, the thresholds or the pick. Gate on it or drop it.
The pick’s confidence barWith three dossiers a cycle the pick sees two or three options, not ten, and a choice’s confidence is scaled to the option count: 0.55 needs a 78% favourite of two, or 70% of three.
recycled_accountIt asks whether the account is far older than the token, but the state carries no token age, and TypeSafe says Jev reads dates as text. Pass both ages as numbers.
ChainsSection 5’s chain table includes Ethereum, collect.py’s doesn’t, and neither covers Monad, which FOMO also trades. As coded, the desk watches Solana, BSC and Robinhood Chain.
The video

The overnight $100 run

The companion video shows the desk’s dashboard over one day on a fresh $100, from 06:30 UTC on 1 October 2026 to 06:30 UTC on 2 October. Jev settled on one coin, COCKROACH (Solana mint H4KUxsEgCp2yDpFvyDuGM5k6XevSKKrrKUxZ3Zr6nyJc), and the bots traded it nine times, one position at a time: eight wins and a loss, ending at $2,197.67. Holding the coin over the same 24 hours returned 3.84 times.

The biggest trade bought a dip at a $234.7K market cap around 15:10 UTC and sold at $1.80M at 15:55, after a single five-minute candle ran the coin from $234K to $1.44M. Watch about nine seconds in: the bank jumps from about $148 to $835 in under a second of video.

The overnight run as the desk’s dashboard shows it, 1 to 2 October 2026, 24 hours in 25 seconds. The clip plays silent.

The nine trades, as the dashboard shows them

Read frame by frame from the dashboard. Prices are market caps; the ticket is the share of the bank each trade put in. The post’s own list of balances leaves out trade 8.

Trade (UTC)TicketBought → soldResultBank after
1 · 08:05–09:20Ticket$69 · 68.9%Bought → sold$246.0K → $297.5KResult+$14Bank after$114.61
2 · 11:10–11:40Ticket$86 · 75.0%Bought → sold$325.1K → $444.4KResult+$32Bank after$148.34
3 · 15:10–15:55Ticket$100 · 68.8%Bought → sold$234.7K → $1.80MResult+$667Bank after$835.16
4 · 18:50–19:10Ticket$669 · 80.5%Bought → sold$1.48M → $2.24MResult+$339Bank after$1,180.22
5 · 21:00–21:20Ticket$632 · 53.9%Bought → sold$1.62M → $1.54MResult−$30Bank after$1,139.85
6 · 23:15–23:50Ticket$923 · 81.3%Bought → sold$1.18M → $1.68MResult+$388Bank after$1,529.66
7 · 01:40–02:00Ticket$1,138 · 74.5%Bought → sold$1.56M → $1.97MResult+$301Bank after$1,834.35
8 · 03:40–04:05Ticket$1,241 · 68.0%Bought → sold$1.26M → $1.43MResult+$169Bank after$1,995.07
9 · 05:45–06:10Ticket$1,356 · 68.2%Bought → sold$971.6K → $1.12MResult+$207Bank after$2,197.67

What the checks found in the video

Checked frame by frame and against GeckoTerminal’s price history for the coin’s pools.

ClaimWhat the checks found
The coin and its chartReal. The dip, the one-candle run to $1.44M, the $1.80M sale and the 3.84× hold all match GeckoTerminal’s five-minute candles for the coin’s main pool. “The top was $2.03M” was only that spike’s top: the coin reached $2.51M at 19:10, and trade 4 sold at $2.24M.
Recording or replayA replay. All 18 buys and sells sit exactly on GeckoTerminal five-minute candle closes, which market orders with slippage wouldn’t. End-of-run figures, the 284% hold and $5.2M of volume, are on screen from the first frame, and its embedded chart screenshots are stamped after the run ended.
The sizingEvery ticket is 54–81% of the bank; the guide’s SIZE prompt caps a ticket at 6%. Replayed at 6%, the same nine trades turn $100 into about $158, or about $138 after the FILLS prompt’s own fee floor, and SIZE would refuse $6 tickets that can’t pay their fees anyway.
The accountUnverifiable. No wallet, transaction or account history is shown. 4% of the bank also sat in the coin all day, outside the nine trades.
The rest of the post“Nine trades” is right, but its list of balances shows eight: it leaves out trade 8. The 21:00 loss was $30 on the trade and $40 on the bank, counting the coin held all day. “87 coins” matches no counter on the dashboard.
The guide’s own codeIt would never have bought this coin: GeckoTerminal reports its authorities as "no", which the published chain check reads as open. The coin also has no project X account; the handle it lists belongs to an individual.
!
The author’s setup, not your strategy

Every threshold here is the author’s, tuned against the launches they trade, over one week, on their bank and their risk tolerance. Run it in shadow mode first: log every answer next to what you would have decided yourself, and move the number where you disagree. Copy the shape, not the constants. Memecoins are among the riskiest things you can trade, and nothing here is financial advice.

i
The rule worth keeping

Code fetches, the model judges, code decides. A seat that computes doesn’t get a judge call, and a seat that judges doesn’t get a calculator.

The guide’s closing clip.