Compare commits

...
Author SHA1 Message Date
Renovate Bot 8df489ce94 chore(deps): update dependency fastapi to v0.139.1 2026-07-16 12:01:54 +00:00
chemavx dc3b17e10c docs: captura de Grafana (90d) + nota sobre el panel Win Rate vacío
El panel Win Rate estuvo vacío los 81 días por diseño, no por bug: la API
emite win_rate: null hasta >=10 mercados resueltos (hubo 2) y Grafana no
renderiza null. Documentado en el informe final.
2026-07-06 11:45:16 +00:00
chemavxandClaude Fable 5 eef721a9ed docs: README de portfolio — arquitectura, decisiones, resultados y archivado
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>
2026-07-06 07:40:31 +00:00
chemavxandClaude Fable 5 cef8a7f2e1 docs: informe final de paper trading + evidencia visual
Generado desde la BD de producción (snapshot 2026-07-06) con el bot aún
vivo, Fase 1 del plan de decomisión. Realized +$247.78 (n=2, anecdótico
y señalado como tal), MTM de las 5 posiciones abiertas a precios reales
de la Gamma API, y el foco en el activo real: pipeline de señales
(~223k evaluaciones), replay determinista y observabilidad.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 07:40:31 +00:00
chemavxandClaude Fable 5 1dde4d27eb ci: switch trigger to manual workflow_dispatch for decommission docs phase [skip ci]
Phase 1 of the decommission plan produces documentation-only commits;
push:main would rebuild and redeploy all three images on each one.
The workflow is kept (not deleted) and can still be run manually from
the Gitea Actions UI.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 07:24:20 +00:00
chemavx 7804d25c51 Merge pull request 'ci: bump outcomes-joiner CronJob image tag alongside deployment-bot' (#18) from ci/bump-outcomes-cronjob-image into main
CI/CD / build-and-push (push) Successful in 15s
2026-07-02 20:21:58 +00:00
chemavxandClaude Fable 5 0816e19740 ci: bump outcomes-joiner CronJob image tag alongside deployment-bot
The outcomes-joiner CronJob (k8s-manifests, Replay R2) runs the same bot
image; without this its tag would freeze at the sha it was created with
while the deployment moves on. Same sed, one more file.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 20:21:39 +00:00
chemavx 2b326ad54f Merge pull request 'feat(replay): R2 outcomes + calibration metrics' (#17) from feat/replay-r2-outcomes into main
CI/CD / build-and-push (push) Successful in 7s
2026-07-02 20:12:21 +00:00
7 changed files with 315 additions and 8 deletions
+6 -4
View File
@@ -1,9 +1,10 @@
name: CI/CD
# Decommission (2026-07): trigger switched from push:main to manual only, so
# documentation commits don't rebuild/redeploy images. Run from the Gitea UI
# (Actions → Run workflow) if a rebuild is ever needed again.
on:
push:
branches:
- main
workflow_dispatch:
env:
REGISTRY: gitea.gitea.svc.cluster.local:3000
@@ -178,7 +179,8 @@ jobs:
# keep their current (still existing) tag in the registry.
if [ "${{ steps.changes.outputs.build_bot }}" = "true" ]; then
sed -i "s|image: .*polymarket-bot[^-].*|image: git.chemavx.xyz/chemavx/polymarket-bot:${TAG}|g" \
polymarket-bot/deployment-bot.yaml
polymarket-bot/deployment-bot.yaml \
polymarket-bot/cronjob-outcomes.yaml
fi
if [ "${{ steps.changes.outputs.build_api }}" = "true" ]; then
sed -i "s|image: .*polymarket-bot-api.*|image: git.chemavx.xyz/chemavx/polymarket-bot-api:${TAG}|g" \
+137 -3
View File
@@ -1,7 +1,118 @@
# polymarket-bot
Bot de paper-trading para Polymarket con estrategia bayesiana, API FastAPI y
dashboard React. Corre en k3s vía GitOps (Gitea Actions → registry → ArgoCD).
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
@@ -11,10 +122,19 @@ dashboard React. Corre en k3s vía GitOps (Gitea Actions → registry → ArgoCD
| api | `api/` (+ `bot/` como librería) | `polymarket-bot-api` | `uvicorn api.main:app` |
| dashboard | `dashboard/` | `polymarket-bot-dashboard` | nginx estático |
Dashboard: https://polymarket.chemavx.xyz
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`):
@@ -33,3 +153,17 @@ sincroniza vía webhook en segundos.
```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).
Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

+171
View File
@@ -0,0 +1,171 @@
# Informe final de paper trading — polymarket-bot
**Snapshot: 2026-07-06** · Generado desde la base de datos de producción con el bot
aún operativo, como parte de la Fase 1 del plan de decomisión (bloqueo ISP de
Polymarket en España, mayo 2026).
---
## ⚠️ Advertencia de tamaño muestral (léase primero)
**12 trades ejecutados, de los cuales solo 2 llegaron a resolución, no constituyen
evidencia estadística de nada.** Ni el P&L positivo demuestra que la estrategia
funcione, ni su ausencia lo habría refutado. El propio bot lo sabía: sus criterios
de promoción a dinero real exigían ≥10 mercados resueltos y ≥30 días de
observación (`promotion_ready: false`, win rate y Sharpe deliberadamente
reportados como `n/a (insufficient_sample)` en toda la instrumentación).
El valor demostrable de este proyecto no está en el P&L: está en la
infraestructura de evaluación, el pipeline de datos y la disciplina de gates que
impidió que el bot tradease sin ventaja medible. Este informe presenta el P&L
como lo que es — **una anécdota** — y dedica el grueso al sistema.
---
## 1. Resultados de trading (anecdóticos)
Bankroll simulado: **$10.000** (USDC virtual) · Capital desplegado: **$2.447** ·
Periodo de actividad: **1422 de abril de 2026** (los 12 trades se abrieron en 9
días; después, el endurecimiento de los gates de edge y confianza dejó el bot en
0 trades — funcionando, evaluando ~1.350 ciclos/día, sin encontrar ventaja neta
que superara sus propios umbrales).
### P&L realizado: **+$247,78** (2 mercados resueltos)
| Mercado | Dirección | Entrada | Resultado | P&L neto |
|---|---|---|---|---|
| Will Ken Paxton win the 2026 Texas Republican Primary? | BUY_YES | 0,618 | ✅ YES (2026-06-11) | **+$299,06** |
| Will NVIDIA be the largest company in the world…? | BUY_NO | 0,156 | ❌ YES (2026-06-30) | **$51,28** |
P&L neto de comisiones (2% simulado por lado). 1 acierto / 1 fallo: n=2.
### P&L no realizado (mark-to-market): **+$809,59** (5 posiciones abiertas)
Marcado a los precios reales de Polymarket (Gamma API) el 2026-07-06. Estas
posiciones **no están cerradas**: el número de abajo cambiaría cada día que los
mercados sigan moviéndose, y varios no resuelven hasta noviembre de 2026.
| Mercado | Dirección | Entrada | Precio actual (lado) | Coste neto | MTM P&L |
|---|---|---|---|---|---|
| Karen Bass gana la alcaldía de LA 2026 | BUY_YES | 0,268 | 0,595 | $510,00 | **+$600,07** |
| OpenAI IPO antes del 31-dic-2026 | BUY_NO | 0,618 | 0,785 | $510,00 | **+$125,11** |
| Demócratas ganan el Senado de Texas 2026 | BUY_YES | 0,418 | 0,435 | $509,49 | **+$10,32** |
| Demócratas ganan gobernación de Ohio 2026 | BUY_NO | 0,458 | 0,520 | $407,74 | **+$46,12** |
| Republicanos ganan gobernación de Nebraska 2026 | BUY_NO | 0,158 | 0,170 | $510,00 | **+$27,97** |
> Nota metodológica: el tracker interno reportaba `unrealized_pnl_est = +$522,73`,
> pero esa cifra es la **expectativa del propio modelo a fecha de entrada**
> (edge_net × coste), no un marcado a mercado. La tabla de arriba usa precios
> reales de mercado del día del snapshot, que es el criterio honesto. Ambas
> cifras coinciden en el signo; ninguna de las dos es un resultado realizado.
### Los otros 5 cierres (sin P&L): la historia real de abril
De los 7 trades cerrados, solo 2 cerraron por resolución. Los otros 5 los cerró
el propio bot al detectar defectos en su lógica — y son la parte más
instructiva del histórico:
- **3 por conflicto de familia**: dos posiciones sobre mercados hermanos del
mismo evento (p. ej. YES-demócratas y YES-republicanos en la misma
gobernación) — el agrupador de familias los detectó y cerró el lado débil.
- **2 por el bug de inversión de Manifold**: la señal cruzada de Manifold se
aplicaba invertida en mercados espejo. Detectado por instrumentación, las
posiciones afectadas se cerraron y el matching se movió a modo
observacional-only con auditoría completa (165.538 filas en
`manifold_match_audit`).
- **1 excluido de métricas** (`invalid_manifold_match_legacy`, P&L $0,00).
Después de esa semana el bot no volvió a operar: los gates endurecidos
(edge neto > mínimo por régimen, confianza ≥ 0,55, guardrail de noticias
prior±0,25) filtraron el 100% de las ~223.000 evaluaciones posteriores.
**Un sistema de paper trading cuyo resultado principal es "no tradees sin
ventaja" es un resultado**, y está instrumentado hasta el último skip.
---
## 2. El sistema (el activo real)
### Pipeline de señales
| Métrica | Valor (snapshot 2026-07-06) |
|---|---|
| Ciclos de evaluación archivados | **5.356** (cadencia ~64 s) |
| Evaluaciones mercado-ciclo archivadas | **222.738** |
| Mercados distintos evaluados | 87 (events 114,9k · politics 96,0k · crypto/finance 7,7k · tech 4,2k evaluaciones) |
| Archivo `signals` operativo desde | 2026-07-02 (replay R0) — crece ~55k filas/día |
| Histórico de métricas diarias | **81 días** (2026-04-14 → 2026-07-06, `metrics_daily`) |
| Snapshots de contexto externo | 5.356 (BTC, dominancia, Fear&Greed por ciclo) |
Cada evaluación archiva el prior, la probabilidad estimada, el edge bruto/neto,
la descomposición por feature (Fear&Greed, momentum, noticias, Manifold,
dominancia BTC en log-odds), el gate que la bloqueó y el contexto de mercado —
suficiente para reproducir la decisión completa offline.
### Motor de replay (R0R2, julio 2026)
- Inyección de reloj + replay determinista de ciclos archivados:
**22.697/22.697 decisiones idénticas** al reproducir el histórico.
- Joiner diario de resoluciones (CronJob 00:30 UTC) contra la Gamma API:
cobertura de outcomes 9/87 mercados al snapshot, creciendo sola.
- Diseñado para calibrar sobre **todas** las evaluaciones (no solo trades),
esquivando el problema del n=12.
### Estrategia (congelada desde 2026-07-03)
Prior desde el precio de Polymarket → ajuste bayesiano en log-odds con señales
externas (GNews con presupuesto de 5 consultas/día y guardrail prior±0,25;
Fear&Greed; momentum BTC; Manifold observacional) → gates en cascada: prior
extremo, edge bruto por régimen de volatilidad, edge neto tras comisión 2% y
spread, confianza mínima 0,55, conflictos de familia, guard de reentrada.
Sizing por Kelly fraccionado con techo por posición.
### Infraestructura
3 imágenes (bot / API FastAPI / dashboard) construidas por Gitea Actions solo
cuando cambian sus fuentes, desplegadas por ArgoCD (prune + selfHeal) en k3s;
PostgreSQL 16 en StatefulSet; secrets vía Infisical operator; smoke test
PostSync contra la API; backups nocturnos con sync offsite (rclone → MEGA);
observabilidad en Grafana + uptime-kuma + notificaciones Telegram (deploys,
checkpoints del bot, fallos de sync). Retención de métricas y join de outcomes
como CronJobs. **88 días de uptime del StatefulSet al snapshot.**
Detalle honesto de observabilidad: el panel *Win Rate* del dashboard de Grafana
(`docs/dashboard-grafana-2026-07-06.png`) pasó vacío los 81 días. No es una
query rota: la API emite `win_rate: null` hasta tener ≥10 mercados resueltos
(hubo 2), y Grafana no pinta nada con un null. El panel vacío es el criterio
de promoción del bot aplicándose a su propia monitorización.
---
## 3. Estado de la base de datos al snapshot
| Tabla | Filas | Contenido |
|---|---|---|
| `signals` | 222.549* | Archivo de evaluaciones por ciclo (desde 2026-07-02) |
| `manifold_match_audit` | 165.538 | Auditoría completa del matching Polymarket↔Manifold |
| `replay_decisions` | 22.697 | Decisiones reproducidas por el motor de replay |
| `ext_snapshots` | 5.353* | Contexto externo por ciclo |
| `metrics_daily` | 516 | Cierres diarios (81 días) + snapshots intradía de hoy |
| `markets` | 87 | Universo evaluado |
| `trades` | 12 | Ledger completo de paper trades |
| `market_outcomes` | 9 | Resoluciones UMA-final joineadas |
| `replay_runs` / `manifold_eval_cooldown` / `checkpoint_alerts` | 1 / 76 / 3 | Metadatos |
\* La BD sigue viva; las tablas por ciclo crecen ~55k y ~1,3k filas/día
respectivamente. Backup verificado del 2026-07-06 en
`/data/backups/backups/polymarket-decommission/` (pg_dump -Fc + CSV.gz +
checksums; restore de prueba con recuentos idénticos 11/11 tablas). El dump
final se hará en la Fase 3, justo antes del apagado.
---
## 4. Conclusión
El proyecto se archiva por una causa externa (bloqueo regulatorio del origen de
datos), no por fracaso técnico. Lo que queda: un motor de evaluación bayesiano
determinista y auditable, un pipeline de datos que archivó cada decisión con su
contexto completo, y la evidencia — anecdótica en P&L, sólida en ingeniería —
de que el sistema prefería no operar antes que operar sin ventaja. Todos los
módulos centrales (edge, familias, replay, observabilidad) son agnósticos de la
fuente y quedan listos para un eventual pivote a Manifold
(`docs/pivot-manifold.md`, Fase 5).
Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

+1 -1
View File
@@ -1,7 +1,7 @@
# Core
asyncpg==0.29.0
httpx==0.27.0
fastapi==0.111.0
fastapi==0.139.1
uvicorn[standard]==0.29.0
pydantic==2.7.0