большинство людей знакомятся с Grok Bot через чужой скриншот pnl, открывают его, наводят на график и теряют первые $50 на токене, который умер еще до того, как они его купили. проблема была не в боте. ему просто никто не объяснил, как выглядит смерть.
вот сетап, который я бы собрал заново сегодня вечером, если бы пришлось начинать с нуля. один бот, одна папка, десять небольших файлов. он не охотится за 100x. его главная задача — убивать токены, а те немногие, что выживают, всё равно требуют, чтобы Grok прочитал X, прежде чем сдвинется хоть один доллар.
шаги с 01 по 07 — это первый вечер: бот, ключ, лимиты, выходы. с 08 по 12 — фильтр рагпулов, 14 проверок, каждая из которых прописана кодом. 13 и 14 переводят всё на автопилот, сначала только на бумаге.
если вы читали мой пост про стол на восемь агентов, то вот тот фундамент, на котором этот стол стоит.
https://x.com/0xboan/status/2092226208234647626
как этим пользоваться: откройте свой Grok Bot, вставьте всю эту статью целиком и скажите «собери этот стол в /workspace/desk, режим paper mode, спрашивай меня, прежде чем что-то коснется кошелька». он сам напишет каждый файл на своем собственном компьютере.
разделение, которого нужно придерживаться: код убивает, Grok читает X, ты подписываешь. никто за столом не делает чужую работу.
01. подготовь четыре вещи до начала
четыре вещи. для каждой указано, что это, что с ней делать сегодня вечером и зачем она нужна столу.
1. Grok Bot
что это. приложение, где ты нанимаешь именных ботов. каждый работает на облачном компьютере с браузером, файлами и терминалом, и продолжает работать, даже когда твой ноутбук закрыт.
что делать. скачай на x.ai/bot, войди, выбери Create your own.
зачем. весь стол живет на этом компьютере. файлы, рутины, алерты — всё в одном месте, которое можно открыть с телефона.
2. API-ключ xAI
что это. платный доступ к Grok из кода со встроенным поиском по X.
что делать. console.x.ai, API Keys, создай один. пока никуда его не вставляй, в шаге 03 сказано, куда он идет.
зачем. рутина не может сидеть и размышлять о сорока токенах в чате. скрипт, делающий один вызов на токен, может, и он логирует каждый ответ в одном формате.
3. Rugcheck и DexScreener
что это. два бесплатных публичных API. Rugcheck читает контракт: права (authorities), LP, холдеров, инсайдеров, кошелек дева. DexScreener читает пул: глубину, возраст, покупки против продаж.
что делать. ничего. никакой регистрации, никаких ключей для чтения.
зачем. факты идут раньше мнений. открытый mint authority — это не вопрос оценки, это факт, а факты убивают первыми.
4. одноразовый кошелек, но не сегодня
что это. свежий Solana-кошелек, на котором никогда ничего не лежало.
что делать. пока ничего. первая неделя проходит на бумаге.
зачем. стол никогда не хранит ключ. когда перейдешь в лайв, он скажет тебе, что покупать, а подпишешь ты сам.
02. найми одного бота, а не восьмерых
мой стол сейчас гоняет восемь ботов. так было не с самого начала, и у тебя не должно быть. восемь ботов в первый день — это восемь вещей, которые придется дебажить одновременно. один бот с одной задачей покажет, что понадобится остальным семерым позже.
выбери Create your own и дай ему это, слово в слово:
1Имя: RUG DESK2Задача: торговый стол для Solana-мемкоинов, сначала на бумаге3Описание: Собери и запусти стол в /workspace/desk по статье, которую я вставлю.4Каждый токен проходит через check.py, без исключений и коротких путей.5Ты никогда не покупаешь, не продаешь, не подписываешь и не перемещаешь средства. Ты никогда не редактируешь config.py, если я не попрошу6в этом чате. Ты никогда не занижаешь параметры фильтров, чтобы пропустить токен.7Сообщай о каждом убийстве с указанием причины, а не своего мнения о токене.8Если источник упал, скажи какой именно и пропусти. Никогда не угадывай недостающее число.
последние две строки важнее первых пяти. бот, который угадывает недостающие числа, рано или поздно угадает то, что будет стоить тебе денег.

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
затем убедись, что ключ работает, прежде чем делать что-то еще. один вызов, чтобы потом никогда не путать плохой ключ с плохим промптом:
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. посмотри на всю воронку до того, как начнешь её строить
восемь проходов. бесплатные запускаются первыми. только то, что переживет бесплатные проверки, стоит вызова Grok, и только то, чему Grok скажет BUY, стоит денег. на этой неделе — даже не это.
10. ЛИД CA откуда угодно: звонок, пост, scan.py -> один минт21. ЛИМИТЫ book.py: дневной убыток, открытые слоты, кулдаун -> бесплатно, вообще без сети32. ФАКТЫ rugcheck.py: 10 жестких убийств -> открытый минт, разблокированный LP, жирный топ-10 умирают здесь43. ПУЛ market.py: глубина, возраст, продажи -> тонкие пулы и ханипоты умирают здесь54. СУДЬЯ grok.py + поиск по X, один вызов -> BUY или SKIP, одна причина, один риск65. ИСПОЛНЕНИЕ бумага: book.py по живой цене -> лайв: ты подписываешь сам76. НАБЛЮДЕНИЕ watch.py каждые 10 минут -> стоп, лесенка, трейлинг, временной стоп87. ОТЧЕТ report.py в 21:30 -> что умерло, что заплатило, что вызывает вопросы
открытый mint authority — это не вопрос оценки. это факт, а факты бесплатны. Grok видит только те токены, которые уже прошли все проверки, доступные скрипту.

