YouMind
Sign in

Grok Bot from zero in one night: full setup + a copy-paste rug filter

@0xboan
ENGLISHOct 17, 2026
126K
139
19
20
439

TL;DR

This guide details building a Grok Bot trading desk from scratch, focusing on a rigorous 14-step rug filter to eliminate scam tokens before execution. It emphasizes a 'code kills, Grok reads, you sign' workflow with paper trading safeguards.

most people meet Grok Bot through someone else's pnl screenshot, open it, point it at a chart, and lose the first $50 to a token that was dead before they bought it. the bot wasn't the problem. nobody told it what dead looks like.

this is the setup I'd build again tonight if I had to start over. one bot, one folder, ten small files. it doesn't hunt for 100x. its main job is killing tokens, and the few that survive still need Grok to read X before a single dollar moves.

steps 01 to 07 are the first evening: the bot, the key, the limits, the exits. 08 to 12 are the rug filter, 14 checks, every one of them written down as code. 13 and 14 put it on autopilot, on paper first.

if you read my eight-agent desk post, this is the floor that desk stands on.

https://x.com/0xboan/status/2092226208234647626

how to use this: open your Grok Bot, paste this whole article, and say "build this desk in /workspace/desk, paper mode, ask me before anything touches a wallet." it writes every file itself, on its own computer.

the split you hold onto: code kills, Grok reads X, you sign. nobody on the desk does someone else's job.

01. get four things before you start

four things. each one says what it is, what you do tonight, and why the desk needs it.

1. Grok Bot

what it is. the app where you hire named bots. each one works on a cloud computer with a browser, files and a terminal, and keeps working with your laptop shut.

what you do. download it at x.ai/bot, sign in, choose Create your own.

why. the whole desk lives on that computer. files, routines, alerts, all in one place you can open from your phone.

2. an xAI API key

what it is. paid access to Grok from code, with X search built in.

what you do. console.x.ai, API Keys, create one. don't paste it anywhere yet, step 03 says where it goes.

why. a routine can't sit and think about forty tokens in a chat. a script that makes one call per token can, and it logs every answer in the same format.

3. Rugcheck and DexScreener

what it is. two free public APIs. Rugcheck reads the contract: authorities, LP, holders, insiders, the dev wallet. DexScreener reads the pool: depth, age, buys against sells.

what you do. nothing. no signup, no key for reads.

why. facts come before opinions. an open mint authority isn't a judgment call, it's a fact, and facts kill first.

4. a burner wallet, but not tonight

what it is. a fresh Solana wallet that has never held anything else.

what you do. nothing yet. week one runs on paper.

why. the desk never holds a key. when you go live, it tells you what to buy and you sign it yourself.

02. hire one bot, not eight

my desk runs eight bots now. it didn't start that way, and yours shouldn't either. eight bots on day one is eight things to debug at once. one bot with one job shows you what the other seven will need later.

choose Create your own and give it this, word for word:

text
1Name: RUG DESK
2Job: Solana memecoin desk, paper first
3Description: Build and run the desk in /workspace/desk from the article I paste.
4Every token goes through check.py, no exceptions, no shortcuts.
5You never buy, sell, sign or move funds. You never edit config.py unless I ask
6in this chat. You never lower a filter number to let a token through.
7Report every kill with its reason, not your opinion of the token.
8When a source is down, say which one and skip. Never guess a missing number.

the last two lines matter more than the first five. a bot that guesses a missing number will eventually guess one that costs you.

boan - inline image

03. keep the key out of the chat

everything you type to a bot lands in a transcript. so the key goes into a file, typed by you, on the bot's computer.

open the bot's computer, click Take over, open the terminal and run this. it asks for the key without echoing it, so it never shows up in history either:

bash
1mkdir -p /workspace/desk && cd /workspace/desk
2read -rs -p "xai key: " K && printf 'XAI_API_KEY=%s\n' "$K" > .env && unset K
3chmod 600 .env
4pip install requests

then prove the key works before anything else. one call, so you never confuse a bad key with a bad prompt later:

bash
1curl -s https://api.x.ai/v1/responses \
2 -H "Authorization: Bearer $(cut -d= -f2 .env)" \
3 -H "Content-Type: application/json" \
4 -d '{"model":"grok-4.7","input":"reply with OK"}'
5
6-> ... "type":"output_text","text":"OK" ...

click I'm done and hand the computer back. from here on the bot can run the desk, and it still never sees the key in the chat.

boan - inline image

04. see the whole funnel before you build any of it

eight passes. the free ones run first. only what survives the free passes costs a Grok call, and only what Grok calls BUY costs money. this week, not even that.

