ほとんどの人は、他人の PnL スクリーンショットを通じて Grok Bot に出会う。そしてそれを開き、チャートに向けて、自分が買う前にすでに死んでいたトークンに最初の 50 ドルを溶かす。問題はボットではない。「死んでいる」とはどういう状態かを、誰も教えていないのだ。
これは、もしゼロからやり直すとしても今夜また同じように組むであろう構成だ。1 つのボット、1 つのフォルダ、10 個の小さなファイル。100 倍を狙い撃ちするものではない。主な仕事はトークンを「殺す」ことであり、生き残ったわずかなトークンについても、1 ドルたりとも動かす前に Grok が X を読む必要がある。
ステップ 01 から 07 は初日の夜にやること:ボット、キー、制限、エグジット。08 から 12 はラグフィルターで、14 のチェック項目すべてがコードとして書き下されている。13 と 14 でオートパイロットに乗せるが、まずはペーパー(仮想取引)からだ。
私の「8 エージェントデスク」の記事を読んだ人ならわかると思うが、この記事はそのデスクが立つ土台そのものだ。
https://x.com/0xboan/status/2092226208234647626
使い方:Grok Bot を開き、この記事全体を貼り付けて、「このデスクを /workspace/desk に構築して。ペーパーモードで、ウォレットに触る前に必ず私に確認すること」と言うだけだ。あとはボット自身が自分のコンピューター上で全ファイルを書き上げる。
絶対に守るべき役割分担:コードが殺し、Grok が X を読み、あなたが署名する。 デスク上の誰かが他人の仕事をすることは決してない。
01. 始める前に 4 つ用意する
4 つある。それぞれについて、それが何か、今夜何をするのか、なぜデスクに必要なのかを説明する。
1. Grok Bot
何か。名前付きのボットを雇うアプリだ。各ボットはブラウザ、ファイル、ターミナルを備えたクラウドコンピューター上で動き、あなたのノート PC の蓋を閉じても作業を続ける。
何をするか。x.ai/bot でダウンロードし、サインインして「Create your own」を選ぶ。
なぜか。デスク全体がこのコンピューター上に存在するからだ。ファイル、ルーティン、アラート、すべてが一箇所にまとまり、スマホからも開ける。
2. xAI API キー
何か。コードから Grok にアクセスするための有料手段で、X 検索が組み込まれている。
何をするか。console.x.ai に行き、API Keys から 1 つ作成する。まだどこにも貼り付けないこと。配置先はステップ 03 で説明する。
なぜか。ルーティンはチャットの中で 40 個のトークンを座って考えることができない。しかしトークンごとに 1 回呼び出すスクリプトなら可能で、すべての回答を同じ形式でログに残せる。
3. Rugcheck と DexScreener
何か。2 つの無料パブリック API。Rugcheck はコントラクトを読む:権限、LP、ホルダー、インサイダー、開発者ウォレットなど。DexScreener はプールを読む:深さ、経過時間、買いに対する売りなど。
何をするか。何もしない。登録も不要で、読み取りにキーも要らない。
なぜか。意見より事実が先に来るからだ。mint 権限が開いているかどうかは判断ではなく事実であり、事実が最初にトークンを殺す。
4. バーナーウォレット(ただし今夜は使わない)
何か。これまで何も保持したことがない、新しい Solana ウォレット。
何をするか。今は何もしない。第 1 週はペーパーで回す。
なぜか。デスクは決してキーを保持しない。本番に移行しても、買うものを指示するのはデスクで、署名するのはあなた自身だ。
02. 8 体ではなく 1 体のボットを雇う
私のデスクは現在 8 体のボットで動いている。だが最初からそうだったわけではないし、あなたのデスクもそうすべきではない。初日に 8 体いれば、同時に 8 つのデバッグを抱えることになる。1 つの仕事を持つ 1 体のボットを使えば、残りの 7 体が後で何を必要とするかが見えてくる。
「Create your own」を選び、以下の内容を一言一句そのまま渡す:
1Name: RUG DESK2Job: Solana ミームコインデスク、まずはペーパーから3Description: 私が貼り付ける記事に基づいて、/workspace/desk にデスクを構築・実行すること。4すべてのトークンは例外なく、抜け道なく check.py を通過させる。5あなたは決して資金を買ったり、売ったり、署名したり、移動させたりしない。6私がこのチャットで指示しない限り、config.py を編集してはいけない。7トークンを通すためにフィルターの数値を下げてはいけない。8トークンへの個人的な意見ではなく、殺した理由とともにすべてのキルを報告すること。9ソースがダウンしている場合は、どれかを伝えてスキップすること。欠損した数値を決して推測しないこと。
最後の 2 行は、最初の 5 行よりも重要だ。欠損した数値を推測するボットは、いずれあなたに損害を与える数値を推測してしまう。

