大多数人是通过别人的 pnl 截图认识 Grok Bot 的,打开它,对准一张 K 线图,然后把第一个 50 刀亏给一个在他们买入前就已经死掉的代币。问题不在机器人身上。没人告诉过它“死掉”长什么样。
如果让我从头再来,今晚我还会搭这套系统。一个 bot,一个文件夹,十个小文件。它不找百倍币。它的主要工作是杀掉代币,而少数活下来的那些,在动用一美元之前,依然需要 Grok 去读 X。
步骤 01 到 07 是第一个晚上要做的事:bot、密钥、限制、退出。08 到 12 是 rug 过滤器,14 项检查,每一项都写成了代码。13 和 14 把它切到自动驾驶,先从纸上交易开始。
如果你读过我那篇关于八 Agent 交易台的帖子,这就是那个交易台踩着的地板。
https://x.com/0xboan/status/2092226208234647626
怎么用:打开你的 Grok Bot,把整篇文章粘贴进去,然后说“在 /workspace/desk 里搭建这个交易台,纸面模式,任何碰到钱包的操作前先问我。”它会自己在那台电脑上把所有文件写好。
你要记住的分工:代码负责杀,Grok 负责读 X,你负责签名。 交易台上没人替别人干活。
01. 开始前先准备四样东西
四样东西。每一样都会说明它是什么、你今晚要做什么、以及为什么交易台需要它。
1. Grok Bot
它是什么。一个你可以雇佣具名 bot 的应用。每个 bot 都在一台带浏览器、文件和终端的云端电脑上工作,就算你合上笔记本它也会继续跑。
你要做什么。去 x.ai/bot 下载,登录,选择 Create your own(创建你自己的)。
为什么。整个交易台都运行在那台电脑上。文件、例行任务、提醒,全在一个地方,手机也能打开。
2. 一个 xAI API 密钥
它是什么。通过代码付费调用 Grok 的权限,内置了 X 搜索。
你要做什么。去 console.x.ai,API Keys,创建一个。先别贴到任何地方,步骤 03 会告诉你放哪。
为什么。例行任务没法坐在聊天窗口里思考四十个代币。但一个每次只调一次接口的脚本可以,而且它会把每个回答按同样的格式记录下来。
3. Rugcheck 和 DexScreener
它们是什么。两个免费的公开 API。Rugcheck 读合约:权限、LP、持仓者、内部人士、开发者钱包。DexScreener 读池子:深度、年龄、买卖对比。
你要做什么。什么都不用做。不用注册,读取不需要密钥。
为什么。事实先于观点。mint 权限没关不是主观判断,而是事实,而事实最先杀人。
4. 一个一次性钱包,但不是今晚
它是什么。一个全新的、从没拿过其他东西的 Solana 钱包。
你要做什么。暂时什么都不做。第一周只做纸上交易。
为什么。交易台永远不保存私钥。等你上线实盘时,它会告诉你买什么,你自己签名。
02. 雇一个 bot,不是八个
我的交易台现在跑着八个 bot。但一开始不是这样,你的也不该是这样。第一天就上八个 bot,等于同时调试八样东西。一个 bot 干一件事,能让你看清另外七个以后需要什么。
选择 Create your own,然后一字不差地给它这段话:
1名称:RUG DESK2工作:Solana memecoin 交易台,先做纸上交易3描述:根据我粘贴的文章,在 /workspace/desk 中搭建并运行交易台。4每个代币都必须经过 check.py,没有例外,没有捷径。5你永远不要买入、卖出、签名或转移资金。除非我在这个对话里要求,否则你永远不要编辑 config.py。你永远不要为了放行某个代币而调低过滤器的数值。6报告每一次击杀时都要带上原因,而不是你对这个代币的看法。7当某个数据源挂了,说出是哪一个然后跳过。永远不要猜缺失的数字。
最后两行比前五行更重要。一个会猜缺失数字的 bot,迟早会猜出一个让你亏钱的数字。

