Reescritura completa como pieza de portfolio: problema/solución, diagrama Mermaid de arquitectura, decisiones de diseño, stack y sección de estado del proyecto (archivado por el bloqueo regulatorio de la DGOJ a Polymarket en España, mayo 2026, con enlaces a las fuentes). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
170 lines
7.2 KiB
Markdown
170 lines
7.2 KiB
Markdown
# 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](https://www.ordenacionjuego.es/novedades/dgoj-abre-expediente-sancionador-plataformas-polymarket-kalshi-ordena-bloqueo-sus-webs)
|
|
> por operar sin licencia de juego ([CoinDesk](https://www.coindesk.com/policy/2026/05/26/spain-joins-growing-list-of-countries-shutting-out-polymarket-and-kalshi)).
|
|
> 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](docs/informe-final.md), 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:
|
|
|
|
1. **Prior desde el precio** de Polymarket (mid del order book CLOB).
|
|
2. **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).
|
|
3. **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.
|
|
4. **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.
|
|
5. **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](docs/informe-final.md).
|
|
|
|
## Arquitectura
|
|
|
|
```mermaid
|
|
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 `kubectl` para cambios persistentes (ArgoCD con
|
|
`selfHeal` revierte 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:main` se desactivó en la Fase 1
|
|
> del archivado; el workflow queda solo bajo `workflow_dispatch` manual.
|
|
|
|
`.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
|
|
|
|
```bash
|
|
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](docs/informe-final.md) — 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).
|