polymarket-bot
Bot de paper trading para mercados de predicción de Polymarket: estrategia bayesiana sobre el precio de mercado como prior, señales externas en log-odds, gates de riesgo en cascada y un motor de replay determinista para calibración offline. API FastAPI + dashboard React. Desplegado en k3s vía GitOps (Gitea Actions → registry → ArgoCD), con PostgreSQL, secrets por Infisical y observabilidad completa (Grafana, uptime-kuma, Telegram).
📦 Estado del proyecto: ARCHIVADO (julio 2026). El 26 de mayo de 2026 la DGOJ ordenó el bloqueo cautelar de Polymarket y Kalshi en España por operar sin licencia de juego (CoinDesk). Con el bloqueo a nivel de ISP la fuente de datos primaria desaparece, así que el proyecto se jubila de forma ordenada: datos respaldados y verificados, resultados documentados en el informe final, e infraestructura apagada vía GitOps. El cierre es parte de la historia del proyecto, no un abandono — los módulos centrales son agnósticos de la fuente y el pivote natural es Manifold Markets (API abierta, play-money, sin fricción regulatoria).
El problema y la solución
Los mercados de predicción publican probabilidades implícitas en el precio. La hipótesis: en mercados de bajo volumen hay ineficiencias detectables combinando el precio con señales externas (noticias, sentimiento cripto, mercados espejo en otras plataformas). El bot la pone a prueba sin arriesgar dinero, con la disciplina de un sistema real:
- Prior desde el precio de Polymarket (mid del order book CLOB).
- Ajuste bayesiano en log-odds con señales independientes: GNews (con presupuesto de 5 consultas/día y guardrail que limita el ajuste a prior±0,25), Fear&Greed, momentum BTC, dominancia BTC, y Manifold Markets como señal cruzada (en modo observacional tras detectarse un bug de inversión — auditado en 165k filas).
- Gates en cascada antes de operar: prior extremo (<0,08 / >0,92), edge bruto mínimo por régimen de volatilidad, edge neto positivo tras comisión (2%) y spread, confianza ≥0,55, conflicto de familias, guard de reentrada.
- Agrupación por familias de mercados: mercados hermanos del mismo evento
(candidatos de una misma elección, umbrales de un mismo precio) comparten
family_key; solo se mantiene la posición de mayor edge para no apilar la misma apuesta con dos nombres. - Sizing por Kelly fraccionado con techo por posición sobre un bankroll simulado de $10.000.
Resultado honesto en 81 días: 12 trades (n estadísticamente irrelevante, y el informe lo subraya), realized +$247,78, y — más interesante — ~223.000 evaluaciones archivadas con su decisión reproducible, porque el sistema prefirió no operar antes que operar sin ventaja medible. Detalle completo: docs/informe-final.md.
Arquitectura
flowchart LR
subgraph ext["Fuentes externas"]
PM["Polymarket<br/>CLOB + Gamma API"]
GN["GNews"]
MF["Manifold Markets"]
CG["CoinGecko /<br/>alternative.me"]
end
subgraph k3s["k3s · namespace polymarket-bot"]
BOT["bot (ciclo ~64s)<br/>estrategia bayesiana + gates"]
PG[("PostgreSQL 16<br/>trades · signals · replay")]
API["api (FastAPI :8000)"]
DASH["dashboard (React/nginx)"]
CJ1["CronJob metrics-retention 00:10"]
CJ2["CronJob outcomes-joiner 00:30"]
end
subgraph obs["Observabilidad"]
GRAF["Grafana"]
UK["uptime-kuma"]
TG["Telegram"]
end
PM --> BOT
GN --> BOT
MF --> BOT
CG --> BOT
BOT --> PG
PG --> API
API --> DASH
API --> GRAF
UK --> DASH
BOT --> TG
PM --> CJ2
CJ2 --> PG
CJ1 --> PG
subgraph ci["GitOps"]
GA["Gitea Actions"] --> REG["registry"] --> ARGO["ArgoCD<br/>prune + selfHeal"]
end
ARGO -.-> k3s
Decisiones de diseño
- Paper trading por diseño, no como demo: el bot exigía ≥10 mercados
resueltos y ≥30 días antes de considerarse promocionable a dinero real, y
reportaba
n/a (insufficient_sample)en vez de métricas infladas. - Todo skip es un dato: cada evaluación archiva prior, estimación, edge
bruto/neto, descomposición por feature en log-odds y el gate exacto que la
bloqueó (~55k filas/día en
signals). - Replay determinista (julio 2026): inyección de reloj + re-ejecución de ciclos archivados con 22.697/22.697 decisiones idénticas; joiner diario de resoluciones UMA para calibrar sobre todas las evaluaciones, no solo los trades.
- Los errores se cierran y se documentan: el bug de inversión de Manifold y los conflictos de familia cerraron 5 posiciones en abril; la señal pasó a observacional-only con auditoría completa en vez de eliminarse.
- GitOps estricto: nunca
kubectlpara cambios persistentes (ArgoCD conselfHealrevierte cualquier parche manual); secrets fuera de git vía Infisical operator; smoke test PostSync con notificación a Telegram.
Componentes
| Componente | Código | Imagen | CMD |
|---|---|---|---|
| bot | bot/ |
polymarket-bot |
python3 -m bot.main |
| api | api/ (+ bot/ como librería) |
polymarket-bot-api |
uvicorn api.main:app |
| dashboard | dashboard/ |
polymarket-bot-dashboard |
nginx estático |
Dashboard (hasta el apagado): https://polymarket.chemavx.xyz
Stack
Python 3 (asyncio) · FastAPI · React + Vite · PostgreSQL 16 · k3s · ArgoCD · Gitea Actions + BuildKit · Infisical (secrets) · Grafana + Prometheus · uptime-kuma · Telegram Bot API.
CI/CD
⚠️ Decomisión: el trigger automático
push:mainse desactivó en la Fase 1 del archivado; el workflow queda solo bajoworkflow_dispatchmanual.
.gitea/workflows/ci.yml construye solo las imágenes cuyos ficheros
cambiaron en el push (diff contra github.event.before):
bot/,api/,requirements.txt→ bot + api (ambas imágenes copian las mismas fuentes Python; solo cambia el CMD)Dockerfile→ bot ·Dockerfile.api→ api ·dashboard/→ dashboard.gitea/workflows/ci.yml, primer push o force-push → todas (fallback seguro)tests/, docs → ninguna (la CI no construye ni despliega nada)
Las imágenes se tagean con ${GITHUB_SHA::8}; el CI actualiza solo los
deployments reconstruidos en k8s-manifests/polymarket-bot/ y ArgoCD
sincroniza vía webhook en segundos.
Tests
python3 -m pytest tests/ -q
Datos y backup
Base de datos respaldada y verificada (2026-07-06): pg_dump -Fc + exports
CSV.gz de las tablas de señales y auditoría, con checksums y restore de prueba
(recuentos idénticos 11/11 tablas). Ubicación: almacenamiento de backups del
clúster con sync offsite. El dump final se toma en la fase de apagado.
Documentación
- Informe final de paper trading — resultados, pipeline de datos y estado de la BD al cierre.
docs/pivot-manifold.md— notas de diseño del posible pivote (Fase 5, pendiente).