text
10. LEAD a CA from anywhere: a call, a post, scan.py -> one mint
21. LIMITS book.py: daily loss, open slots, cooldown -> free, no network at all
32. FACTS rugcheck.py: 10 hard kills -> open mint, unlocked LP, fat top 10 die here
43. POOL market.py: depth, age, sells -> thin pools and honeypots die here
54. JUDGE grok.py + X search, one call -> BUY or SKIP, one reason, one risk
65. FILL paper: book.py at the live price -> live: you sign it yourself
76. WATCH watch.py every 10 minutes -> stop, ladder, trail, time stop
87. REPORT report.py at 21:30 -> what died, what paid, what to question

an open mint authority isn't a judgment call. it's a fact, and facts are free. Grok only ever sees tokens that already passed every check a script can do.

boan - inline image

05. put every number in one file

save as config.py. every number on the desk lives here, so when you retune, you edit one file and nothing else. these are mine, tuned for a $25 ticket. they are not a recommendation, they are a starting point you will move.

python
1# config.py
2# every number on the desk lives here. when you retune, you edit this file and nothing else.
3
4MODEL = "grok-4.7" # check console.x.ai -> Models if the API rejects it
5PAPER = True # week one: fake fills at real prices. flip only after you read the log
6
7TICKET_USD = 25 # flat. never sized off the last win
8MAX_OPEN = 4 # 4 x $25 = $100 in the market, max
9MAX_ENTRIES_PER_DAY = 6
10DAILY_LOSS_HALT_USD = 50 # realized loss today hits this -> no new entries until tomorrow
11COOLDOWN_HOURS = 24 # no re-entry into a token you just left
12
13FACTS = { # the rug filter. code only, no model
14 "max_risk_score": 40, # rugcheck score_normalised, 0-100, higher = riskier
15 "min_lp_locked_pct": 90, # biggest pool: LP locked or burned
16 "max_top10_pct": 30, # top 10 holders, pools and lockers excluded
17 "max_dev_pct": 5, # supply still sitting in the creator wallet
18 "max_insiders": 0, # insider-flagged holders + insider graph hits
19 "min_liquidity_usd": 30000,
20 "max_ticket_share": 0.01, # your ticket is never more than 1% of the pool
21 "min_age_min": 30, # younger than this, every report is still guessing
22 "max_age_hours": 72,
23}
24
25MIN_CONFIDENCE = 0.7 # Grok says BUY below this -> treated as SKIP
26
27EXIT = {
28 "stop_pct": -35, # hard stop from entry
29 "ladder": [(100, 0.50), (200, 0.25)], # +100% sells half, +200% sells a quarter
30 "trail_pct": 30, # the rest: out when price falls 30% from its peak
31 "time_stop_hours": 6, # not up 20% after 6 hours -> out, dead money
32 "time_stop_min_pct": 20,
33 "liq_drop_pct": 40, # pool liquidity down 40% from entry -> out now
34}
35
36LOG = "checks.csv"
37POSITIONS = "positions.json"
38TRADES = "trades.csv"
text
1FLAT TICKET. the size never grows because the last three won.
2THE BOT READS config.py, IT NEVER WRITES IT. a number moves when you move it.
3A FILTER IS LOOSENED IN WRITING. you change a number, you say why in the chat, same day.

06. run the limits before anything else

save as book.py. the cheapest check on the desk is the one that never calls an API. if today already lost $50, the desk doesn't even look at the token. if four positions are open, same. this file is also the desk's memory: two files you can open yourself, positions.json and trades.csv.