03. 别让密钥出现在聊天里
你发给 bot 的所有内容都会留在记录里。所以密钥要放进文件,由你在 bot 的电脑上输入。
打开 bot 的电脑,点击 Take over(接管),打开终端运行下面这段。它会询问密钥但不回显,所以历史记录里也不会出现:
1mkdir -p /workspace/desk && cd /workspace/desk2read -rs -p "xai key: " K && printf 'XAI_API_KEY=%s\n' "$K" > .env && unset K3chmod 600 .env4pip install requests
然后在做任何事之前先证明密钥能用。调一次接口,这样你以后就不会把坏密钥和烂提示词搞混:
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"}'56-> ... "type":"output_text","text":"OK" ...
点击 I'm done(我完成了)把电脑交还回去。从现在起 bot 就能运行交易台了,而且它在聊天里依然看不到密钥。

04. 动手之前先看完整条漏斗
八道关卡。免费的先跑。只有通过了免费关卡的才值得花一次 Grok 调用,只有 Grok 喊 BUY 的才花钱。这周连钱都不花。
10. LEAD 从任何地方拿到一个 CA:喊单、帖子、scan.py -> 一个 mint21. LIMITS book.py:日亏损、开仓数、冷却期 -> 免费,完全不联网32. FACTS rugcheck.py:10 项硬性击杀 -> mint 未关、LP 未锁、前十持仓过重,死在这里43. POOL market.py:深度、年龄、卖盘 -> 薄池子和貔貅盘死在这里54. JUDGE grok.py + X 搜索,一次调用 -> BUY 或 SKIP,一个理由,一个风险65. FILL 纸面:book.py 按实时价格成交 -> 实盘:你自己签名76. WATCH watch.py 每 10 分钟 -> 止损、分批止盈、追踪止损、时间止损87. REPORT report.py 在 21:30 -> 谁死了,谁赚钱了,什么该质疑
mint 权限没关不是主观判断。它是事实,而事实是免费的。Grok 只会看到那些已经通过了脚本能做的所有检查的代币。