05. помести все числа в один файл
сохрани как config.py. каждое число на столе живет здесь, поэтому при перенастройке ты редактируешь один файл и больше ничего. это мои настройки, заточенные под тикет в $25. они не рекомендация, а стартовая точка, которую ты будешь двигать.
1# config.py2# каждое число на столе живет здесь. при перенастройке ты редактируешь только этот файл.34MODEL = "grok-4.7" # проверь console.x.ai -> Models, если API его отклонит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 = { # фильтр рагпулов. только код, без модели14 "max_risk_score": 40, # score_normalised из rugcheck, 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, # нет +20% через 6 часов -> выход, мертвые деньги32 "time_stop_min_pct": 20,33 "liq_drop_pct": 40, # ликвидность пула упала на 40% от входа -> немедленный выход34}3536LOG = "checks.csv"37POSITIONS = "positions.json"38TRADES = "trades.csv"
1ФИКСИРОВАННЫЙ ТИКЕТ. размер никогда не растет из-за того, что последние три сделки были плюсовыми.2БОТ ЧИТАЕТ 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 = всё, что осталось). Возвращает реализованный 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"] -= 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%: уходит еще четверть
- после первого тейка остаток выходит, когда цена падает на 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']}: пул исчез. считаем рагпулом, продавай что можешь")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 = "продано" if PAPER else "ПРОДАВАЙ СЕЙЧАС в своем кошельке"30 size = "остаток" if frac >= 1 else f"{int(frac * 100)}%"31 events.append(f"{s['symbol']}: {verb} {size} на {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 способов смерти токена
если бы мне пришлось выбросить всё вышеперечисленное и оставить одну вещь, это была бы она. она работает и без всяких ботов: пройдись вручную по тем же 14 проверкам на rugcheck.xyz и dexscreener перед следующей покупкой, и ты пропустишь большинство токенов, о которых собирался пожалеть.
13 проверок — это код. сравнения, без модели, без мнений. 14-я — единственная, которой нужен Grok.
#
проверка
убивает, когда
почему
1
уже рагнули
Rugcheck помечает его как rugged
кто-то уже сделал это
2
mint authority
всё ещё открыт
дев может напечатать новую эмиссию прямо в твой бэг
3
freeze authority
всё ещё открыт
дев может заморозить твои токены, ты не сможешь продать
4
флаги опасности
любой риск уровня danger
Rugcheck уже увидел то, что тебе пришлось бы опровергать
5
risk score
выше 40/100
мелкие предупреждения складываются
6
блокировка LP
крупнейший пул заблокирован или сожжен менее чем на 90%
разблокированная ликвидность — это кнопка для рагпула
7
топ-10 холдеров
больше 30%, пулы и блокировщики исключены
десять кошельков могут вместе закрыть твою сделку
8
кошелек дева
создатель всё ещё держит больше 5%
дев — первый продавец
9
инсайдеры / бандлы
любой холдер-инсайдер или совпадение в графе инсайдеров
кошельки, профинансированные вместе, продают вместе
10
глубина пула
ликвидность меньше $30k
ты станешь чьим-то экзитом
11
доля тикета
твои $25 — это больше 1% пула
твоя собственная покупка сдвигает цену
12
возраст
меньше 30 мин или больше 72ч
слишком свежий для любого отчета, либо движение уже прошло
13
есть ли продажи
20+ покупок за последний час, ноль продаж
это форма ханипота
14
чтение X (Grok)
нет постов, скопированный CA, купленный хайп, история рагпулов
та часть, которую не вернет ни один API

