la mayoría de la gente conoce a Grok Bot por la captura 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 pull, 14 comprobaciones, cada una escrita como código. el 13 y el 14 lo ponen en piloto automático, primero en modo simulación (paper).
si leíste mi publicación 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: abre tu Grok Bot, pega todo este artículo y dile "construye este escritorio en /workspace/desk, modo paper, pregúntame antes de que algo toque una wallet". él mismo escribe cada archivo, en su propia computadora.
la regla que debes grabarte: el código mata, Grok lee X, tú firmas. nadie en el escritorio hace el trabajo de otro.
01. consigue cuatro cosas antes de empezar
cuatro cosas. cada una explica qué es, qué haces esta noche y por qué el escritorio la necesita.
1. Grok Bot
qué es. la app donde contratas 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é haces. descárgala en x.ai/bot, inicia sesión, elige Create your own.
por qué. todo el escritorio vive en esa computadora. archivos, rutinas, alertas, todo en un solo lugar que puedes abrir desde el teléfono.
2. una clave API de xAI
qué es. acceso de pago a Grok desde código, con la búsqueda de X integrada.
qué haces. console.x.ai, API Keys, crea una. no la pegues en ningún lado todavía, el paso 03 dice dónde va.
por qué. una rutina no puede sentarse a pensar en cuarenta tokens dentro de 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 gratuitas. Rugcheck lee el contrato: autoridades, LP, holders, insiders, la wallet del desarrollador. DexScreener lee el pool: profundidad, antigüedad, compras contra ventas.
qué haces. nada. sin registro, sin clave para lecturas.
por qué. los hechos van antes que las opiniones. una autoridad de acuñación abierta no es una cuestión de criterio, es un hecho, y los hechos matan primero.
4. una wallet desechable, pero no esta noche
qué es. una wallet de Solana nueva que nunca ha guardado nada más.
qué haces. nada por ahora. la primera semana funciona en papel.
por qué. el escritorio nunca guarda una clave. cuando pases a real, te dirá qué comprar y tú lo firmas.
02. contrata un bot, no ocho
mi escritorio usa ocho bots ahora. no empezó así, y el tuyo tampoco debería. ocho bots el primer día son ocho cosas que depurar a la vez. un bot con un solo trabajo te muestra lo que necesitarán los otros siete más adelante.
elige Create your own y dale esto, palabra por palabra:
1Nombre: RUG DESK2Trabajo: Escritorio de memecoins de Solana, primero en modo paper3Descripción: Construye y ejecuta el escritorio en /workspace/desk a partir del artículo que pegaré.4Cada token pasa por check.py, sin excepciones, sin atajos.5Nunca compras, vendes, firmas ni mueves fondos. Nunca editas config.py a menos que yo lo pida6en este chat. Nunca reduces el valor de un filtro para dejar pasar un token.7Reporta cada eliminación con su motivo, no con tu opinión sobre el token.8Cuando una fuente esté caída, di cuál y omítela. 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 terminará adivinando uno que te cueste dinero.

03. mantén la clave fuera del chat
todo lo que le escribes a un bot queda en un historial. así que la clave va a un archivo, escrita por ti, en la computadora del bot.
abre la computadora del bot, haz clic en Take over, abre la terminal y ejecuta esto. pide la clave sin mostrarla en pantalla, así que 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
luego demuestra que la clave funciona antes de cualquier otra cosa. una sola llamada, para que después 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" ...
haz clic en I'm done y devuélvele la computadora. de aquí en adelante el bot puede ejecutar el escritorio, y aun así nunca ve la clave en el chat.

04. visualiza todo el embudo antes de construir nada
ocho pasadas. las gratuitas van primero. solo lo que sobrevive a las pasadas gratuitas cuesta una llamada a Grok, y solo lo que Grok marca como BUY cuesta dinero. esta semana, ni siquiera eso.
10. CANDIDATO un CA de cualquier parte: una llamada, un post, scan.py -> una acuñación21. LÍMITES book.py: pérdida diaria, slots abiertos, cooldown -> gratis, sin red en absoluto32. HECHOS rugcheck.py: 10 eliminaciones duras -> mint abierta, LP desbloqueada, top 10 concentrado mueren aquí43. POOL market.py: profundidad, antigüedad, ventas -> pools finos y honeypots mueren aquí54. JUEZ grok.py + búsqueda en X, una llamada -> BUY o SKIP, un motivo, un riesgo65. EJECUCIÓN paper: book.py al precio real -> real: lo firmas tú mismo76. VIGILANCIA watch.py cada 10 minutos -> stop, escalonado, trailing, stop por tiempo87. REPORTE report.py a las 21:30 -> qué murió, qué pagó, qué cuestionar
una autoridad de acuñación abierta no es una cuestión de criterio. es un hecho, y los hechos son gratis. Grok solo ve tokens que ya pasaron todas las comprobaciones que un script puede hacer.