python
1# book.py
2# positions, trades, limits. the desk's memory lives in two files you can open yourself.
3import csv, json, os, sys, time
4from config import (POSITIONS, TRADES, TICKET_USD, MAX_OPEN, MAX_ENTRIES_PER_DAY,
5 DAILY_LOSS_HALT_USD, COOLDOWN_HOURS)
6
7def load() -> dict:
8 return json.load(open(POSITIONS)) if os.path.exists(POSITIONS) else {}
9
10def save(book: dict):
11 json.dump(book, open(POSITIONS, "w"), indent=2)
12
13def trades() -> list[dict]:
14 return list(csv.DictReader(open(TRADES))) if os.path.exists(TRADES) else []
15
16def record(mint, symbol, action, usd, pnl, reason):
17 new = not os.path.exists(TRADES)
18 with open(TRADES, "a", newline="") as f:
19 w = csv.DictWriter(f, fieldnames=["time", "mint", "symbol", "action", "usd", "pnl", "reason"])
20 if new:
21 w.writeheader()
22 w.writerow({"time": time.strftime("%Y-%m-%d %H:%M"), "mint": mint, "symbol": symbol,
23 "action": action, "usd": round(usd, 2), "pnl": round(pnl, 2), "reason": reason})
24
25def today_pnl() -> float:
26 day = time.strftime("%Y-%m-%d")
27 return sum(float(t["pnl"]) for t in trades() if t["time"].startswith(day))
28
29def guard(mint: str) -> str | None:
30 """Limits run before any network call. A blocked entry costs nothing."""
31 day = time.strftime("%Y-%m-%d")
32 book = load()
33 if today_pnl() <= -DAILY_LOSS_HALT_USD: return "halted_daily_loss"
34 if mint in book: return "already_open"
35 if len(book) >= MAX_OPEN: return "max_open"
36 opens = [t for t in trades() if t["action"] == "open" and t["time"].startswith(day)]
37 if len(opens) >= MAX_ENTRIES_PER_DAY: return "max_entries_today"
38 cutoff = time.strftime("%Y-%m-%d %H:%M", time.localtime(time.time() - COOLDOWN_HOURS * 3600))
39 if any(t["mint"] == mint and t["action"] == "close" and t["time"] >= cutoff for t in trades()):
40 return "cooldown"
41 return None
42
43def open_position(mint, symbol, price, liq_usd, usd=TICKET_USD):
44 book = load()
45 book[mint] = {"symbol": symbol, "entry": price, "qty0": usd / price, "qty": usd / price,
46 "peak": price, "liq0": liq_usd, "opened": time.time(), "steps": 0}
47 save(book)
48 record(mint, symbol, "open", usd, 0, "entry")
49
50def sell(mint, frac_of_original, price, reason) -> float:
51 """Sells a fraction of the ORIGINAL size (1.0 = everything left). Returns realized pnl."""
52 book = load()
53 p = book[mint]
54 q = min(p["qty"], p["qty0"] * frac_of_original)
55 pnl = q * (price - p["entry"])
56 p["qty"] -= q
57 action = "close" if p["qty"] <= p["qty0"] * 1e-6 else "partial"
58 record(mint, p["symbol"], action, q * price, pnl, reason)
59 if action == "close":
60 del book[mint]
61 save(book)
62 return pnl
63
64if __name__ == "__main__":
65 # live mode: after you buy in your own wallet -> python book.py add <mint> <usd> <price> <liq_usd>
66 # after you sell by hand -> python book.py close <mint> <price>
67 cmd = sys.argv[1]
68 if cmd == "add":
69 mint, usd, price, liq = sys.argv[2], float(sys.argv[3]), float(sys.argv[4]), float(sys.argv[5])
70 open_position(mint, mint[:6], price, liq, usd)
71 elif cmd == "close":
72 print(round(sell(sys.argv[2], 1.0, float(sys.argv[3]), "manual"), 2))

07. write the exit before the entry

most people decide when to sell while staring at a green candle. that's the worst moment to decide anything. on this desk every exit exists before the token is bought, and the bot just checks it every 10 minutes:

  • liquidity pulled 40% from entry: out, all of it, no questions
  • down 35%: out
  • up 100%: half goes, the rest is the market's money
  • up 200%: another quarter goes
  • after the first take, the rest leaves when price drops 30% from its peak
  • six hours without +20%: out, the slot is worth more than the hope

save as watch.py:

python
1# watch.py
2# every open position, every 10 minutes. prints nothing when nothing changed.
3import time
4from config import EXIT, PAPER
5from market import pool
6import book
7
8def watch() -> list[str]:
9 events = []
10 for mint, p in list(book.load().items()):
11 try:
12 m = pool(mint)
13 except Exception as e:
14 events.append(f"{p['symbol']}: price feed down ({e}), holding, will retry")
15 continue
16 if not m or m["price"] <= 0:
17 events.append(f"{p['symbol']}: pool gone. treat as rugged, sell what you can")
18 book.sell(mint, 1.0, 0.0, "pool_gone")
19 continue
20
21 price = m["price"]
22 b = book.load(); s = b[mint]
23 s["peak"] = max(s["peak"], price); book.save(b)
24 pct = (price / s["entry"] - 1) * 100
25 hours = (time.time() - s["opened"]) / 3600
26
27 def out(frac, why):
28 pnl = book.sell(mint, frac, price, why)
29 verb = "sold" if PAPER else "SELL NOW in your wallet"
30 size = "the rest" if frac >= 1 else f"{int(frac * 100)}%"
31 events.append(f"{s['symbol']}: {verb} {size} at {pct:+.0f}% ({why}), pnl ${pnl:+.2f}")
32
33 if m["liq_usd"] < s["liq0"] * (1 - EXIT["liq_drop_pct"] / 100):
34 out(1.0, "liquidity_pulled"); continue
35 if pct <= EXIT["stop_pct"]:
36 out(1.0, "stop"); continue
37 ladder = EXIT["ladder"]
38 if s["steps"] < len(ladder) and pct >= ladder[s["steps"]][0]:
39 out(ladder[s["steps"]][1], f"take_profit_{ladder[s['steps']][0]}")
40 b = book.load()
41 if mint in b:
42 b[mint]["steps"] += 1; book.save(b)
43 continue
44 if s["steps"] > 0 and price <= s["peak"] * (1 - EXIT["trail_pct"] / 100):
45 out(1.0, "trailing_stop"); continue
46 if hours >= EXIT["time_stop_hours"] and pct < EXIT["time_stop_min_pct"]:
47 out(1.0, "time_stop")
48 return events
49
50if __name__ == "__main__":
51 for e in watch():
52 print(e)
text
1LIQUIDITY FIRST. a pulled pool beats every other rule, even on a green position.
2THE TRAIL ARMS AFTER THE FIRST TAKE. before that, -35% is the only floor.
3DEAD MONEY IS A LOSS TOO. six flat hours free the slot for the next token.
4SILENCE IS A STATUS. watch.py prints nothing when nothing changed, so a quiet chat means you don't have to look.
boan - inline image