09. позволь коду убить очевидных
проверки с 1 по 9. он тянет один отчет и оставляет только те поля, которые читает стол. Grok никогда не видит сырой отчет: тяжелый пейлоад жрет токены и снижает точность.
одна деталь, которую люди упускают: собственный сейв пула и блокировщики сидят в списке топ-холдеров. посчитай их — и любой токен покажется китовым. код выкидывает их до того, как суммировать топ-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# единственный файл, в котором хранится ключ, и единственное место, где деск общается с 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 """Один вызов: 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"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полномочия (authorities), LP, холдеров, кошелёк дева, инсайдеров, глубину пула, возраст.7Не перепроверяй эти цифры. Ты решаешь, зайдёт ли один плоский тикет.8Ты никогда не определяешь размер позиции и никогда не выбираешь точки выхода.910ВХОДНЫЕ ДАННЫЕ: {"token": {...факты из rugcheck...}, "pool": {...факты из dexscreener...}}1112ШАГ 1. Найди в X тикер и адрес контракта за последние 72 часа.13ШАГ 2. Оценивай только то, чего код не видит:14 - складываются ли риски уровня warn во что-то более серьёзное вместе15 - кто запостил первым: дев, реальный аккаунт с историей или волна свежих аккаунтов16 - органика или покупка: одинаковые формулировки, одна и та же минута, аккаунты без прошлого17 - совпадает ли адрес контракта в этих постах с тем, что в ВХОДНЫХ ДАННЫХ18 - был ли этот тикер или этот дев замешан в раге раньше1920ВОЗВРАЩАЙ ТОЛЬКО JSON:21{"verdict": "BUY" | "SKIP", "confidence": 0.0-1.0,22 "why": "максимум две строки", "x_read": "одна строка о том, как выглядит X",23 "main_risk": "одна строка"}2425Ноль постов в X — это тоже ответ: никто не знает, что он существует. SKIP.26Другой адрес контракта под тем же тикером в X: риск копии. SKIP.27НИКОГДА не отвечай BUY, если нужное тебе поле равно null.28НИКОГДА не упоминай ценовые цели. Ты их не знаешь.29"""
12. собираем всё в одну команду
один адрес контракта на входе, один вердикт на выходе. лимиты, факты, пул, Grok — именно в таком порядке, и первый отказ всё останавливает:
1# check.py2# один адрес контракта на входе, один вердикт на выходе. запускается каждый раз, когда токен попадает на твой деск.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{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 никогда не решает, открыт ли минт.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"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 — необязательный источник лидов. он тянет свежие токены Solana, которые только что оплатили профиль на DexScreener. оплаченный профиль — не показатель качества, он лишь значит, что дев потратил деньги. остальное решит фильтр:
1# scan.py2# необязательный источник лидов: свежие токены Solana, только что оплатившие профиль на DexScreener.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]: # по пять за запуск, чтобы один активный час не съел весь бюджет API15 subprocess.run([sys.executable, "check.py", mint])
последнее на сегодня: Settings, General, Auto-review. добавь два правила Ask first (спрашивать сначала) — они остановят действие и будут ждать тебя независимо от того, что думает любая рутина:
1Спрашивай перед любым подключением кошелька, подписью, свопом или переводом.2Спрашивай перед редактированием любого файла с именем config.py.


