la mayoría de la gente conoce a Grok Bot por la captura de pantalla de PnL de otra persona, lo abre, lo apunta a un gráfico y pierde los primeros $50 en un token que ya estaba muerto antes de comprarlo. el bot no fue el problema. nadie le explicó cómo se ve algo muerto.
esta es la configuración que volvería a armar esta misma noche si tuviera que empezar de cero. un bot, una carpeta, diez archivos pequeños. no sale a cazar 100x. su trabajo principal es matar tokens, y los pocos que sobreviven igual necesitan que Grok lea X antes de mover un solo dólar.
los pasos del 01 al 07 son la primera noche: el bot, la clave, los límites, las salidas. del 08 al 12 son el filtro de rug pulls, 14 verificaciones, cada una escrita como código. el 13 y el 14 lo ponen en piloto automático, primero en modo paper (simulado).
si leíste mi post sobre el escritorio de ocho agentes, este es el piso sobre el que se sostiene ese escritorio.
https://x.com/0xboan/status/2092226208234647626
cómo usar esto: abrí tu Grok Bot, pegá todo este artículo y decile "armá este escritorio en /workspace/desk, modo paper, preguntame antes de que algo toque una wallet". escribe todos los archivos por su cuenta, en su propia computadora.
la división que tenés que grabarte: el código mata, Grok lee X, vos firmás. nadie en el escritorio hace el trabajo de otro.
01. conseguí cuatro cosas antes de empezar
cuatro cosas. cada una explica qué es, qué hacés esta noche y por qué el escritorio la necesita.
1. Grok Bot
qué es. la app donde contratás bots con nombre propio. cada uno trabaja en una computadora en la nube con navegador, archivos y terminal, y sigue funcionando aunque tengas la laptop cerrada.
qué hacés. descargalo en x.ai/bot, iniciá sesión, elegí Create your own (Crear el tuyo).
por qué. todo el escritorio vive en esa computadora. archivos, rutinas, alertas, todo en un solo lugar que podés abrir desde el celular.
2. una API key de xAI
qué es. acceso pago a Grok desde código, con la búsqueda de X integrada.
qué hacés. console.x.ai, API Keys, creá una. todavía no la pegues en ningún lado, el paso 03 dice dónde va.
por qué. una rutina no puede sentarse a pensar sobre cuarenta tokens en un chat. un script que hace una llamada por token sí puede, y registra cada respuesta en el mismo formato.
3. Rugcheck y DexScreener
qué es. dos APIs públicas y gratuitas. Rugcheck lee el contrato: autoridades, LP, holders, insiders, la wallet del dev. DexScreener lee el pool: profundidad, antigüedad, compras contra ventas.
qué hacés. nada. sin registro, sin clave para lecturas.
por qué. los hechos van antes que las opiniones. una autoridad de minteo abierta no es una cuestión de criterio, es un hecho, y los hechos matan primero.
4. una burner wallet, pero no esta noche
qué es. una wallet de Solana nueva que nunca tuvo nada más.
qué hacés. nada todavía. la primera semana corre en modo paper.
por qué. el escritorio nunca guarda una clave. cuando pases a real, te dice qué comprar y lo firmás vos mismo.
02. contratá un bot, no ocho
mi escritorio hoy usa ocho bots. no empezó así, y el tuyo tampoco debería. ocho bots el primer día son ocho cosas para debugear al mismo tiempo. un bot con un solo trabajo te muestra qué van a necesitar los otros siete más adelante.
elegí Create your own y dale esto, palabra por palabra:
1Nombre: RUG DESK2Trabajo: Escritorio de memecoins de Solana, primero en modo paper3Descripción: Armá y ejecutá el escritorio en /workspace/desk a partir del artículo que te voy a pegar.4Cada token pasa por check.py, sin excepciones, sin atajos.5Nunca comprás, vendés, firmás ni movés fondos. Nunca editás config.py salvo que yo te lo pida6en este chat. Nunca bajás el número de un filtro para dejar pasar un token.7Reportá cada eliminación con su motivo, no con tu opinión sobre el token.8Cuando una fuente esté caída, decí cuál y saltala. Nunca adivines un número faltante.
las últimas dos líneas importan más que las primeras cinco. un bot que adivina un número faltante tarde o temprano va a adivinar uno que te va a salir caro.