08. learn the 14 ways a token dies

if I had to throw away everything above and keep one thing, it would be this. it also works without any bot: run the same 14 checks by hand on rugcheck.xyz and dexscreener before your next buy and you'll skip most of the tokens you were about to regret.

13 of the checks are code. comparisons, no model, no opinion. the 14th is the only one that needs Grok.

#

check

kills when

why

1

already rugged

Rugcheck marks it rugged

someone already did it

2

mint authority

still open

the dev can print new supply into your bag

3

freeze authority

still open

the dev can freeze your tokens, you can't sell

4

danger flags

any danger-level risk

Rugcheck already saw something you'd have to prove wrong

5

risk score

above 40/100

small warnings add up

6

LP lock

biggest pool under 90% locked or burned

unlocked liquidity is a pull button

7

top 10 holders

over 30%, pools and lockers excluded

ten wallets can end your trade together

8

dev wallet

creator still holds over 5%

the dev is the first seller

9

insiders / bundles

any insider holder or insider graph hit

wallets funded together sell together

10

pool depth

under $30k liquidity

you'd be someone's exit

11

ticket share

your $25 is over 1% of the pool

your own buy moves the price

12

age

under 30 min or over 72h

too fresh for any report, or the move already happened

13

sells exist

20+ buys in the last hour, zero sells

that's the shape of a honeypot

14

X read (Grok)

no posts, copycat CA, bought hype, rug history

the part no API returns

boan - inline image

09. let code kill the obvious ones

checks 1 to 9. it fetches one report and keeps only the fields the desk reads. Grok never sees the raw report, a fat payload costs tokens and costs accuracy.

one detail people miss: the pool's own vault and the lockers sit in the top holders list. count them and every token looks like a whale token. the code drops them before it adds up the top 10.

python
1# rugcheck.py
2# one report per token, trimmed to the fields the desk reads. Grok never sees the raw report.
3import requests
4from config import FACTS
5
6RC = "https://api.rugcheck.xyz/v1/tokens"
7
8def report(mint: str) -> dict:
9 r = requests.get(f"{RC}/{mint}/report", timeout=20)
10 r.raise_for_status()
11 d = r.json()
12 token = d.get("token") or {}
13 known = d.get("knownAccounts") or {}
14
15 def is_pool(h): # AMM vaults and lockers sit in top holders, they are not whales
16 tag = known.get(h.get("owner")) or known.get(h.get("address")) or {}
17 return str(tag.get("type", "")).upper() in ("AMM", "LOCKER")
18
19 holders = [h for h in (d.get("topHolders") or []) if not is_pool(h)]
20 markets = sorted(d.get("markets") or [],
21 key=lambda m: ((m.get("lp") or {}).get("baseUSD") or 0)
22 + ((m.get("lp") or {}).get("quoteUSD") or 0),
23 reverse=True)
24 lp = (markets[0].get("lp") or {}) if markets else {}
25 supply = token.get("supply") or 0
26 risks = d.get("risks") or []
27
28 return {
29 "mint": mint,
30 "symbol": (d.get("tokenMeta") or {}).get("symbol"),
31 "rugged": bool(d.get("rugged")),
32 "risk_score": d.get("score_normalised"),
33 "danger": [x.get("name") for x in risks if x.get("level") == "danger"],
34 "warn": [x.get("name") for x in risks if x.get("level") == "warn"],
35 "mint_authority": token.get("mintAuthority", d.get("mintAuthority")),
36 "freeze_authority": token.get("freezeAuthority", d.get("freezeAuthority")),
37 "lp_locked_pct": lp.get("lpLockedPct", d.get("lpLockedPct")),
38 "top10_pct": round(sum(h.get("pct") or 0 for h in holders[:10]), 2) if holders else None,
39 "dev_pct": round((d.get("creatorBalance") or 0) / supply * 100, 2) if supply else None,
40 "insiders": sum(1 for h in holders if h.get("insider")) + (d.get("graphInsidersDetected") or 0),
41 "holders": d.get("totalHolders"),
42 }
43
44def fact_kill(t: dict) -> str | None:
45 """Plain comparisons. First failure wins, the reason gets logged."""
46 if t["risk_score"] is None: return "no_report"
47 if t["rugged"]: return "already_rugged"
48 if t["mint_authority"]: return "mint_open"
49 if t["freeze_authority"]: return "freeze_open"
50 if t["danger"]: return "danger:" + t["danger"][0]
51 if t["risk_score"] > FACTS["max_risk_score"]: return "risk_score"
52 if t["lp_locked_pct"] is None or t["lp_locked_pct"] < FACTS["min_lp_locked_pct"]:
53 return "lp_unlocked"
54 if t["top10_pct"] is None or t["top10_pct"] > FACTS["max_top10_pct"]:
55 return "top10_heavy"
56 if t["dev_pct"] is None or t["dev_pct"] > FACTS["max_dev_pct"]:
57 return "dev_holds"
58 if t["insiders"] > FACTS["max_insiders"]: return "insiders"
59 return None
boan - inline image