05. pon todos los números en un solo archivo
guárdalo como config.py. todos los números del escritorio viven aquí, así que cuando reajustes, editas un solo archivo y nada más. estos son los míos, ajustados para una entrada de $25. no son una recomendación, son un punto de partida que vas a modificar.
1# config.py2# todos los números del escritorio viven aquí. cuando reajustes, editas este archivo y nada más.34MODEL = "grok-4.7" # revisa console.x.ai -> Models si la API lo rechaza5PAPER = True # semana uno: ejecuciones falsas a precios reales. cámbialo 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 hoy llega a esto -> no hay nuevas entradas hasta mañana11COOLDOWN_HOURS = 24 # no volver a entrar a un token que acabas de dejar1213FACTS = { # el filtro de rug pull. solo código, sin modelo14 "max_risk_score": 40, # score_normalised de rugcheck, 0-100, mayor = 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, # suministro que aún está 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 entrada nunca es más del 1% del pool21 "min_age_min": 30, # más joven que esto, todo 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 loss duro desde la entrada29 "ladder": [(100, 0.50), (200, 0.25)], # +100% vende la mitad, +200% vende un cuarto30 "trail_pct": 30, # el resto: salida cuando el precio cae 30% desde su máximo31 "time_stop_hours": 6, # si no sube 20% tras 6 horas -> fuera, dinero muerto32 "time_stop_min_pct": 20,33 "liq_drop_pct": 40, # si la liquidez del pool cae 40% desde la entrada -> fuera de inmediato34}3536LOG = "checks.csv"37POSITIONS = "positions.json"38TRADES = "trades.csv"
1ENTRADA FIJA. el tamaño nunca crece porque las últimas tres ganaron.2EL BOT LEE config.py, NUNCA LO ESCRIBE. un número cambia cuando tú lo cambias.3UN FILTRO SE RELAJA POR ESCRITO. si cambias un número, explicas por qué en el chat, el mismo día.
06. ejecuta los límites antes que cualquier otra cosa
guárdalo como book.py. la comprobació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 puedes abrir tú mismo, positions.json y trades.csv.
1# book.py2# posiciones, operaciones, límites. la memoria del escritorio vive en dos archivos que puedes abrir tú 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 se ejecutan 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 manualmente -> 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. escribe la salida antes que la entrada
la mayoría decide cuándo vender mientras mira fijamente 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 comprueba cada 10 minutos:
- liquidez retirada 40% desde la entrada: fuera, todo, sin preguntas
- baja 35%: fuera
- sube 100%: la mitad se va, el resto es dinero 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 máximo
- seis horas sin +20%: fuera, el slot vale más que la esperanza
guárdalo 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, reintentaré")15 continue16 if not m or m["price"] <= 0:17 events.append(f"{p['symbol']}: pool desaparecido. tratar como rug pull, vende 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 "VENDE 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 TRAS LA PRIMERA TOMA DE GANANCIAS. antes de eso, -35% es el único piso.3EL DINERO MUERTO TAMBIÉN ES UNA PÉRDIDA. seis horas planas liberan el slot para el siguiente token.4EL SILENCIO ES UN ESTADO. watch.py no imprime nada si nada cambió, así que un chat silencioso significa que no tienes que mirar.

08. aprende las 14 formas en que muere un token
si tuviera que tirar todo lo anterior y quedarme con una sola cosa, sería esto. además funciona sin ningún bot: haz las mismas 14 comprobaciones a mano en rugcheck.xyz y dexscreener antes de tu próxima compra y evitarás la mayoría de los tokens de los que ibas a arrepentirte.
13 de las comprobaciones son código. comparaciones, sin modelo, sin opinión. la 14.ª es la única que necesita a Grok.
#
comprobación
mata cuando
por qué
1
ya es rug pull
Rugcheck lo marca como rugged
alguien ya lo hizo
2
autoridad de acuñación
sigue abierta
el desarrollador puede imprimir nuevo suministro en tu bolsa
3
autoridad de congelación
sigue abierta
el desarrollador puede congelar tus tokens, no puedes vender
4
banderas de peligro
cualquier riesgo de nivel danger
Rugcheck ya vio algo que tendrías que demostrar que está mal
5
puntuación de riesgo
por encima de 40/100
las pequeñas advertencias 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 salida
7
top 10 de holders
más del 30%, excluyendo pools y lockers
diez wallets pueden acabar con tu operación juntas
8
wallet del desarrollador
el creador aún tiene más del 5%
el desarrollador es el primer vendedor
9
insiders / paquetes
cualquier holder insider o coincidencia en el grafo de insiders
las wallets financiadas juntas venden juntas
10
profundidad del pool
menos de $30k de liquidez
serías la salida de alguien
11
peso de tu entrada
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 ocurrió
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 rug pulls
la parte que ninguna API devuelve