03. mantené la clave fuera del chat
todo lo que le escribís a un bot queda guardado en una transcripción. por eso la clave va en un archivo, tipeada por vos, en la computadora del bot.
abre la computadora del bot, hacé clic en Take over (Tomar control), abrí la terminal y corré esto. pide la clave sin mostrarla en pantalla, así tampoco aparece en el historial:
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
después comprobá que la clave funciona antes de hacer cualquier otra cosa. una sola llamada, para que más adelante nunca confundas una clave mala con un prompt malo:
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" ...
hacé clic en I'm done (Terminé) y devolvele la computadora. de acá en adelante el bot puede manejar el escritorio, y sigue sin ver la clave en el chat.

04. mirá todo el embudo antes de construir algo
ocho filtros. los gratuitos corren primero. solo lo que sobrevive a los filtros gratis cuesta una llamada a Grok, y solo lo que Grok marca como BUY (comprar) cuesta plata. esta semana, ni siquiera eso.
10. LEAD un CA de cualquier lado: un aviso, un post, scan.py -> un minteo21. LIMITS book.py: pérdida diaria, slots abiertos, cooldown -> gratis, sin usar la red32. FACTS rugcheck.py: 10 eliminaciones duras -> minteo abierto, LP desbloqueado, top 10 pesado mueren acá43. POOL market.py: profundidad, antigüedad, ventas -> pools chicos y honeypots mueren acá54. JUDGE grok.py + búsqueda en X, una llamada -> BUY o SKIP, un motivo, un riesgo65. FILL paper: book.py al precio real -> real: lo firmás vos mismo76. WATCH watch.py cada 10 minutos -> stop, escalera, trailing, stop por tiempo87. REPORT report.py a las 21:30 -> qué murió, qué pagó, qué cuestionar
una autoridad de minteo abierta no es una cuestión de criterio. es un hecho, y los hechos son gratis. Grok solo llega a ver los tokens que ya pasaron todas las verificaciones que un script puede hacer.