03. キーはチャットに入れない
ボットに入力したものはすべてトランスクリプトに残る。だからキーはファイルに入れ、ボットのコンピューター上であなた自身がタイプする。
ボットのコンピューターを開き、「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
次に、他の何よりも先にキーが機能することを証明する。1 回だけ呼び出せば、後で不正なキーと不正なプロンプトを混同することがなくなる:
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」をクリックしてコンピューターを返す。これでボットはデスクを実行できるようになるが、チャット上でキーを見ることは依然としてない。

04. 作る前にファネル全体を見渡す
8 つのパスがある。無料のものが先に走る。無料パスを生き残ったものだけが Grok の呼び出しコストをかけ、Grok が BUY と判定したものだけが実際の資金を使う。今週はそれすらやらない。
10. LEAD どこからでも得た CA:通話、投稿、scan.py -> 1 つの mint21. LIMITS book.py:日次損失、空き枠、クールダウン -> 無料、ネットワーク通信すら不要32. FACTS rugcheck.py:10 のハードキル -> mint 開放、LP 未ロック、上位 10 位の大量保有者はここで死ぬ43. POOL market.py:深さ、経過時間、売り -> 薄いプールとハニーポットはここで死ぬ54. JUDGE grok.py + X 検索、1 回の呼び出し -> BUY か SKIP、理由 1 つ、リスク 1 つ65. FILL ペーパー:book.py がリアルタイム価格で記録 -> 本番:あなた自身が署名する76. WATCH watch.py が 10 分ごと -> ストップ、ラダー、トレール、タイムストップ87. REPORT report.py が 21:30 に -> 何が死んだか、何が利益を出したか、何を疑うべきか
mint 権限が開いているかどうかは判断ではない。それは事実であり、事実は無料だ。Grok は、スクリプトで可能なすべてのチェックをすでに通過したトークンしか目にしない。

05. すべての数値を 1 つのファイルに入れる
config.py として保存する。デスク上のすべての数値はここに集約されるため、調整するときはこの 1 ファイルだけを編集すればよい。以下は私のもので、25 ドルのチケットサイズに合わせて調整してある。推奨値ではなく、あなたが動かしていくための出発点だ。
1# config.py2# デスク上のすべての数値はここにある。調整するときは、このファイルだけを編集する。34MODEL = "grok-4.7" # API が拒否する場合は console.x.ai -> Models を確認5PAPER = True # 第 1 週:実価格でのフェイク約定。ログを読み終えるまで切り替えない67TICKET_USD = 25 # 固定。直近の勝ちでサイズを変えない8MAX_OPEN = 4 # 4 x $25 = 市場投入は最大 $1009MAX_ENTRIES_PER_DAY = 610DAILY_LOSS_HALT_USD = 50 # 本日実現損がこの値に達したら -> 明日まで新規エントリー不可11COOLDOWN_HOURS = 24 # 手放したばかりのトークンへの再エントリー禁止1213FACTS = { # ラグフィルター。コードのみ、モデル不使用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% で 1/4 売却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チケットは固定。直近 3 回勝ったからといってサイズは大きくしない。2ボットは config.py を読むだけで、決して書き込まない。数値が変わるのはあなたが変えたときだけ。3フィルター緩和は明文化する。数値を変えるなら、同日中にチャットで理由を述べる。
06. 他の何よりも先に制限を走らせる
book.py として保存する。デスクで最も安いチェックは、API を一切呼ばないものだ。今日の損失がすでに 50 ドルに達していれば、デスクはトークンすら見ない。4 つのポジションが開いていても同様だ。このファイルはデスクの記憶でもある:あなた自身が開ける 2 つのファイル、positions.json と trades.csv だ。
1# book.py2# ポジション、トレード、制限。デスクの記憶は、あなた自身が開ける 2 つのファイルに存在する。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. エントリーの前にエグジットを書く
ほとんどの人は、緑のローソク足を見つめながらいつ売るかを決める。それは何かを決めるのに最悪のタイミングだ。このデスクでは、トークンを買う前にすべてのエグジットが存在しており、ボットは 10 分ごとにそれを確認するだけだ:
- 流動性がエントリーから 40% 抜かれた:質問無用で全量撤退
- -35%:撤退
- +100%:半分を利確、残りは市場の金
- +200%:さらに 1/4 を利確
- 最初の利確後、残りは高値から 30% 下落したら撤退
- 6 時間経っても +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']}: プール消失。ラグプル扱いとし、売れる分を売る")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 "今すぐ自分のウォレットで売る"30 size = "残り全部" 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死に金も損失である。6 時間動かなければ、次のトークンのために枠を空ける。4沈黙もステータスだ。watch.py は変化がないと何も出力しないので、チャットが静かなら見る必要はないということだ。

