大多數人都是透過別人的 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、前 10 大持倉太集中,都在這裡出局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 -> Models5PAPER = True # 第一週:用真實價格做假成交。讀完 log 後再切換67TICKET_USD = 25 # 固定金額。絕對不要因為上次贏了就加大部位8MAX_OPEN = 4 # 市場上最多 4 x $25 = $1009MAX_ENTRIES_PER_DAY = 610DAILY_LOSS_HALT_USD = 50 # 今日已實現虧損達到此數值 -> 明天之前不再進場11COOLDOWN_HOURS = 24 # 剛退出的代幣,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 -> 視為 SKIP2627EXIT = {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. 在進場之前,先把出場規則寫好
大多數人是在盯著綠色 K 棒時才決定何時賣出。那是做決定最糟的時機。在這個交易台上,每個出場規則都在買進代幣前就存在了,而 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']}: 價格來源掛了 ({e}),繼續持有,稍後重試")15 continue16 if not m or m["price"] <= 0:17 events.append(f"{p['symbol']}: 池子消失了。視為被 rug,能賣多少賣多少")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 永遠不會看到原始報告,太大的 payload 既花 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 pull 有關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,只要其中一個說 no 就結束:
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))
你不需要掃描器也能用它。不管是 call 群組裡的 CA、某篇貼文,還是朋友傳來的訊息:只要告訴機器人「check <CA>」,它就會跑完那一行指令。回傳的結果長這樣(範例格式,不是真實代幣):
1{2 "verdict": "SKIP",3 "confidence": 0.78,4 "why": "通過所有硬性檢查,但前 30 篇貼文都來自本週建立的帳號,而且措辭一模一樣。",5 "x_read": "一個真實帳號,加上一大批新帳號,合約地址沒有不一致",6 "main_risk": "買來的熱度,一旦停止發文買家就會跑掉",7 "stage": "grok",8 "symbol": "EXAMPLE",9 "reason": "通過所有硬性檢查,但前 30 篇貼文都來自本週建立的帳號,而且措辭一模一樣。"10}

1先看事實,再看意見。Grok 永遠不會去判斷某個 mint 是否開放。2呼叫失敗就是 SKIP。Rugcheck 掛了、DexScreener 掛了、Grok 掛了、JSON 爛掉了:一律 SKIP。3NULL 不等於乾淨。沒回傳的欄位會直接殺掉這個代幣,絕對不會讓它過關。4每個代幣只有一個判定。在它說出 BUY 之前,絕對不要重跑。如果你不同意,那你反對的是 config.py。5看被殺掉的原因,而不是看買入。買入感覺很爽,但淘汰原因才能告訴你篩選器有沒有用。
13. 三個例行任務,各一句話
不用工作流程建立工具。你只要用一句話把每個例行任務告訴機器人一次,它就會顯示下次執行的時間。以下是我的設定,一字不差:
1每 10 分鐘,在 /workspace/desk 執行 python watch.py。2如果它沒印出任何東西,你就什麼都別說。如果有印出任何東西,就一字不漏地貼到這裡。
1每 30 分鐘,在 /workspace/desk 執行 python scan.py。2只貼出 BUY 的判定:代號、信心度、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} {'(PAPER)' if PAPER else '(LIVE)'}")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。加入兩條「先詢問」規則,無論任何例行任務怎麼想,它們都會擋下動作並等你確認:
1在任何錢包連線、簽名、交換或轉帳之前,都要先詢問。2在編輯任何名為 config.py 的檔案之前,都要先詢問。


14. 在花第一塊錢之前,先模擬一週
PAPER = True 代表 desk 除了錢以外,其他一切都來真的。真實代幣、真實價格、真實出場,只有成交是假的。七天後,打開 checks.csv,像讀陌生人寫的東西一樣去讀它。
你要找的是:哪個檢查淘汰最多代幣、那些 BUY 是不是你真的會買的,以及有沒有哪個 SKIP 理由看起來不對。模擬成交用的是螢幕上的價格,真實成交永遠不會是那個價,所以要把模擬結果當成天花板,而不是預測。
當你準備上實盤時,把 PAPER 改成 False,除了最後一步之外什麼都不會變。check.py 不再自動買入,而是貼出一行核准提示:你自己用錢包買 $25,然後下一道指令把它記進帳本,讓 watch.py 開始守護它。
出問題的時候
這套系統會碰到錢,所以每一種錯誤都要在發生前先寫好解答。
1xAI 回傳 401 -> 金鑰錯誤。接管控制權,重寫 .env,再跑一次步驟 03 的 curl。2xAI 回傳 400 / 404 -> 模型名稱改了。修正 config.py 裡的 MODEL,其他都不用動。3xAI 回傳 429 -> 觸發速率限制。那個代幣就是 SKIP,下次執行會繼續往下走。絕對不要用迴圈重試。4關於 x_search 的錯誤 -> 你的 MODEL 無法搜尋 X。到 console.x.ai -> Models 挑一個可以搜的。5Grok 用散文回答 -> ValueError,記錄下來,SKIP。絕對不要把一段文章變成交易。6rugcheck 404 或空白 -> 太新還沒被索引。SKIP。新不代表乾淨。7所有東西都顯示 lp_unlocked -> 有些池子類型根本沒有 LP 代幣。在你動那個數字之前,8 先自己去 rugcheck 開一個看看,並用文字記錄你的修改。9no_pool -> 還沒在 DEX 上交易,或不是 Solana。SKIP。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