05. poné todos los números en un solo archivo
guardalo como config.py. todos los números del escritorio viven acá, así que cuando recalibrás, editás un solo archivo y nada más. estos son los míos, ajustados para tickets de $25. no son una recomendación, son un punto de partida que vas a mover.
1# config.py2# todos los números del escritorio viven acá. cuando recalibrás, editás este archivo y nada más.34MODEL = "grok-4.7" # revisá console.x.ai -> Models si la API lo rechaza5PAPER = True # semana uno: fills falsos a precios reales. cambialo solo después de leer el log67TICKET_USD = 25 # fijo. nunca dimensionado según la última ganancia8MAX_OPEN = 4 # 4 x $25 = $100 en el mercado, máximo9MAX_ENTRIES_PER_DAY = 610DAILY_LOSS_HALT_USD = 50 # si la pérdida realizada de hoy llega a esto -> no hay nuevas entradas hasta mañana11COOLDOWN_HOURS = 24 # no reentrar a un token que acabás de dejar1213FACTS = { # el filtro de rug pulls. solo código, sin modelo14 "max_risk_score": 40, # score_normalised de rugcheck, 0-100, más alto = más riesgoso15 "min_lp_locked_pct": 90, # pool más grande: LP bloqueado o quemado16 "max_top10_pct": 30, # top 10 de holders, excluyendo pools y lockers17 "max_dev_pct": 5, # supply que sigue en la wallet del creador18 "max_insiders": 0, # holders marcados como insider + coincidencias en el grafo de insiders19 "min_liquidity_usd": 30000,20 "max_ticket_share": 0.01, # tu ticket nunca es más del 1% del pool21 "min_age_min": 30, # más joven que esto, cualquier reporte sigue siendo una suposición22 "max_age_hours": 72,23}2425MIN_CONFIDENCE = 0.7 # si Grok dice BUY por debajo de esto -> se trata como SKIP2627EXIT = {28 "stop_pct": -35, # stop duro desde la entrada29 "ladder": [(100, 0.50), (200, 0.25)], # +100% vende la mitad, +200% vende un cuarto30 "trail_pct": 30, # el resto: afuera cuando el precio cae 30% desde su pico31 "time_stop_hours": 6, # si no sube 20% después de 6 horas -> afuera, plata muerta32 "time_stop_min_pct": 20,33 "liq_drop_pct": 40, # si la liquidez del pool cae 40% desde la entrada -> afuera ahora34}3536LOG = "checks.csv"37POSITIONS = "positions.json"38TRADES = "trades.csv"
1TICKET FIJO. el tamaño nunca crece porque las últimas tres operaciones ganaron.2EL BOT LEE config.py, NUNCA LO ESCRIBE. un número se mueve cuando vos lo movés.3UN FILTRO SE AFLOJA POR ESCRITO. si cambiás un número, explicás por qué en el chat, el mismo día.
06. corré los límites antes que cualquier otra cosa
guardalo como book.py. la verificación más barata del escritorio es la que nunca llama a una API. si hoy ya perdiste $50, el escritorio ni siquiera mira el token. si hay cuatro posiciones abiertas, lo mismo. este archivo también es la memoria del escritorio: dos archivos que podés abrir vos mismo, positions.json y trades.csv.
1# book.py2# posiciones, operaciones, límites. la memoria del escritorio vive en dos archivos que podés abrir vos mismo.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 """Los límites corren antes de cualquier llamada de red. Una entrada bloqueada no cuesta nada."""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 """Vende una fracción del tamaño ORIGINAL (1.0 = todo lo que queda). Devuelve el pnl realizado."""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 # modo real: después de comprar en tu propia wallet -> python book.py add <mint> <usd> <price> <liq_usd>66 # después de vender a mano -> 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. escribí la salida antes que la entrada
la mayoría decide cuándo vender mientras mira una vela verde. ese es el peor momento para decidir cualquier cosa. en este escritorio cada salida existe antes de comprar el token, y el bot solo la revisa cada 10 minutos:
- liquidez retirada 40% desde la entrada: afuera, todo, sin preguntas
- baja de 35%: afuera
- sube 100%: la mitad se va, el resto es plata del mercado
- sube 200%: otro cuarto se va
- después de la primera toma de ganancias, el resto sale cuando el precio cae 30% desde su pico
- seis horas sin +20%: afuera, el slot vale más que la esperanza
guardalo como watch.py:
1# watch.py2# cada posición abierta, cada 10 minutos. no imprime nada si nada cambió.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']}: feed de precio caído ({e}), manteniendo, va a reintentar")15 continue16 if not m or m["price"] <= 0:17 events.append(f"{p['symbol']}: pool desapareció. tratar como rugged, vendé lo que puedas")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 = "vendido" if PAPER else "VENDÉ AHORA en tu wallet"30 size = "el resto" if frac >= 1 else f"{int(frac * 100)}%"31 events.append(f"{s['symbol']}: {verb} {size} a {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)
1LIQUIDEZ PRIMERO. un pool vaciado supera cualquier otra regla, incluso en una posición en verde.2EL TRAILING SE ACTIVA DESPUÉS DE LA PRIMERA TOMA. antes de eso, -35% es el único piso.3LA PLATA MUERTA TAMBIÉN ES UNA PÉRDIDA. seis horas planas liberan el slot para el próximo token.4EL SILENCIO ES UN ESTADO. watch.py no imprime nada si nada cambió, así que un chat silencioso significa que no tenés que mirar.