05. 把所有数字放进一个文件
保存为 config.py。交易台上的每个数字都在这里,所以当你调参时,只需要改这一个文件,别的都不用动。这些是我的参数,针对 25 美元的仓位调过。它们不是推荐,只是你会不断调整的起点。
1# config.py2# 交易台上的每个数字都在这里。调参时,你只改这个文件,别的不动。34MODEL = "grok-4.7" # 如果 API 拒绝,去 console.x.ai -> Models 查一下5PAPER = True # 第一周:按真实价格假成交。读完日志再关掉67TICKET_USD = 25 # 固定金额。永远不按上一笔盈利来算仓位8MAX_OPEN = 4 # 4 x $25 = $100,这是市场里的最大敞口9MAX_ENTRIES_PER_DAY = 610DAILY_LOSS_HALT_USD = 50 # 今日已实现亏损达到这个数 -> 明天之前不再开新仓11COOLDOWN_HOURS = 24 # 刚离场的代币,这段时间内不再进1213FACTS = { # rug 过滤器。纯代码,不用模型14 "max_risk_score": 40, # rugcheck score_normalised,0-100,越高越危险15 "min_lp_locked_pct": 90, # 最大的池子:LP 锁定或销毁的比例16 "max_top10_pct": 30, # 前 10 大持仓者,排除池子和锁仓地址17 "max_dev_pct": 5, # 还在创建者钱包里的供应量18 "max_insiders": 0, # 被标记为内部人士的持仓者 + 内部关系图命中数19 "min_liquidity_usd": 30000,20 "max_ticket_share": 0.01, # 你的仓位永远不超过池子的 1%21 "min_age_min": 30, # 比这还新的话,所有报告都只是在猜22 "max_age_hours": 72,23}2425MIN_CONFIDENCE = 0.7 # Grok 在低于这个值时说 BUY -> 按 SKIP 处理2627EXIT = {28 "stop_pct": -35, # 相对入场价的硬止损29 "ladder": [(100, 0.50), (200, 0.25)], # +100% 卖一半,+200% 再卖四分之一30 "trail_pct": 30, # 剩下的:价格从高点回落 30% 就出31 "time_stop_hours": 6, # 6 小时还没涨 20% -> 出局,死钱32 "time_stop_min_pct": 20,33 "liq_drop_pct": 40, # 池子流动性比入场时跌 40% -> 立刻出34}3536LOG = "checks.csv"37POSITIONS = "positions.json"38TRADES = "trades.csv"
1固定仓位。不会因为连赢三把就加大仓位。2BOT 只读 config.py,永远不写。数字只有你能改。3放宽过滤器必须留下文字记录。你改了数字,当天就要在聊天里说明原因。
06. 在做任何事之前先跑限制条件
保存为 book.py。交易台上最便宜的检查,就是那个根本不调 API 的检查。如果今天已经亏了 50 美元,交易台连看都不会看那个代币一眼。如果已经有四个仓位开着,也一样。这个文件也是交易台的记忆:两个你可以自己打开的文件,positions.json 和 trades.csv。
1# book.py2# 仓位、交易、限制。交易台的记忆存在两个你可以自己打开的文件里。3import csv, json, os, sys, time4from config import (POSITIONS, TRADES, TICKET_USD, MAX_OPEN, MAX_ENTRIES_PER_DAY,5 DAILY_LOSS_HALT_USD, COOLDOWN_HOURS)67def load() -> dict:8 return json.load(open(POSITIONS)) if os.path.exists(POSITIONS) else {}910def save(book: dict):11 json.dump(book, open(POSITIONS, "w"), indent=2)1213def trades() -> list[dict]:14 return list(csv.DictReader(open(TRADES))) if os.path.exists(TRADES) else []1516def 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})2425def 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))2829def guard(mint: str) -> str | None:30 """限制条件在任何网络请求之前运行。被拦下的入场不花一分钱。"""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 None4243def 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")4950def sell(mint, frac_of_original, price, reason) -> float:51 """卖出原始仓位的某个比例(1.0 = 全部剩余)。返回已实现盈亏。"""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"] -= q57 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 pnl6364if __name__ == "__main__":65 # 实盘模式:在你自己的钱包买入后 -> python book.py add <mint> <usd> <price> <liq_usd>66 # 手动卖出后 -> 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. 在入场之前先写好退出
大多数人是在盯着绿柱子的时候决定什么时候卖的。那是做决定的最差时机。在这个交易台上,每个退出规则都在买入代币之前就存在了,bot 只是每 10 分钟检查一次:
- 流动性比入场时抽走 40%:出,全出,不问为什么
- 跌 35%:出
- 涨 100%:出一半,剩下的是市场的钱
- 涨 200%:再出四分之一
- 第一次止盈后,剩下的在价格从高点回落 30% 时离场
- 六小时还没涨 20%:出,这个仓位坑位比那点希望更值钱
保存为 watch.py:
1# watch.py2# 每 10 分钟检查所有开仓。没变化就不输出任何东西。3import time4from config import EXIT, PAPER5from market import pool6import book78def 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 continue16 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 continue2021 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) * 10025 hours = (time.time() - s["opened"]) / 36002627 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}")3233 if m["liq_usd"] < s["liq0"] * (1 - EXIT["liq_drop_pct"] / 100):34 out(1.0, "liquidity_pulled"); continue35 if pct <= EXIT["stop_pct"]:36 out(1.0, "stop"); continue37 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 continue44 if s["steps"] > 0 and price <= s["peak"] * (1 - EXIT["trail_pct"] / 100):45 out(1.0, "trailing_stop"); continue46 if hours >= EXIT["time_stop_hours"] and pct < EXIT["time_stop_min_pct"]:47 out(1.0, "time_stop")48 return events4950if __name__ == "__main__":51 for e in watch():52 print(e)
1流动性优先。池子被抽走这条规则压过一切,哪怕你正赚着。2追踪止损在第一次止盈后才启动。在那之前,-35% 是唯一的底。3死钱也是亏损。横盘六小时就把坑位让给下一个代币。4沉默也是一种状态。没变化时 watch.py 什么都不输出,所以聊天安静就意味着你不用去看。