10. check the pool, not just the contract

checks 10 to 13. a contract can be perfect and the pool still won't let you out. this reads the deepest pool for the token and asks four boring questions: is there enough in it, how big is my ticket next to it, how old is it, and is anyone actually selling.

python
1# market.py
2# the pool itself: depth, age, price, and whether anyone can actually sell.
3import time
4import requests
5from config import FACTS, TICKET_USD
6
7DS = "https://api.dexscreener.com/tokens/v1/solana"
8
9def pool(mint: str) -> dict | None:
10 r = requests.get(f"{DS}/{mint}", timeout=15)
11 r.raise_for_status()
12 data = r.json()
13 pairs = data if isinstance(data, list) else (data.get("pairs") or [])
14 pairs = [p for p in pairs if (p.get("baseToken") or {}).get("address") == mint]
15 if not pairs:
16 return None
17 p = max(pairs, key=lambda x: (x.get("liquidity") or {}).get("usd") or 0)
18 h1 = (p.get("txns") or {}).get("h1") or {}
19 born = p.get("pairCreatedAt")
20 return {
21 "symbol": (p.get("baseToken") or {}).get("symbol"),
22 "price": float(p.get("priceUsd") or 0),
23 "liq_usd": (p.get("liquidity") or {}).get("usd") or 0,
24 "age_min": round((time.time() * 1000 - born) / 60000) if born else None,
25 "buys_1h": h1.get("buys", 0),
26 "sells_1h": h1.get("sells", 0),
27 "vol_1h": (p.get("volume") or {}).get("h1"),
28 "url": p.get("url"),
29 }
30
31def market_kill(m: dict | None) -> str | None:
32 if not m: return "no_pool"
33 if m["price"] <= 0: return "no_price"
34 if m["liq_usd"] < FACTS["min_liquidity_usd"]: return "thin_pool"
35 if TICKET_USD / m["liq_usd"] > FACTS["max_ticket_share"]: return "ticket_too_big"
36 if m["age_min"] is None or m["age_min"] < FACTS["min_age_min"]:
37 return "too_fresh"
38 if m["age_min"] > FACTS["max_age_hours"] * 60: return "too_old"
39 if m["buys_1h"] >= 20 and m["sells_1h"] == 0: return "nobody_can_sell"
40 return None

11. let Grok read what code can't

check 14. this is the reason the desk runs on Grok and not on any other model: it can search X while it thinks. a token can pass all 13 checks and still be a copycat with a different contract address, or a launch where forty accounts made last week posted the same sentence in the same minute. no API returns that. X does.

save as grok.py. it's the only file that reads the key and the only place the desk talks to xAI:

python
1# grok.py
2# the only file that holds the key and the only place the desk talks to xAI.
3import os, json, datetime, requests
4from config import MODEL
5
6URL = "https://api.x.ai/v1/responses"
7
8def _key() -> str:
9 k = os.environ.get("XAI_API_KEY")
10 if not k and os.path.exists(".env"):
11 for line in open(".env"):
12 if line.startswith("XAI_API_KEY="):
13 k = line.split("=", 1)[1].strip().strip('"')
14 if not k:
15 raise RuntimeError("no XAI_API_KEY in the environment or in .env")
16 return k
17
18def _text(resp: dict) -> str:
19 if isinstance(resp.get("output_text"), str):
20 return resp["output_text"]
21 parts = []
22 for item in resp.get("output") or []:
23 if item.get("type") == "message":
24 for c in item.get("content") or []:
25 if c.get("type") in ("output_text", "text"):
26 parts.append(c.get("text") or "")
27 return "\n".join(parts)
28
29def ask(system: str, payload: dict, read_x: bool = False) -> dict:
30 """One call, JSON in, JSON out. Anything that isn't JSON raises, and a raise is a SKIP upstream."""
31 body = {"model": MODEL,
32 "input": [{"role": "system", "content": system},
33 {"role": "user", "content": json.dumps(payload)}]}
34 if read_x:
35 since = (datetime.date.today() - datetime.timedelta(days=3)).isoformat()
36 body["tools"] = [{"type": "x_search", "from_date": since}]
37 body["max_turns"] = 2 # two rounds of searching, then it has to answer
38 r = requests.post(URL, timeout=180, json=body,
39 headers={"Authorization": f"Bearer {_key()}"})
40 r.raise_for_status()
41 text = _text(r.json())
42 start, end = text.find("{"), text.rfind("}")
43 if start == -1:
44 raise ValueError(f"no json in answer: {text[:200]}")
45 return json.loads(text[start:end + 1])

save as prompts.py. this is the file you'll actually reread:

python
1# prompts.py
2# the one Grok prompt on the desk. this is the file you will actually reread.
3
4JUDGE_PROMPT = """
5You are RUG DESK. A Solana memecoin just passed every hard check in code:
6authorities, LP, holders, dev wallet, insiders, pool depth, age.
7Do not re-check those numbers. You decide if one flat ticket goes in.
8You never size, you never pick exits.
9
10INPUT: {"token": {...rugcheck facts...}, "pool": {...dexscreener facts...}}
11
12STEP 1. Search X for the ticker and for the contract address, last 72 hours.
13STEP 2. Judge only what code can't see:
14 - do the warn-level risks stack into something worse together
15 - who posted first: the dev, a real account with history, or a wave of fresh accounts
16 - organic or bought: same wording, same minute, accounts with no past
17 - is the contract address in those posts the same one as in INPUT
18 - has this ticker name or this dev been tied to a rug before
19
20RETURN ONLY JSON:
21{"verdict": "BUY" | "SKIP", "confidence": 0.0-1.0,
22 "why": "two lines max", "x_read": "one line on what X looks like",
23 "main_risk": "one line"}
24
25Zero posts on X is an answer: nobody knows it exists. SKIP.
26Another contract address under the same ticker on X: copycat risk. SKIP.
27NEVER answer BUY when a field you needed is null.
28NEVER mention price targets. You don't know them.
29"""

12. wire it into one command

one contract address in, one verdict out. limits, facts, pool, Grok, in that order, and the first one that says no ends it:

python
1# check.py
2# one contract address in, one verdict out. runs every time a token crosses your desk.
3import csv, json, os, sys, time
4from config import LOG, MIN_CONFIDENCE, PAPER, TICKET_USD
5from rugcheck import report, fact_kill
6from market import pool, market_kill
7from grok import ask
8from prompts import JUDGE_PROMPT
9import book
10
11def log(row: dict):
12 new = not os.path.exists(LOG)
13 with open(LOG, "a", newline="") as f:
14 w = csv.DictWriter(f, fieldnames=["time", "mint", "symbol", "stage", "verdict",
15 "confidence", "reason"])
16 if new:
17 w.writeheader()
18 w.writerow({k: row.get(k) for k in w.fieldnames})
19
20def check(mint: str) -> dict:
21 if (g := book.guard(mint)): # limits, free
22 return {"stage": "limits", "verdict": "SKIP", "reason": g}
23
24 try:
25 token = report(mint) # rug filter, free
26 except Exception as e:
27 return {"stage": "facts", "verdict": "SKIP", "reason": f"rugcheck failed: {e}"}
28 if (k := fact_kill(token)):
29 return {"stage": "facts", "verdict": "SKIP", "reason": k, "symbol": token["symbol"]}
30
31 try:
32 m = pool(mint) # pool, free
33 except Exception as e:
34 return {"stage": "pool", "verdict": "SKIP", "reason": f"dexscreener failed: {e}"}
35 if (k := market_kill(m)):
36 return {"stage": "pool", "verdict": "SKIP", "reason": k, "symbol": token["symbol"]}
37
38 try:
39 ans = ask(JUDGE_PROMPT, {"token": token, "pool": m}, read_x=True) # the only paid step
40 except Exception as e:
41 return {"stage": "grok", "verdict": "SKIP", "reason": f"grok failed: {e}"}
42
43 verdict, conf = ans.get("verdict"), ans.get("confidence") or 0
44 if verdict == "BUY" and conf < MIN_CONFIDENCE:
45 verdict = "SKIP"
46 ans["why"] = f"low confidence {conf}"
47 return {**ans, "stage": "grok", "verdict": verdict, "symbol": m["symbol"] or token["symbol"],
48 "reason": ans.get("why"), "price": m["price"], "liq_usd": m["liq_usd"], "url": m["url"]}
49
50if __name__ == "__main__":
51 mint = sys.argv[1].strip()
52 v = check(mint)
53 log({"time": time.strftime("%Y-%m-%d %H:%M"), "mint": mint, **v})
54 if v["verdict"] == "BUY":
55 if PAPER:
56 book.open_position(mint, v["symbol"], v["price"], v["liq_usd"])
57 v["action"] = f"PAPER BUY ${TICKET_USD} at {v['price']}"
58 else:
59 v["action"] = (f"APPROVAL NEEDED: buy ${TICKET_USD} of {v['symbol']} in your own wallet, "
60 f"then run: python book.py add {mint} {TICKET_USD} <fill price> {v['liq_usd']}")
61 print(json.dumps(v, indent=2))