08. aprendé las 14 formas en que un token muere
si tuviera que tirar todo lo anterior y quedarme con una sola cosa, sería esto. además funciona sin ningún bot: hacé las mismas 14 verificaciones a mano en rugcheck.xyz y dexscreener antes de tu próxima compra y vas a esquivar la mayoría de los tokens de los que te ibas a arrepentir.
13 de las verificaciones son código. comparaciones, sin modelo, sin opinión. la 14 es la única que necesita a Grok.
#
verificación
mata cuando
por qué
1
ya está ruggeado
Rugcheck lo marca como rugged
alguien ya lo hizo
2
autoridad de minteo
sigue abierta
el dev puede imprimir nuevo supply directo a tu bolsa
3
autoridad de congelamiento
sigue abierta
el dev puede congelar tus tokens, no podés vender
4
banderas de peligro
cualquier riesgo de nivel danger
Rugcheck ya vio algo que tendrías que demostrar que está mal
5
puntaje de riesgo
arriba de 40/100
las advertencias chicas se acumulan
6
bloqueo de LP
el pool más grande tiene menos del 90% bloqueado o quemado
la liquidez desbloqueada es un botón de retiro
7
top 10 de holders
más del 30%, excluyendo pools y lockers
diez wallets pueden terminar tu operación juntas
8
wallet del dev
el creador sigue teniendo más del 5%
el dev es el primer vendedor
9
insiders / bundles
algún holder insider o coincidencia en el grafo de insiders
las wallets fondeadas juntas venden juntas
10
profundidad del pool
menos de $30k de liquidez
serías la salida de alguien
11
participación del ticket
tus $25 son más del 1% del pool
tu propia compra mueve el precio
12
antigüedad
menos de 30 min o más de 72h
demasiado nuevo para cualquier reporte, o el movimiento ya pasó
13
existen ventas
20+ compras en la última hora, cero ventas
esa es la forma de un honeypot
14
lectura de X (Grok)
sin posts, CA copiado, hype comprado, historial de rugs
la parte que ninguna API devuelve