08. 学会代币死亡的 14 种方式
如果上面那些只能留一样,我会留这个。它甚至不需要任何 bot:下次买入前,在 rugcheck.xyz 和 dexscreener 上手动跑一遍这 14 项检查,你就会避开大部分事后会让你后悔的代币。
其中 13 项是代码。纯比较,不用模型,不带观点。第 14 项是唯一需要 Grok 的。
#
检查项
何时击杀
为什么
1
已经被 rug
Rugcheck 标记为 rugged
别人已经干过了
2
mint 权限
仍然开放
项目方可以往你的袋子里印新币
3
freeze 权限
仍然开放
项目方可以冻结你的代币,你卖不掉
4
危险标记
任何 danger 级别的风险
Rugcheck 已经看到了你得去证伪的东西
5
风险分
高于 40/100
小警告加在一起就是大雷
6
LP 锁定
最大池子锁定或销毁不足 90%
没锁的流动性就是个撤资按钮
7
前 10 大持仓
超过 30%,不含池子和锁仓地址
十个钱包联手就能终结你的交易
8
开发者钱包
创建者仍持有超过 5%
项目方是第一个砸盘的人
9
内部人士 / 捆绑包
任何被标记为内部人士的持仓或关系图命中
一起充值的钱包会一起砸盘
10
池子深度
流动性低于 3 万美元
你就是别人的出口
11
仓位占比
你的 25 美元超过池子的 1%
你自己的买单就会拉动价格
12
年龄
不到 30 分钟或超过 72 小时
太新,任何报告都是猜;或者行情已经走完了
13
有卖盘吗
过去一小时 20+ 笔买入,零卖出
这就是貔貅盘的形状
14
读 X(Grok)
没帖子、山寨 CA、买来的热度、rug 前科
这是任何 API 都给不了的部分

09. 让代码杀掉那些明显的
检查 1 到 9。它拉一份报告,只保留交易台需要的字段。Grok 永远看不到原始报告,臃肿的数据包既费 token 又降准确率。
一个大家容易忽略的细节:池子自己的金库和锁仓地址也在头部持仓列表里。把它们算进去,每个代币看起来都像巨鲸盘。代码在统计前 10 之前会把它们剔除。
1# rugcheck.py2# 每个代币一份报告,裁剪到交易台需要的字段。Grok 永远看不到原始报告。3import requests4from config import FACTS56RC = "https://api.rugcheck.xyz/v1/tokens"78def 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 {}1415 def is_pool(h): # AMM 金库和锁仓地址在头部持仓里,它们不是巨鲸16 tag = known.get(h.get("owner")) or known.get(h.get("address")) or {}17 return str(tag.get("type", "")).upper() in ("AMM", "LOCKER")1819 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 026 risks = d.get("risks") or []2728 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 }4344def fact_kill(t: dict) -> str | None:45 """纯比较。第一个失败就定生死,原因会被记录。"""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