you don't need a scanner to use it. a CA from a call channel, a post, a friend's message: tell the bot "check <CA>" and it runs one line. what comes back looks like this (example shape, not a real token):

json
1{
2 "verdict": "SKIP",
3 "confidence": 0.78,
4 "why": "passes every hard check, but the first 30 posts came from accounts created this week, same wording.",
5 "x_read": "one real account, a wave of fresh ones, no CA mismatch",
6 "main_risk": "bought hype, the buyers leave when the posting stops",
7 "stage": "grok",
8 "symbol": "EXAMPLE",
9 "reason": "passes every hard check, but the first 30 posts came from accounts created this week, same wording."
10}
boan - inline image
text
1FACTS BEFORE OPINIONS. Grok never decides whether a mint is open.
2A FAILED CALL IS A SKIP. Rugcheck down, DexScreener down, Grok down, bad JSON: SKIP.
3NULL IS NOT CLEAN. a field that didn't come back kills the token, it never passes it.
4ONE VERDICT PER TOKEN. never re-run until it says BUY. if you disagree, you disagree with config.py.
5READ THE KILLS, NOT THE BUYS. the buys feel good. the kill reasons tell you if the filter works.

13. three routines, one sentence each

no workflow builder. you tell the bot each routine once, in a sentence, and it shows you the next run. these are mine, word for word:

text
1Every 10 minutes, in /workspace/desk run python watch.py.
2If it prints nothing, say nothing. If it prints anything, post it here word for word.
text
1Every 30 minutes, in /workspace/desk run python scan.py.
2Post only BUY verdicts: symbol, confidence, x_read.
3If Rugcheck or DexScreener is down, say which one once, not every run.
text
1Every day at 21:30, in /workspace/desk run python report.py and post the output here.
2Add one thing that worked today and one number in config.py you would question, with the reason.
3Never change that number yourself.

the two files those routines run. save as report.py:

python
1# report.py
2# the evening report. everything in it comes from checks.csv and trades.csv, nothing from memory.
3import csv, os, time
4from collections import Counter
5from config import LOG, PAPER
6import book
7
8day = time.strftime("%Y-%m-%d")
9checks = [r for r in csv.DictReader(open(LOG))] if os.path.exists(LOG) else []
10checks = [r for r in checks if r["time"].startswith(day)]
11done = [t for t in book.trades() if t["time"].startswith(day) and t["action"] != "open"]
12
13by_stage = Counter(r["stage"] for r in checks if r["verdict"] == "SKIP")
14buys = [r for r in checks if r["verdict"] == "BUY"]
15wins = [t for t in done if float(t["pnl"]) > 0]
16reasons = Counter(r["reason"] for r in checks if r["stage"] in ("facts", "pool"))
17
18print(f"EVENING REPORT {day} {'(PAPER)' if PAPER else '(LIVE)'}")
19print(f"checked: {len(checks)}")
20print(f"killed by rug filter: {by_stage['facts']} | by pool: {by_stage['pool']} | "
21 f"by Grok: {by_stage['grok']} | by limits: {by_stage['limits']}")
22print(f"bought: {len(buys)} | open now: {len(book.load())}")
23print(f"realized pnl today: ${book.today_pnl():+.2f}")
24if done:
25 print(f"green exits: {len(wins)}/{len(done)}")
26 best = max(done, key=lambda t: float(t["pnl"]))
27 worst = min(done, key=lambda t: float(t["pnl"]))
28 print(f"best: {best['symbol']} ${float(best['pnl']):+.2f} | worst: {worst['symbol']} ${float(worst['pnl']):+.2f}")
29if reasons:
30 top, n = reasons.most_common(1)[0]
31 print(f"most common kill: {top} ({n}x)")