09. dejá que el código mate a los obvios
verificaciones 1 a 9. trae un solo reporte y se queda únicamente con los campos que el escritorio lee. Grok nunca ve el reporte crudo, un payload gigante gasta tokens y resta precisión.
un detalle que muchos pasan por alto: la bóveda del propio pool y los lockers aparecen en la lista de top holders. si los contás, cualquier token parece un token de ballenas. el código los descarta antes de sumar el top 10.
1# rugcheck.py2# un reporte por token, recortado a los campos que el escritorio lee. Grok nunca ve el reporte crudo.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): # las bóvedas AMM y los lockers están en el top de holders, no son ballenas16 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 """Comparaciones simples. El primer fallo gana, el motivo se registra."""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. revisá el pool, no solo el contrato
verificaciones 10 a 13. un contrato puede ser perfecto y el pool igual no te deja salir. esto lee el pool más profundo del token y hace cuatro preguntas aburridas: ¿hay suficiente adentro?, ¿qué tan grande es mi ticket al lado?, ¿qué tan viejo es?, y ¿alguien está vendiendo de verdad?
1# market.py2# el pool en sí: profundidad, antigüedad, precio y si alguien realmente puede vender.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. dejá que Grok lea lo que el código no puede
verificación 14. esta es la razón por la que el escritorio corre sobre Grok y no sobre cualquier otro modelo: puede buscar en X mientras piensa. un token puede pasar las 13 verificaciones y seguir siendo una copia con otra dirección de contrato, o un lanzamiento donde cuarenta cuentas creadas la semana pasada publicaron la misma frase en el mismo minuto. ninguna API devuelve eso. X sí.
guardalo como grok.py. es el único archivo que lee la clave y el único lugar donde el escritorio habla con xAI:
1# grok.py2# el único archivo que guarda la clave y el único lugar donde el desk se comunica con 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 hay XAI_API_KEY en el entorno ni en .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 """Una sola llamada, entra JSON, sale JSON. Cualquier cosa que no sea JSON lanza un error, y un error es un SKIP aguas arriba."""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 # dos rondas de búsqueda, luego tiene que responder38 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 hay json en la respuesta: {text[:200]}")45 return json.loads(text[start:end + 1])
guárdalo como prompts.py. este es el archivo que realmente vas a releer:
1# prompts.py2# el único prompt de Grok en el desk. este es el archivo que realmente vas a releer.34JUDGE_PROMPT = """5Eres RUG DESK. Una memecoin de Solana acaba de pasar todas las verificaciones estrictas en el código:6autoridades, LP, holders, wallet del dev, insiders, profundidad del pool, antigüedad.7No vuelvas a revisar esos números. Tú decides si entra un ticket fijo.8Nunca defines el tamaño, nunca eliges las salidas.910ENTRADA: {"token": {...datos de rugcheck...}, "pool": {...datos de dexscreener...}}1112PASO 1. Busca en X el ticker y la dirección del contrato, últimas 72 horas.13PASO 2. Juzga solo lo que el código no puede ver:14 - si los riesgos de nivel warn se acumulan para formar algo peor en conjunto15 - quién publicó primero: el dev, una cuenta real con historial o una ola de cuentas nuevas16 - orgánico o comprado: mismas palabras, mismo minuto, cuentas sin pasado17 - si la dirección del contrato en esas publicaciones es la misma que en la ENTRADA18 - si este nombre de ticker o este dev han estado vinculados a un rug antes1920DEVUELVE SOLO JSON:21{"verdict": "BUY" | "SKIP", "confidence": 0.0-1.0,22 "why": "máximo dos líneas", "x_read": "una línea sobre cómo se ve X",23 "main_risk": "una línea"}2425Cero publicaciones en X es una respuesta: nadie sabe que existe. SKIP.26Otra dirección de contrato bajo el mismo ticker en X: riesgo de imitación. SKIP.27NUNCA respondas BUY cuando un campo que necesitabas sea null.28NUNCA menciones objetivos de precio. No los conoces.29"""
12. conéctalo todo en un solo comando
entra una dirección de contrato, sale un veredicto. límites, datos, pool, Grok, en ese orden, y el primero que diga que no lo termina:
1# check.py2# entra una dirección de contrato, sale un veredicto. se ejecuta cada vez que un token llega a tu 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)): # límites, gratis22 return {"stage": "limits", "verdict": "SKIP", "reason": g}2324 try:25 token = report(mint) # filtro de rug, gratis26 except Exception as e:27 return {"stage": "facts", "verdict": "SKIP", "reason": f"rugcheck falló: {e}"}28 if (k := fact_kill(token)):29 return {"stage": "facts", "verdict": "SKIP", "reason": k, "symbol": token["symbol"]}3031 try:32 m = pool(mint) # pool, gratis33 except Exception as e:34 return {"stage": "pool", "verdict": "SKIP", "reason": f"dexscreener falló: {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) # el único paso de pago40 except Exception as e:41 return {"stage": "grok", "verdict": "SKIP", "reason": f"grok falló: {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"confianza baja {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"COMPRA EN PAPEL ${TICKET_USD} a {v['price']}"58 else:59 v["action"] = (f"SE REQUIERE APROBACIÓN: compra ${TICKET_USD} de {v['symbol']} en tu propia wallet, "60 f"luego ejecuta: python book.py add {mint} {TICKET_USD} <precio de llenado> {v['liq_usd']}")61 print(json.dumps(v, indent=2))
no necesitas un escáner para usarlo. un CA de un canal de llamadas, una publicación, el mensaje de un amigo: dile al bot "check <CA>" y ejecuta una línea. lo que regresa se ve así (forma de ejemplo, no un token real):
1{2 "verdict": "SKIP",3 "confidence": 0.78,4 "why": "pasa todas las verificaciones estrictas, pero las primeras 30 publicaciones vinieron de cuentas creadas esta semana, con las mismas palabras.",5 "x_read": "una cuenta real, una ola de cuentas nuevas, ninguna discrepancia de CA",6 "main_risk": "hype comprado, los compradores se van cuando dejan de publicar",7 "stage": "grok",8 "symbol": "EJEMPLO",9 "reason": "pasa todas las verificaciones estrictas, pero las primeras 30 publicaciones vinieron de cuentas creadas esta semana, con las mismas palabras."10}