10. 检查池子,而不只是合约
检查 10 到 13。合约可以完美无缺,但池子照样让你出不来。这里会读取该代币最深的池子,问四个无聊的问题:里面钱够不够、我的仓位跟它比起来有多大、它多老了、到底有没有人在卖。
1# market.py2# 池子本身:深度、年龄、价格,以及是否真有人能卖得出去。3import time4import requests5from config import FACTS, TICKET_USD67DS = "https://api.dexscreener.com/tokens/v1/solana"89def 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 None17 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 }3031def 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. 让 Grok 去读代码读不到的东西
检查 14。这就是交易台跑在 Grok 而不是其他模型上的原因:它能一边思考一边搜 X。一个代币可能通过全部 13 项检查,但依然是个换了合约地址的山寨盘,或者是四十个上周刚建的号在同一分钟发了同一句话的发射局。没有 API 能返回这些。X 可以。
保存为 grok.py。这是唯一读取密钥的文件,也是交易台唯一和 xAI 对话的地方:
1# grok.py2# 唯一保存密钥的文件,也是 desk 与 xAI 通信的唯一地方。3import os, json, datetime, requests4from config import MODEL56URL = "https://api.x.ai/v1/responses"78def _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("环境变量或 .env 中没有 XAI_API_KEY")16 return k1718def _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)2829def ask(system: str, payload: dict, read_x: bool = False) -> dict:30 """一次调用,JSON 进,JSON 出。任何非 JSON 都会抛出异常,而抛出异常在上游就意味着跳过(SKIP)。"""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 # 搜索两轮,然后必须给出答案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"回答中没有 json:{text[:200]}")45 return json.loads(text[start:end + 1])
保存为 prompts.py。这才是你真正会反复阅读的文件:
1# prompts.py2# desk 上唯一的 Grok 提示词。这才是你真正会反复阅读的文件。34JUDGE_PROMPT = """5你是 RUG DESK。一个 Solana memecoin 刚刚通过了代码中的所有硬性检查:6权限、LP、持有者、开发者钱包、内部人员、池子深度、存续时间。7不要重新检查那些数字。由你决定是否投入这一张固定金额的票。8你永远不负责确定仓位大小,也永远不挑选退出时机。910输入:{"token": {...rugcheck 事实...}, "pool": {...dexscreener 事实...}}1112步骤 1. 在 X 上搜索该代币符号和合约地址,范围是过去 72 小时。13步骤 2. 只判断代码看不到的东西:14 - warn 级别的风险叠加在一起是否会变成更严重的问题15 - 谁最先发帖:开发者、有历史记录的真实账号,还是一波新注册的账号16 - 自然流量还是买量:相同的措辞、同一分钟发布、没有历史记录的账号17 - 这些帖子中的合约地址是否与输入中的一致18 - 这个代币符号或这个开发者以前是否涉及过跑路(rug)1920仅返回 JSON:21{"verdict": "BUY" | "SKIP", "confidence": 0.0-1.0,22 "why": "最多两行", "x_read": "一行描述 X 上的情况",23 "main_risk": "一行"}2425X 上零帖子也是一个答案:没人知道它的存在。跳过(SKIP)。26X 上同一个代币符号下出现了另一个合约地址:仿盘风险。跳过(SKIP)。27当你需要的某个字段为 null 时,绝对不要回答买入(BUY)。28绝对不要提及目标价。你不知道目标价。29"""
12. 把它们串成一条命令
输入一个合约地址,输出一个判定结果。依次经过限额、事实、池子、Grok,第一个说不的环节就会终止流程:
1# check.py2# 输入一个合约地址,输出一个判定结果。每当有代币来到你的 desk 时都会运行。3import csv, json, os, sys, time4from config import LOG, MIN_CONFIDENCE, PAPER, TICKET_USD5from rugcheck import report, fact_kill6from market import pool, market_kill7from grok import ask8from prompts import JUDGE_PROMPT9import book1011def 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})1920def check(mint: str) -> dict:21 if (g := book.guard(mint)): # 限额检查,免费22 return {"stage": "limits", "verdict": "SKIP", "reason": g}2324 try:25 token = report(mint) # rug 过滤,免费26 except Exception as e:27 return {"stage": "facts", "verdict": "SKIP", "reason": f"rugcheck 失败:{e}"}28 if (k := fact_kill(token)):29 return {"stage": "facts", "verdict": "SKIP", "reason": k, "symbol": token["symbol"]}3031 try:32 m = pool(mint) # 池子检查,免费33 except Exception as e:34 return {"stage": "pool", "verdict": "SKIP", "reason": f"dexscreener 失败:{e}"}35 if (k := market_kill(m)):36 return {"stage": "pool", "verdict": "SKIP", "reason": k, "symbol": token["symbol"]}3738 try:39 ans = ask(JUDGE_PROMPT, {"token": token, "pool": m}, read_x=True) # 唯一付费的步骤40 except Exception as e:41 return {"stage": "grok", "verdict": "SKIP", "reason": f"grok 失败:{e}"}4243 verdict, conf = ans.get("verdict"), ans.get("confidence") or 044 if verdict == "BUY" and conf < MIN_CONFIDENCE:45 verdict = "SKIP"46 ans["why"] = f"置信度过低 {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"]}4950if __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"模拟买入 ${TICKET_USD},价格 {v['price']}"58 else:59 v["action"] = (f"需要批准:在你自己的钱包中买入 ${TICKET_USD} 的 {v['symbol']},"60 f"然后运行:python book.py add {mint} {TICKET_USD} <成交价格> {v['liq_usd']}")61 print(json.dumps(v, indent=2))
你不需要扫描器也能用它。从喊单频道、帖子或朋友消息里拿到一个 CA(合约地址):告诉机器人 "check <CA>",它就会跑完整个流程。返回的结果长这样(示例结构,不是真实代币):
1{2 "verdict": "SKIP",3 "confidence": 0.78,4 "why": "通过了所有硬性检查,但前 30 个帖子来自本周创建的账号,措辞完全相同。",5 "x_read": "一个真实账号,一波新注册账号,没有 CA 不一致的情况",6 "main_risk": "买来的热度,发帖一停买家就跑",7 "stage": "grok",8 "symbol": "EXAMPLE",9 "reason": "通过了所有硬性检查,但前 30 个帖子来自本周创建的账号,措辞完全相同。"10}