09. deja que el código mate a los obvios
comprobaciones 1 a 9. obtiene un solo reporte y conserva únicamente los campos que el escritorio lee. Grok nunca ve el reporte crudo, un payload pesado cuesta tokens y cuesta precisión.
un detalle que la gente pasa por alto: la bóveda del propio pool y los lockers aparecen en la lista de principales holders. si los cuentas, todo 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. comprueba el pool, no solo el contrato
comprobaciones 10 a 13. un contrato puede ser perfecto y el pool aun así no dejarte salir. esto lee el pool más profundo del token y hace cuatro preguntas aburridas: ¿hay suficiente en él?, ¿qué tan grande es mi entrada comparada?, ¿qué antigüedad tiene? y ¿alguien está vendiendo realmente?
1# market.py2# el pool en sí: profundidad, antigüedad, precio y si alguien puede vender de verdad.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. deja que Grok lea lo que el código no puede
comprobación 14. esta es la razón por la que el escritorio funciona con Grok y no con ningún otro modelo: puede buscar en X mientras piensa. un token puede pasar las 13 comprobaciones y seguir siendo una copia con una dirección de contrato distinta, o un lanzamiento donde cuarenta cuentas creadas la semana pasada publicaron la misma frase en el mismo minuto. ninguna API devuelve eso. X sí.
guárdalo 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 contiene 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 comprobaciones estrictas en el código:6autoridades, LP, holders, wallet del dev, insiders, profundidad del pool, antigüedad.7No vuelvas a comprobar esos números. Tú decides si entra un ticket fijo.8Nunca calculas 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 juntos15 - quién publicó primero: el dev, una cuenta real con historial o una oleada 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 cruza 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 NECESITA 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 devuelve se ve así (forma de ejemplo, no un token real):
1{2 "verdict": "SKIP",3 "confidence": 0.78,4 "why": "pasa todas las comprobaciones estrictas, pero las primeras 30 publicaciones vinieron de cuentas creadas esta semana, con las mismas palabras.",5 "x_read": "una cuenta real, una oleada 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 comprobaciones 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 incorrecto: SKIP.3NULL NO SIGNIFICA LIMPIO. un campo que no regresó elimina el 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 sientan bien. los motivos de eliminación te dicen si el filtro funciona.
13. tres rutinas, una frase cada una
sin constructor de flujos de trabajo. le dices al bot cada rutina una vez, en una frase, 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í.2Añade 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 informe 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"INFORME NOCTURNO {day} {'(PAPEL)' if PAPER else '(REAL)'}")19print(f"comprobados: {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 de pago 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 de pago 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 intensa no agote el presupuesto de la API15 subprocess.run([sys.executable, "check.py", mint])
última cosa de esta noche: Settings, General, Auto-review. añade 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 desconocido.
lo que buscas: qué comprobació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 la previsión.
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 incorrecta. 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 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 cotiza en un DEX, o no es Solana. SKIP.10las rutinas se silenciaron -> Grok Bot pausa las rutinas después de que lleves 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 comprobaciones, con la búsqueda en X activada.
1costo_por_token = tokens_entrada / 1_000_000 * precio_por_M_entrada2 + tokens_salida / 1_000_000 * precio_por_M_salida3 + publicaciones_obtenidas / 1_000 * 5 # búsqueda en X: $5 por cada 1,000 publicaciones obtenidas45max_turns = 2 mantiene la búsqueda en dos rondas, así que publicaciones_obtenidas se mantiene bajo.6cada respuesta informa sus propios x_posts_fetched, así que el primer día te da el número real.78precio: [precio de console.x.ai]
la parte en la que la gente se equivoca: la factura escala con los supervivientes, no con el escáner. scan.py puede alimentar al desk con 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.
ejecútalo 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. no es consejo financiero.
el código elimina, Grok lee X, tú firmas. un script nunca toma decisiones de juicio, Grok nunca recibe una calculadora, y nadie más que tú llega a la wallet.
guarda esto antes de tu próxima compra. sígueme si planeas montar lo mismo, habrá más de esto 🫶
mi tg - t.me/+8wAXS0nHWgA2MGEy
mi github - github.com/0xboan
opera aquí - fomo.family/r/0xboan