1DATOS ANTES QUE OPINIONES. Grok nunca decide si un mint está abierto.2UNA LLAMADA FALLIDA ES UN SKIP. Rugcheck caído, DexScreener caído, Grok caído, JSON malo: SKIP.3NULL NO SIGNIFICA LIMPIO. un campo que no regresó elimina al token, nunca lo aprueba.4UN VEREDICTO POR TOKEN. nunca lo vuelvas a ejecutar hasta que diga BUY. si no estás de acuerdo, no estás de acuerdo con config.py.5LEE LOS KILLS, NO LAS COMPRAS. las compras se sienten bien. los motivos de eliminación te dicen si el filtro funciona.
13. tres rutinas, una oración cada una
sin constructor de flujos de trabajo. le dices al bot cada rutina una vez, en una oración, y te muestra la siguiente ejecución. estas son las mías, palabra por palabra:
1Cada 10 minutos, en /workspace/desk ejecuta python watch.py.2Si no imprime nada, no digas nada. Si imprime algo, publícalo aquí palabra por palabra.
1Cada 30 minutos, en /workspace/desk ejecuta python scan.py.2Publica solo los veredictos BUY: symbol, confidence, x_read.3Si Rugcheck o DexScreener están caídos, di cuál una sola vez, no en cada ejecución.
1Todos los días a las 21:30, en /workspace/desk ejecuta python report.py y publica el resultado aquí.2Agrega una cosa que funcionó hoy y un número en config.py que cuestionarías, con el motivo.3Nunca cambies ese número tú mismo.
los dos archivos que ejecutan esas rutinas. guárdalo como report.py:
1# report.py2# el reporte nocturno. todo lo que contiene viene de checks.csv y trades.csv, nada de la memoria.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"REPORTE NOCTURNO {day} {'(PAPEL)' if PAPER else '(REAL)'}")19print(f"revisados: {len(checks)}")20print(f"eliminados por filtro de rug: {by_stage['facts']} | por pool: {by_stage['pool']} | "21 f"por Grok: {by_stage['grok']} | por límites: {by_stage['limits']}")22print(f"comprados: {len(buys)} | abiertos ahora: {len(book.load())}")23print(f"pnl realizado hoy: ${book.today_pnl():+.2f}")24if done:25 print(f"salidas en verde: {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"mejor: {best['symbol']} ${float(best['pnl']):+.2f} | peor: {worst['symbol']} ${float(worst['pnl']):+.2f}")29if reasons:30 top, n = reasons.most_common(1)[0]31 print(f"eliminación más común: {top} ({n}x)")
y scan.py, la fuente opcional de leads. extrae tokens nuevos de Solana que acaban de pagar por un perfil en DexScreener. un perfil pagado no es una señal de calidad, solo significa que el dev gastó dinero. el filtro decide el resto:
1# scan.py2# fuente opcional de leads: tokens nuevos de Solana que acaban de pagar por un perfil en DexScreener.3# un perfil pagado no es una señal de calidad, solo significa que el dev gastó dinero. el filtro decide.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]: # cinco por ejecución, para que una hora ocupada no agote el presupuesto de la API15 subprocess.run([sys.executable, "check.py", mint])
última cosa de esta noche: Settings, General, Auto-review. agrega dos reglas de "Ask first" (preguntar primero); detienen la acción y te esperan sin importar lo que piense cualquier rutina:
1Pregunta primero antes de cualquier conexión de wallet, firma, swap o transferencia.2Pregunta primero antes de editar cualquier archivo llamado config.py.


