# 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 `.** --- ## 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 |