and scan.py, the optional lead source. it pulls fresh Solana tokens that just paid for a DexScreener profile. a paid profile is not a quality signal, it only means the dev spent money. the filter decides the rest:

python
1# scan.py
2# optional lead source: fresh Solana tokens that just paid for a DexScreener profile.
3# a paid profile is not a quality signal, it just means the dev spent money. the filter decides.
4import csv, os, subprocess, sys
5import requests
6from config import LOG
7
8seen = {r["mint"] for r in csv.DictReader(open(LOG))} if os.path.exists(LOG) else set()
9r = requests.get("https://api.dexscreener.com/token-profiles/latest/v1", timeout=15)
10r.raise_for_status()
11fresh = [t["tokenAddress"] for t in r.json()
12 if t.get("chainId") == "solana" and t.get("tokenAddress") not in seen]
13
14for mint in fresh[:5]: # five per run, so one busy hour can't drain the API budget
15 subprocess.run([sys.executable, "check.py", mint])

last thing tonight: Settings, General, Auto-review. add two Ask first rules, they stop the action and wait for you no matter what any routine thinks:

text
1Ask first before any wallet connection, signature, swap or transfer.
2Ask first before editing any file named config.py.
boan - inline image
boan - inline image

14. a week on paper before a single dollar

PAPER = True means the desk does everything for real except the money. real tokens, real prices, real exits, fake fills. after seven days, open checks.csv and read it like a stranger wrote it.

what you're looking for: which check kills the most, whether the BUYs would have been ones you'd buy, and whether any SKIP reason looks wrong. a paper fill happens at the screen price, a live fill never does, so treat the paper result as the ceiling, not the forecast.

when you go live, flip PAPER to False and nothing else changes except the last step. check.py stops buying and posts an approval line instead: buy $25 yourself in your own wallet, then one command puts it in the book so watch.py starts guarding it.

when it breaks

this touches money, so every failure has one answer written down before it happens.

text
1401 from xAI -> bad key. Take over, rewrite .env, run the curl from step 03 again.
2400 / 404 from xAI -> model name changed. fix MODEL in config.py, nothing else.
3429 from xAI -> rate limit. that token is a SKIP, the next run moves on. never loop it.
4error about x_search -> your MODEL can't search X. pick one that can on console.x.ai -> Models.
5Grok answers in prose -> ValueError, logged, SKIP. never turn a paragraph into a trade.
6rugcheck 404 or empty -> too fresh to index. SKIP. fresh is not clean.
7lp_unlocked on everything -> some pool types have no LP token at all. open one on rugcheck yourself
8 before you touch that number, and change it in writing.
9no_pool -> not trading on a DEX yet, or not Solana. SKIP.
10routines went quiet -> Grok Bot pauses routines after you've been away a long time.
11 open Routines and switch them back on.

the bill

everything before the judge is free: the limits, Rugcheck, DexScreener. the only paid step is one Grok call per token that survived all 13 checks, with X search on.

text
1cost_per_token = input_tokens / 1_000_000 * price_per_M_input
2 + output_tokens / 1_000_000 * price_per_M_output
3 + posts_fetched / 1_000 * 5 # X search: $5 per 1,000 posts fetched
4
5max_turns = 2 keeps the search to two rounds, so posts_fetched stays small.
6every response reports its own x_posts_fetched, so day one tells you the real number.
7
8price: [price from console.x.ai]

the part people get wrong: the bill scales with survivors, not with the scanner. scan.py can feed the desk 240 tokens a day and most of them die for free before Grok ever hears about them.

boan - inline image

important part

this is my setup, not your strategy.

every number in config.py is mine, picked for a $25 ticket and my own tolerance for watching something go to zero. most memecoins do go to zero, and most people who trade them lose money. the filter doesn't make a token good. it removes the obvious ways to lose, and it makes you read why every token died.

run a week on paper. read the kills. move the numbers where you disagree, in writing. copy the shape, not the constants. not financial advice.

code kills, Grok reads X, you sign. a script never gets a judgment call, Grok never gets a calculator, and nobody but you ever gets the wallet.

save this before your next buy. follow if you're planning on running the same thing, more of this coming 🫶

my tg - t.me/+8wAXS0nHWgA2MGEy

my github - github.com/0xboan

trade here - fomo.family/r/0xboan

One-click save

Use YouMind for AI deep reading of viral articles

Save the source, ask focused questions, summarize the argument, and turn a viral article into reusable notes in one AI workspace.

Explore YouMind
For creators

Turn your Markdown into a clean 𝕏 article

When you publish your own long-form writing, images, tables, and code blocks make 𝕏 formatting painful. YouMind turns a full Markdown draft into a clean, ready-to-post 𝕏 article.

Try Markdown to 𝕏

More patterns to decode

Recent viral articles

Explore more viral articles