14. неделя на бумаге до первого доллара
PAPER = True означает, что деск делает всё по-настоящему, кроме денег. реальные токены, реальные цены, реальные выходы, фейковые исполнения. через семь дней открой checks.csv и прочитай его так, будто его писал незнакомец.
что искать: какая проверка убивает чаще всего, купил бы ты сам те токены, где вышел BUY, и не выглядят ли ошибочными причины SKIP. бумажное исполнение происходит по цене на экране, реальное — никогда, поэтому считай бумажный результат потолком, а не прогнозом.
когда перейдёшь в лайв, переключи PAPER на False, и ничего не изменится, кроме последнего шага. check.py перестаёт покупать и вместо этого выдаёт строку подтверждения: купи $25 сам в своём кошельке, а затем одной командой занеси это в книгу, чтобы watch.py начал его охранять.
когда всё ломается
здесь дело касается денег, поэтому для каждого сбоя ответ должен быть записан до того, как он случится.
1401 от xAI -> плохой ключ. Перехвати управление, перепиши .env, снова запусти curl из шага 03.2400 / 404 от xAI -> изменилось имя модели. поправь MODEL в config.py, больше ничего.3429 от xAI -> rate limit. этот токен — SKIP, следующий запуск идёт дальше. никогда не зацикливай.4ошибка про x_search -> твой MODEL не умеет искать в X. выбери тот, что умеет, в console.x.ai -> Models.5Grok отвечает текстом -> ValueError, логируется, SKIP. никогда не превращай абзац в сделку.6rugcheck 404 или пусто -> слишком свежий для индекса. SKIP. свежее не значит чистое.7lp_unlocked на всём -> у некоторых типов пулов вообще нет LP-токена. открой такой на rugcheck сам,8 прежде чем трогать эту цифру, и меняй её осознанно.9no_pool -> ещё не торгуется на DEX или не Solana. SKIP.10рутины замолчали -> Grok Bot ставит рутины на паузу, если тебя долго не было.11 открой Routines и включи их обратно.
счёт
всё до судьи бесплатно: лимиты, Rugcheck, DexScreener. единственный платный шаг — один вызов Grok на токен, прошедший все 13 проверок, с включённым поиском по 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: $5 за 1000 найденных постов45max_turns = 2 ограничивает поиск двумя раундами, поэтому posts_fetched остаётся небольшим.6каждый ответ сообщает свой x_posts_fetched, так что уже в первый день ты увидишь реальную цифру.78цена: [цена из console.x.ai]
в чём люди ошибаются: счёт растёт от выживших, а не от сканера. scan.py может подавать на деск 240 токенов в день, и большинство из них умрут бесплатно, прежде чем Grok вообще о них узнает.

важная часть
это мой сетап, а не твоя стратегия.
каждая цифра в config.py — моя, подобранная под тикет в $25 и мою личную готовность смотреть, как что-то уходит в ноль. большинство мемкоинов действительно уходят в ноль, и большинство тех, кто ими торгует, теряют деньги. фильтр не делает токен хорошим. он убирает очевидные способы потерять деньги и заставляет тебя читать, почему умер каждый токен.
погоняй неделю на бумаге. читай отказы. меняй цифры там, где не согласен, и делай это осознанно. копируй структуру, а не константы. не финансовая рекомендация.
код убивает, Grok читает X, подписываешь ты. скрипт никогда не принимает решений на своё усмотрение, Grok никогда не получает калькулятор, и никто, кроме тебя, не получает доступ к кошельку.
сохрани это перед следующей покупкой. подписывайся, если планируешь запустить то же самое, такого контента будет больше 🫶
мой tg - t.me/+8wAXS0nHWgA2MGEy
мой github - github.com/0xboan
торгуй здесь - fomo.family/r/0xboan