08. トークンが死ぬ 14 の方法を学ぶ
上記のすべてを捨てて 1 つだけ残すとしたら、私はこれを選ぶ。これはボットなしでも機能する:次に買う前に、rugcheck.xyz と dexscreener で同じ 14 のチェックを手動で実行すれば、後悔することになるトークンのほとんどを回避できる。
13 のチェックはコードだ。比較のみで、モデルも意見も介在しない。14 番目だけが Grok を必要とする。
#
チェック
死ぬ条件
理由
1
すでにラグプル済み
Rugcheck がラグと判定
誰かがすでにやった
2
mint 権限
まだ開いている
開発者があなたのバッグに新規供給を刷り込める
3
freeze 権限
まだ開いている
開発者がトークンを凍結でき、売れなくなる
4
危険フラグ
danger レベルのリスクが 1 つでも
Rugcheck がすでに、あなたが覆す必要のある何かを見つけている
5
リスクスコア
40/100 を超える
小さな警告が積み重なる
6
LP ロック
最大プールで 90% 未満しかロックまたはバーンされていない
未ロックの流動性は引き抜きボタンだ
7
上位 10 ホルダー
30% 超(プールとロッカーを除く)
10 個のウォレットが連携してあなたのトレードを終わらせられる
8
開発者ウォレット
クリエイターがまだ 5% 以上保有
開発者が最初の売り手になる
9
インサイダー / バンドル
インサイダーホルダーまたはインサイダーグラフヒットが 1 つでも
一緒に資金提供されたウォレットは一緒に売る
10
プールの深さ
流動性 3 万ドル未満
あなたが誰かの出口になる
11
チケット比率
あなたの 25 ドルがプールの 1% を超える
あなた自身の買いで価格が動く
12
経過時間
30 分未満または 72 時間超
レポートを出すには早すぎるか、すでにムーブは終わった後
13
売りが存在するか
過去 1 時間で 20 件以上の買い、売りゼロ
ハニーポットの形をしている
14
X の読み取り(Grok)
投稿なし、模倣 CA、ハイプ買い、ラグ履歴
どの API も返さない部分