1先看事实,再看观点。Grok 永远不会去判断某个 mint 是否开放。2调用失败就是跳过。Rugcheck 挂了、DexScreener 挂了、Grok 挂了、JSON 格式错误:全部跳过。3NULL 不等于干净。某个字段没返回就会直接淘汰该代币,绝不会让它通过。4每个代币只有一个判定结果。在它显示买入之前,绝不要重复运行。如果你不同意,那你反对的是 config.py。5看淘汰原因,别看买入记录。买入让人心情好,但淘汰原因才能告诉你过滤器是否有效。
13. 三个例行任务,每个一句话
不需要工作流构建器。你用一句话把每个例行任务告诉机器人一次,它就会给你展示下一次运行的情况。以下是我的原话,一字不差:
1每 10 分钟,在 /workspace/desk 目录下运行 python watch.py。2如果它什么都没输出,你就什么都别说。如果它输出了任何内容,就一字不差地发到这里。
1每 30 分钟,在 /workspace/desk 目录下运行 python scan.py。2只发布判定为买入的结果:代币符号、置信度、x_read。3如果 Rugcheck 或 DexScreener 挂了,只说一次是哪个挂了,不要每次运行都重复。
1每天 21:30,在 /workspace/desk 目录下运行 python report.py,并把输出发到这里。2补充一件今天奏效的事,以及一个你在 config.py 中想质疑的数字,并说明理由。3绝对不要自己修改那个数字。
这两个文件就是上述例行任务要运行的脚本。保存为 report.py:
1# report.py2# 晚间报告。里面的所有内容都来自 checks.csv 和 trades.csv,不靠记忆。3import csv, os, time4from collections import Counter5from config import LOG, PAPER6import book78day = 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"]1213by_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"))1718print(f"晚间报告 {day} {'(模拟)' if PAPER else '(实盘)'}")19print(f"已检查:{len(checks)}")20print(f"被 rug 过滤器淘汰:{by_stage['facts']} | 被池子淘汰:{by_stage['pool']} | "21 f"被 Grok 淘汰:{by_stage['grok']} | 被限额拦截:{by_stage['limits']}")22print(f"已买入:{len(buys)} | 当前持仓:{len(book.load())}")23print(f"今日已实现盈亏:${book.today_pnl():+.2f}")24if done:25 print(f"盈利退出:{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['symbol']} ${float(best['pnl']):+.2f} | 最差:{worst['symbol']} ${float(worst['pnl']):+.2f}")29if reasons:30 top, n = reasons.most_common(1)[0]31 print(f"最常见的淘汰原因:{top}({n} 次)")
还有 scan.py,可选的线索来源。它会抓取刚刚付费购买了 DexScreener 主页的新鲜 Solana 代币。付费主页并不是质量信号,它只意味着开发者花了钱。剩下的由过滤器决定:
1# scan.py2# 可选的线索来源:刚刚付费购买了 DexScreener 主页的新鲜 Solana 代币。3# 付费主页不是质量信号,只意味着开发者花了钱。由过滤器来决定。4import csv, os, subprocess, sys5import requests6from config import LOG78seen = {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]1314for mint in fresh[:5]: # 每次运行五个,这样再忙的一个小时也不会耗尽 API 额度15 subprocess.run([sys.executable, "check.py", mint])
今晚最后一件事:Settings(设置)、General(常规)、Auto-review(自动审查)。添加两条 Ask first(先询问)规则,无论任何例行任务怎么想,它们都会暂停操作并等你确认:
1在进行任何钱包连接、签名、兑换或转账之前,先询问我。2在编辑任何名为 config.py 的文件之前,先询问我。


