File size: 4,794 Bytes
71b8eb2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | # MARKETS.md — Módulo de mercados
Referencia para el frontend: contrato HTTP, mapping de datos de Polymarket, evento socket y errores.
---
## Variables de entorno requeridas
```
DATABASE_URL=file:./backend/prisma/polysignal.db
PORT=7860
```
No se necesita clave de API para Polymarket Gamma (pública).
---
## Endpoints
### `GET /api/v1/markets`
Lista paginada de mercados activos sincronizados desde Polymarket. **No requiere autenticación.**
**Query params**
| Param | Tipo | Default | Descripción |
|---|---|---|---|
| `limit` | int (1-100) | `20` | Máximo de resultados |
| `offset` | int | `0` | Paginación por offset |
| `category` | string | — | Filtro: `politics` \| `crypto` \| `economics` \| `sports` |
| `status` | string | `active` | Filtro: `active` \| `closed` \| `resolved` |
**Respuesta `200`**
```json
{
"ok": true,
"data": [
{
"id": "559677",
"question": "Will Hillary Clinton win the 2028 Democratic presidential nomination?",
"category": null,
"countryCode": null,
"yesPrice": 0.0075,
"noPrice": 0.9925,
"volumeEur": 38608906.44,
"liquidityEur": 2301398.64,
"status": "active",
"closesAt": "2028-11-07T00:00:00.000Z",
"lastSynced": "2026-05-16T09:38:30.204Z"
}
],
"meta": {
"total": 100,
"limit": 1,
"offset": 0
}
}
```
> `category` y `countryCode` pueden ser `null` si Polymarket no los proporciona.
---
### `GET /api/v1/markets/:id`
Detalle de un mercado por su ID de Polymarket. **No requiere autenticación.**
**Params**
| Param | Tipo | Descripción |
|---|---|---|
| `id` | string | ID numérico de Polymarket (ej. `559677`) |
**Respuesta `200`**
```json
{
"ok": true,
"data": {
"id": "559677",
"question": "Will Hillary Clinton win the 2028 Democratic presidential nomination?",
"category": null,
"countryCode": null,
"yesPrice": 0.0075,
"noPrice": 0.9925,
"volumeEur": 38608906.44,
"liquidityEur": 2301398.64,
"status": "active",
"closesAt": "2028-11-07T00:00:00.000Z",
"lastSynced": "2026-05-16T09:38:30.204Z"
}
}
```
**Respuesta `404`**
```json
{
"ok": false,
"error": { "code": "NOT_FOUND", "message": "Market not found" }
}
```
---
### `GET /api/v1/markets/:id/signal`
Señal AI más reciente para un mercado. Ver [SIGNALS.md](SIGNALS.md) para el contrato completo.
---
## Mapping Polymarket Gamma API → `Market`
URL de origen: `https://gamma-api.polymarket.com/markets?active=true&closed=false&limit=100`
| Campo Gamma API | Campo `Market` | Transformación |
|---|---|---|
| `id` | `id` | String (ID numérico de Polymarket) |
| `question` | `question` | Directo |
| `category` | `category` | Minúsculas; `null` si Gamma no lo envía |
| — | `countryCode` | `null` por defecto (no proporcionado por Gamma) |
| `outcomePrices[0]` | `yesPrice` | `parseFloat`; rango 0.0–1.0 |
| `outcomePrices[1]` | `noPrice` | `parseFloat`; rango 0.0–1.0 |
| `volume` | `volumeEur` | `parseFloat(volume) * 0.93` (USD→EUR tasa fija) |
| `liquidity` | `liquidityEur` | Igual que `volumeEur` |
| `active` + `closed` + `archived` | `status` | `active=true → "active"`, `closed=true → "closed"`, `archived=true → "resolved"` |
| `endDateIso` | `closesAt` | `new Date(endDateIso)` |
| — | `lastSynced` | `new Date()` en el momento del upsert |
La sincronización usa `prisma.market.upsert({ where: { id }, ... })` para evitar duplicados.
---
## Socket — evento `market_update`
Emitido por `src/socket/broadcaster.js` cada 30 s (tras `syncMarkets`).
**Nombre del evento:** `market_update`
**Payload**
```json
{
"marketId": "0x1a2b...",
"yesPrice": 0.63,
"noPrice": 0.37,
"volumeEur": 125000.00
}
```
**Uso en el frontend (Socket.io client)**
```js
import { io } from 'socket.io-client';
const socket = io('http://localhost:7860');
socket.on('market_update', ({ marketId, yesPrice, noPrice }) => {
// actualizar estado local del mercado
});
```
En producción (HF Spaces) el frontend y backend comparten origen → usar `io()` sin URL.
---
## Ejemplos `curl`
```bash
# Listar mercados (primeros 5)
curl "http://localhost:7860/api/v1/markets?limit=5"
# Filtrar por estado
curl "http://localhost:7860/api/v1/markets?status=active&limit=10"
# Detalle de un mercado
curl "http://localhost:7860/api/v1/markets/559677"
# Señal AI del mercado
curl "http://localhost:7860/api/v1/markets/559677/signal"
```
---
## Códigos de error relevantes
| HTTP | Código | Cuándo |
|---|---|---|
| `400` | `VALIDATION_ERROR` | Parámetro inválido (ej. `limit` > 100) |
| `404` | `NOT_FOUND` | ID de mercado no existe en DB |
| `500` | `INTERNAL` | Error inesperado del servidor |
Los endpoints de markets **no requieren autenticación** — son datos públicos.
|