09. 明白なものはコードに殺させる
チェック 1〜9。レポートを 1 つ取得し、デスクが読むフィールドだけを残す。Grok は生のレポートを決して見ない。巨大なペイロードはトークンを消費し、精度も下げる。
多くの人が見落とす詳細:プール自身のボールトとロッカーが上位ホルダーリストに入っていることだ。これらを数えると、あらゆるトークンがクジラトークンに見えてしまう。コードは上位 10 位を合計する前にこれらを除外する。
1# rugcheck.py2# トークンごとに 1 レポート、デスクが読むフィールドに絞る。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。コントラクトが完璧でも、プールが出口を塞いでいることがある。ここでは対象トークンの最も深いプールを読み取り、退屈な 4 つの質問をする:十分な資金はあるか、それに対して自分のチケットはどれくらい大きいか、どれくらい古いか、実際に売っている人はいるか。
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 のチェックをすべて通過しても、異なるコントラクトアドレスを持つ模倣品だったり、先週作られた 40 アカウントが同じ分に同じ文章を投稿したローンチだったりする。どの API もそれを返さない。X なら返せる。
grok.py として保存する。キーを読み取るのはこのファイルだけであり、デスクが xAI と対話する唯一の場所でもある:
1# grok.py2# キーを保持する唯一のファイルであり、デスクが 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("no XAI_API_KEY in the environment or in .env")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 """1回の呼び出しで、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 # 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"no json in answer: {text[:200]}")45 return json.loads(text[start:end + 1])
prompts.py として保存します。これが実際に何度も読み返すファイルです:
1# prompts.py2# デスクにある唯一の Grok プロンプト。実際に何度も読み返すのはこのファイルです。34JUDGE_PROMPT = """5あなたは RUG DESK です。ある Solana ミームコインが、コードによるすべてのハードチェックを通過しました:6権限、LP、ホルダー、開発者ウォレット、インサイダー、プール深度、経過時間。7これらの数値を再確認しないでください。あなたが決めるのは、1枚のフラットチケットを入れるかどうかだけです。8ポジションサイズを決めたり、出口を選んだりすることは決してありません。910INPUT: {"token": {...rugcheck の事実...}, "pool": {...dexscreener の事実...}}1112STEP 1. 直近72時間について、ティッカーとコントラクトアドレスを X で検索してください。13STEP 2. コードでは見えない部分だけを判断してください:14 - warn レベルのリスクが組み合わさって、より深刻な問題になっていないか15 - 最初に投稿したのは誰か:開発者か、履歴のある実在アカウントか、新規アカウントの群れか16 - オーガニックか購入か:同じ文言、同じ時刻、過去のないアカウント17 - それらの投稿にあるコントラクトアドレスは INPUT のものと一致しているか18 - このティッカー名や開発者が、過去にラグプルに関与していないか1920JSON のみを返答してください:21{"verdict": "BUY" | "SKIP", "confidence": 0.0-1.0,22 "why": "最大2行", "x_read": "X の状況を1行で",23 "main_risk": "1行"}2425X に投稿がゼロというのも一つの答えです:誰もその存在を知りません。SKIP。26X 上で同じティッカーの別コントラクトアドレスがある場合:コピーキャットのリスク。SKIP。27必要なフィールドが null の場合は、絶対に BUY と答えないでください。28目標価格には絶対に言及しないでください。あなたには分かりません。29"""
12. 1つのコマンドにつなげる
コントラクトアドレスを1つ入れれば、判定が1つ出ます。リミット、ファクト、プール、Grok の順に処理され、最初に「ノー」と言った時点で終了します:
1# check.py2# コントラクトアドレスを1つ入れれば、判定が1つ出る。トークンがデスクを通るたびに実行される。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) # ラグフィルター、無料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"]}3031 try:32 m = pool(mint) # プール、無料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"]}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 failed: {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"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"]}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"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))
これを使うのにスキャナーは不要です。コールチャンネルの CA、投稿、友人からのメッセージなど何でも構いません。ボットに「check <CA>」と伝えれば、1行の実行が走ります。返ってくる結果はこんな感じです(実際のトークンではなく例示用の構造です):
1{2 "verdict": "SKIP",3 "confidence": 0.78,4 "why": "すべてのハードチェックは通過したが、最初の30件の投稿は今週作成されたアカウントからのもので、文言も同一だった。",5 "x_read": "実在アカウントが1つ、新規アカウントの群れ、CAの不一致なし",6 "main_risk": "購入されたハイプ。投稿が止まると買い手は去る",7 "stage": "grok",8 "symbol": "EXAMPLE",9 "reason": "すべてのハードチェックは通過したが、最初の30件の投稿は今週作成されたアカウントからのもので、文言も同一だった。"10}

1意見より先に事実。Grok がミントが開いているかどうかを決めることは決してない。2失敗した呼び出しは SKIP。Rugcheck ダウン、DexScreener ダウン、Grok ダウン、不正な JSON:すべて SKIP。3NULL はクリーンではない。返ってこなかったフィールドはトークンをキルするものであり、通過させるものではない。41トークンにつき1つの判定。BUY が出るまで再実行しない。納得できないなら、それは config.py への不満だ。5BUY ではなく KILL を読め。BUY は気分が良い。だがフィルターが機能しているかは kill の理由が教えてくれる。
13. 3つのルーティン、それぞれ1文
ワークフロービルダーは使いません。各ルーティンを1文でボットに伝えるだけで、次の実行内容が表示されます。以下は私の設定で、一言一句そのままです:
110分ごとに /workspace/desk で python watch.py を実行して。2何も出力しなければ何も言わないこと。何か出力したら、ここに一言一句そのまま投稿して。
130分ごとに /workspace/desk で python scan.py を実行して。2BUY 判定だけを投稿して:symbol、confidence、x_read。3Rugcheck または DexScreener がダウンしていたら、どのサービスかを1回だけ言って。毎回言う必要はない。
1毎日21:30に /workspace/desk で python report.py を実行し、出力をここに投稿して。2今日うまくいったことを1つと、config.py の中で疑問に思う数値を1つ、理由とともに追加して。3その数値を自分で変更してはいけない。
これらのルーティンが実行する2つのファイル。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"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)")
そして 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]: # 1回の実行で5件まで。忙しい1時間で API 枠を使い切らないようにするため15 subprocess.run([sys.executable, "check.py", mint])
今夜の最後:Settings → General → Auto-review。「Ask first」ルールを2つ追加します。これはルーティンがどう判断しようとも、アクションを停止してあなたの確認を待ちます:
1ウォレット接続、署名、スワップ、送金の前には必ず確認を求めること。2config.py という名前のファイルを編集する前には必ず確認を求めること。