14. una semana en papel antes de un solo dólar
PAPER = True significa que el desk hace todo de verdad excepto mover dinero. tokens reales, precios reales, salidas reales, ejecuciones falsas. después de siete días, abre checks.csv y léelo como si lo hubiera escrito un extraño.
lo que buscas: qué verificación elimina más, si los BUY habrían sido tokens que tú comprarías, y si algún motivo de SKIP parece incorrecto. una ejecución en papel ocurre al precio de pantalla, una ejecución real nunca lo hace, así que trata el resultado en papel como el techo, no como el pronóstico.
cuando pases a real, cambia PAPER a False y nada más cambia excepto el último paso. check.py deja de comprar y en su lugar publica una línea de aprobación: compra $25 tú mismo en tu propia wallet, luego un comando lo pone en el libro para que watch.py empiece a protegerlo.
cuando falla
esto toca dinero, así que cada fallo tiene una respuesta escrita antes de que ocurra.
1401 de xAI -> clave mala. Toma el control, reescribe .env, ejecuta el curl del paso 03 otra vez.2400 / 404 de xAI -> el nombre del modelo cambió. corrige MODEL en config.py, nada más.3429 de xAI -> límite de tasa. ese token es un SKIP, la siguiente ejecución sigue adelante. nunca lo pongas en bucle.4error sobre x_search -> tu MODEL no puede buscar en X. elige uno que sí pueda en console.x.ai -> Models.5Grok responde en prosa -> ValueError, registrado, SKIP. nunca conviertas un párrafo en una operación.6rugcheck 404 o vacío -> demasiado nuevo para indexarse. SKIP. nuevo no significa limpio.7lp_unlocked en todo -> algunos tipos de pool no tienen token LP en absoluto. abre uno en rugcheck tú mismo8 antes de tocar ese número, y cámbialo por escrito.9no_pool -> aún no se negocia en un DEX, o no es Solana. SKIP.10las rutinas se callaron -> Grok Bot pausa las rutinas después de que llevas mucho tiempo fuera.11 abre Routines y vuelve a activarlas.
la factura
todo lo anterior al juez es gratis: los límites, Rugcheck, DexScreener. el único paso de pago es una llamada a Grok por cada token que sobrevivió a las 13 verificaciones, con la búsqueda en X activada.
1costo_por_token = input_tokens / 1_000_000 * precio_por_M_input2 + output_tokens / 1_000_000 * precio_por_M_output3 + posts_obtenidos / 1_000 * 5 # búsqueda en X: $5 por cada 1,000 posts obtenidos45max_turns = 2 mantiene la búsqueda en dos rondas, así que posts_obtenidos se mantiene bajo.6cada respuesta reporta su propio x_posts_fetched, así que el primer día te dice el número real.78precio: [precio de console.x.ai]
la parte en la que la gente se equivoca: la factura escala con los sobrevivientes, no con el escáner. scan.py puede enviarle al desk 240 tokens al día y la mayoría muere gratis antes de que Grok siquiera se entere de ellos.

parte importante
esta es mi configuración, no tu estrategia.
cada número en config.py es mío, elegido para un ticket de $25 y mi propia tolerancia a ver algo irse a cero. la mayoría de las memecoins se van a cero, y la mayoría de la gente que las opera pierde dinero. el filtro no hace que un token sea bueno. elimina las formas obvias de perder, y te obliga a leer por qué murió cada token.
corre una semana en papel. lee las eliminaciones. mueve los números con los que no estés de acuerdo, por escrito. copia la estructura, no las constantes. esto no es consejo financiero.
el código elimina, Grok lee X, tú firmas. un script nunca toma una decisión de juicio, Grok nunca recibe una calculadora, y nadie más que tú toca la wallet.
guarda esto antes de tu próxima compra. sígueme si planeas correr lo mismo, viene más de esto 🫶
mi tg - t.me/+8wAXS0nHWgA2MGEy
mi github - github.com/0xboan
opera aquí - fomo.family/r/0xboan





