Spaces:
Sleeping
Sleeping
| # WATCHLIST.md — Módulo de watchlist | |
| Mercados favoritos del usuario con umbral de alerta opcional. Cuando `yesPrice >= alertThreshold`, el job `processAlerts` crea una `Alert` y envía notificación por Telegram. | |
| **Todos los endpoints requieren `Authorization: Bearer <token>`.** | |
| --- | |
| ## Endpoints | |
| ### `POST /api/v1/watchlist` | |
| Añade un mercado a la watchlist del usuario. | |
| **Body** | |
| ```json | |
| { | |
| "marketId": "559677", | |
| "alertThreshold": 0.75 | |
| } | |
| ``` | |
| | Campo | Tipo | Descripción | | |
| |---|---|---| | |
| | `marketId` | string | ID del mercado (debe existir en DB) | | |
| | `alertThreshold` | float (0–1) \| null | Precio YES que dispara la alerta; `null` para no alertar | | |
| **Respuesta `201`** | |
| ```json | |
| { | |
| "ok": true, | |
| "data": { | |
| "id": 2, | |
| "userId": 1, | |
| "marketId": "559677", | |
| "alertThreshold": 0.75, | |
| "createdAt": "2026-05-16T09:21:41.606Z", | |
| "market": { | |
| "id": "559677", | |
| "question": "Will Hillary Clinton win the 2028 Democratic presidential nomination?", | |
| "yesPrice": 0.0075, | |
| "noPrice": 0.9925, | |
| "status": "active" | |
| } | |
| } | |
| } | |
| ``` | |
| **Respuesta `409`** — mercado ya está en la watchlist del usuario: | |
| ```json | |
| { | |
| "ok": false, | |
| "error": { "code": "CONFLICT", "message": "Market already in watchlist" } | |
| } | |
| ``` | |
| --- | |
| ### `GET /api/v1/watchlist` | |
| Lista todos los mercados en la watchlist del usuario con datos de mercado embebidos. | |
| **Respuesta `200`** | |
| ```json | |
| { | |
| "ok": true, | |
| "data": [ | |
| { | |
| "id": 2, | |
| "userId": 1, | |
| "marketId": "559677", | |
| "alertThreshold": 0.001, | |
| "createdAt": "2026-05-16T09:21:41.606Z", | |
| "market": { | |
| "id": "559677", | |
| "question": "Will Hillary Clinton win the 2028 Democratic presidential nomination?", | |
| "yesPrice": 0.0075, | |
| "noPrice": 0.9925, | |
| "status": "active" | |
| } | |
| } | |
| ] | |
| } | |
| ``` | |
| --- | |
| ### `DELETE /api/v1/watchlist/:marketId` | |
| Elimina un mercado de la watchlist del usuario. | |
| **Params** | |
| | Param | Tipo | Descripción | | |
| |---|---|---| | |
| | `marketId` | string | ID del mercado | | |
| **Respuesta `204`** — sin body. | |
| **Respuesta `404`** | |
| ```json | |
| { | |
| "ok": false, | |
| "error": { "code": "NOT_FOUND", "message": "Watchlist entry not found" } | |
| } | |
| ``` | |
| --- | |
| ## Lógica de alertas | |
| El job `processAlerts` (cron `* * * * *`) evalúa cada entrada de watchlist con `alertThreshold` definido: | |
| - Si `market.yesPrice >= alertThreshold` → crea `Alert` + envía Telegram + emite `price_alert` por socket. | |
| - Deduplicación: no se crea una segunda alerta si ya existe una para el mismo mercado en los últimos 5 min. | |
| Para recibir alertas por Telegram, el usuario debe tener `telegramChatId` configurado en su perfil (campo opcional en `User`). | |
| --- | |
| ## Ejemplos `curl` | |
| ```bash | |
| TOKEN=$(curl -s -X POST http://localhost:7860/api/v1/auth/login \ | |
| -H 'Content-Type: application/json' \ | |
| -d '{"email":"admin@polysignal.test","password":"Admin123!"}' | jq -r '.data.token') | |
| # Añadir a watchlist con umbral 75% | |
| curl -s -X POST http://localhost:7860/api/v1/watchlist \ | |
| -H "Authorization: Bearer $TOKEN" \ | |
| -H "Content-Type: application/json" \ | |
| -d '{"marketId":"559677","alertThreshold":0.75}' | jq | |
| # Listar watchlist | |
| curl -s -H "Authorization: Bearer $TOKEN" http://localhost:7860/api/v1/watchlist | jq | |
| # Eliminar de watchlist | |
| curl -s -X DELETE -H "Authorization: Bearer $TOKEN" \ | |
| "http://localhost:7860/api/v1/watchlist/559677" | |
| ``` | |
| --- | |
| ## Códigos de error | |
| | HTTP | Código | Cuándo | | |
| |---|---|---| | |
| | `400` | `VALIDATION_ERROR` | Body inválido (marketId vacío, threshold fuera de 0–1) | | |
| | `401` | `UNAUTHORIZED` | Sin token o token inválido | | |
| | `404` | `NOT_FOUND` | Mercado no existe en DB o entrada no encontrada al borrar | | |
| | `409` | `CONFLICT` | Mercado ya en watchlist | | |
| | `500` | `INTERNAL` | Error inesperado | | |