File size: 3,756 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 | # 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 |
|