14. 1ドルも使う前に1週間はペーパーで
PAPER = True は、お金の動き以外すべて本番通りに行うという意味です。本物のトークン、本物の価格、本物のエグジット、そして偽の約定です。7日経ったら checks.csv を開き、他人が書いたものだと思って読んでみてください。
見るべきポイント:どのチェックが最も多くキルしているか、BUY になったものが本当に自分が買うであろうものか、そして SKIP の理由におかしいものがないか。ペーパー約定は画面価格で成立しますが、本番約定はそうならないため、ペーパーの結果は予測ではなく上限として扱います。
本番に移行するときは PAPER を False に切り替えるだけで、最後のステップ以外は変わりません。check.py は購入せず、代わりに承認行を出力します:自分のウォレットで $25 を購入し、1つのコマンドで台帳に登録すれば、watch.py が監視を開始します。
壊れたとき
お金が関わるため、すべての障害に対して事前に答えを書き留めておきます。
1xAI から 401 -> キーが無効。Take over し、.env を書き換えて、ステップ03の curl を再実行する。2xAI から 400 / 404 -> モデル名が変更された。config.py の MODEL を修正する。他は何もしない。3xAI から 429 -> レート制限。そのトークンは SKIP、次の実行に進む。絶対にループさせない。4x_search に関するエラー -> 使用中の MODEL は X 検索に対応していない。console.x.ai -> Models で対応モデルを選ぶ。5Grok が文章で回答 -> ValueError、ログ記録、SKIP。段落をトレードに変換してはいけない。6rugcheck が 404 または空 -> インデックス化するには新しすぎる。SKIP。新しい=クリーンではない。7すべてで lp_unlocked -> プールの種類によっては LP トークンが存在しないものもある。その数値に触れる前に8 自分で rugcheck で1つ開いて確認し、変更点を明文化すること。9no_pool -> まだ DEX で取引されていないか、Solana ではない。SKIP。10ルーティンが止まった -> 長時間離席すると Grok Bot がルーティンを一時停止する。11 Routines を開いて再度オンにする。
請求額
ジャッジの前まではすべて無料です:リミット、Rugcheck、DexScreener。唯一の有料ステップは、13個すべてのチェックを生き残ったトークンごとに1回呼ぶ 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 により検索は2ラウンドに制限されるため、posts_fetched は小さく抑えられる。6すべてのレスポンスが自身の x_posts_fetched を報告するので、初日に実数が分かる。78価格:[console.x.ai の価格]
多くの人が誤解する点:請求額はスキャナーの数ではなく、生き残った数に比例します。scan.py は1日240トークンをデスクに流し込めますが、そのほとんどは Grok に届く前に無料で死んでいきます。

重要なこと
これは私のセットアップであって、あなたの戦略ではありません。
config.py の数値はすべて私のもので、$25 のチケットと、価値がゼロになるのを眺める自分自身の許容度に合わせて選んだものです。ほとんどのミームコインはゼロになり、それをトレードするほとんどの人はお金を失います。フィルターがトークンを良くするわけではありません。明らかな負け筋を取り除き、なぜそのトークンが死んだのかを読ませるのです。
1週間はペーパーで回してください。kill を読んでください。納得できない数値があれば、明文化した上で変更してください。定数ではなく構造をコピーしてください。財務アドバイスではありません。
コードがキルし、Grok が X を読み、あなたが署名します。スクリプトが判断を下すことはなく、Grok が計算機を持つこともなく、あなた以外がウォレットに触ることもありません。
次に買う前にこれを保存しておいてください。同じものを動かす予定ならフォローしてください。今後もこういう内容を発信していきます 🫶
私の TG - t.me/+8wAXS0nHWgA2MGEy
私の GitHub - github.com/0xboan
ここでトレード - fomo.family/r/0xboan