14. 在花第一块钱之前,先模拟跑一周
PAPER = True 意味着 desk 除了不动真金白银之外,其他一切都按真实情况执行。真实的代币、真实的价格、真实的退出,只是成交是假的。七天后,打开 checks.csv,像读陌生人写的东西一样去读它。
你要找的是:哪个检查环节淘汰得最多;那些判定为买入的代币是不是你真的会买的;以及有没有哪个跳过理由看起来不对劲。模拟成交是按屏幕价格算的,而实盘成交永远做不到这一点,所以要把模拟结果当成上限,而不是预测。
当你准备上实盘时,把 PAPER 改成 False,除了最后一步之外什么都不用变。check.py 不再自动买入,而是发出一条审批信息:你自己去钱包里买 $25,然后用一条命令把它记入账本,这样 watch.py 就会开始盯防它。
出问题时怎么办
这涉及到钱,所以每种故障都要在发生前写好唯一的应对方案。
1xAI 返回 401 -> 密钥错误。接管控制权,重写 .env,再次运行第 03 步的 curl 命令。2xAI 返回 400 / 404 -> 模型名称变了。修改 config.py 中的 MODEL,别的都不用动。3xAI 返回 429 -> 触发速率限制。该代币直接跳过,下次运行继续。绝不要循环重试。4关于 x_search 的报错 -> 你的 MODEL 无法搜索 X。去 console.x.ai -> Models 选一个能搜的。5Grok 用散文回答 -> 抛出 ValueError,记录日志,跳过。绝不要把一段话变成一笔交易。6rugcheck 返回 404 或为空 -> 太新了还没被索引。跳过。新不等于干净。7所有代币都报 lp_unlocked -> 有些池子类型根本没有 LP 代币。在改这个数字之前,8 先去 rugcheck 上自己开一个看看,并用文字记录下来。9no_pool -> 还没在 DEX 上交易,或者不是 Solana 链。跳过。10例行任务没动静了 -> 你离开太久后,Grok Bot 会暂停例行任务。11 打开 Routines 重新开启它们。
账单
裁判之前的所有环节都是免费的:限额、Rugcheck、DexScreener。唯一付费的步骤是,对挺过全部 13 项检查的代币调用一次 Grok,并开启 X 搜索。
1cost_per_token = input_tokens / 1_000_000 * price_per_M_input2 + output_tokens / 1_000_000 * price_per_M_output3 + posts_fetched / 1_000 * 5 # X 搜索:每获取 1,000 个帖子 $545max_turns = 2 把搜索限制在两轮以内,所以 posts_fetched 会保持很小。6每次响应都会报告自己的 x_posts_fetched,所以第一天你就能知道真实数字。78价格:[来自 console.x.ai 的价格]
人们最容易搞错的地方:账单规模取决于存活下来的代币数量,而不是扫描器的数量。scan.py 一天可以给 desk 喂 240 个代币,其中大部分在 Grok 听说它们之前就已经被免费淘汰了。

重要的一点
这是我的配置,不是你的策略。
config.py 里的每一个数字都是我定的,针对 $25 的单笔金额和我自己看着资产归零的承受能力。大多数 memecoin 确实会归零,大多数交易它们的人都在亏钱。过滤器并不能让一个代币变好。它只是剔除了那些显而易见的亏损方式,并逼着你去读懂每个代币死掉的原因。
先模拟跑一周。看看那些淘汰记录。把你不同意的数字改掉,并用文字记录下来。抄它的框架,别照搬它的常量。这不是财务建议。
代码负责淘汰,Grok 负责读 X,你负责签名。脚本永远不做主观判断,Grok 永远拿不到计算器,除了你之外没人能碰钱包。
在下一次买入前把这个存下来。如果你打算跑同样的东西,请关注我,后续还会更新更多 🫶
我的 tg - t.me/+8wAXS0nHWgA2MGEy
我的 github - github.com/0xboan
在这里交易 - fomo.family/r/0xboan





