Changes include: - Updated architecture documentation - Enhanced module definitions (OQI-001 to OQI-008) - ML integration documentation updates - Trading strategies documentation - Orchestration and inventory updates - Docker configuration updates 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
449 lines
12 KiB
Markdown
449 lines
12 KiB
Markdown
---
|
|
id: "RF-ML-005"
|
|
title: "Notificaciones y Alertas de Senales"
|
|
type: "Requirement"
|
|
status: "Done"
|
|
priority: "Alta"
|
|
epic: "OQI-006"
|
|
project: "trading-platform"
|
|
version: "1.0.0"
|
|
created_date: "2025-12-05"
|
|
updated_date: "2026-01-04"
|
|
---
|
|
|
|
# RF-ML-005: Notificaciones y Alertas de Señales
|
|
|
|
**Versión:** 1.0.0
|
|
**Fecha:** 2025-12-05
|
|
**Épica:** OQI-006 - Señales ML y Predicciones
|
|
**Prioridad:** P2
|
|
**Story Points:** 7
|
|
|
|
---
|
|
|
|
## Descripción
|
|
|
|
El sistema debe enviar notificaciones en tiempo real a los usuarios cuando se generen nuevas señales de trading de alta prioridad, cuando se alcancen niveles de TP/SL, o cuando ocurran eventos importantes en el modelo ML.
|
|
|
|
---
|
|
|
|
## Requisitos Funcionales
|
|
|
|
### RF-ML-005.1: Tipos de Notificaciones
|
|
|
|
El sistema debe soportar los siguientes tipos de notificaciones:
|
|
|
|
| Tipo | Prioridad | Descripción |
|
|
|------|-----------|-------------|
|
|
| **NEW_SIGNAL** | Alta | Nueva señal BUY/SELL generada |
|
|
| **TP_HIT** | Media | Take Profit alcanzado |
|
|
| **SL_HIT** | Alta | Stop Loss alcanzado |
|
|
| **SIGNAL_EXPIRED** | Baja | Señal expiró sin ejecutarse |
|
|
| **MODEL_RETRAINED** | Media | Modelo re-entrenado con nuevas métricas |
|
|
| **LOW_CONFIDENCE** | Baja | Confianza del modelo cayó bajo umbral |
|
|
|
|
### RF-ML-005.2: Canales de Notificación
|
|
|
|
El sistema debe soportar múltiples canales:
|
|
|
|
**In-App (Prioridad 1):**
|
|
- Notificaciones en la plataforma web
|
|
- Badge counter en navbar
|
|
- Panel de notificaciones con historial
|
|
|
|
**Push Notifications (Prioridad 2):**
|
|
- Web Push API para navegadores
|
|
- Notificaciones desktop
|
|
|
|
**Email (Prioridad 3):**
|
|
- Resumen diario de señales
|
|
- Alertas críticas (SL hit)
|
|
|
|
**Webhook (Prioridad 4):**
|
|
- Integración con Telegram/Discord
|
|
- Webhooks personalizados
|
|
|
|
### RF-ML-005.3: Configuración de Alertas por Usuario
|
|
|
|
Cada usuario debe poder configurar:
|
|
|
|
```typescript
|
|
interface AlertPreferences {
|
|
user_id: string;
|
|
|
|
// Canales activos
|
|
channels: {
|
|
in_app: boolean;
|
|
push: boolean;
|
|
email: boolean;
|
|
webhook?: string; // URL del webhook
|
|
};
|
|
|
|
// Filtros de señales
|
|
filters: {
|
|
min_priority: 'HIGH' | 'MEDIUM' | 'LOW';
|
|
symbols: string[]; // Vacío = todos
|
|
horizons: string[]; // Vacío = todos
|
|
actions: ('BUY' | 'SELL')[]; // Vacío = ambos
|
|
min_confidence: number; // 0-1
|
|
min_score: number; // 0-10
|
|
};
|
|
|
|
// Configuración de horarios
|
|
quiet_hours?: {
|
|
enabled: boolean;
|
|
start_hour: number; // 0-23
|
|
end_hour: number;
|
|
timezone: string;
|
|
};
|
|
|
|
// Rate limiting
|
|
max_notifications_per_hour?: number;
|
|
}
|
|
```
|
|
|
|
### RF-ML-005.4: Contenido de Notificaciones
|
|
|
|
**NEW_SIGNAL - Nueva Señal:**
|
|
```json
|
|
{
|
|
"type": "NEW_SIGNAL",
|
|
"timestamp": "2025-12-05T19:00:00.000Z",
|
|
"priority": "HIGH",
|
|
"title": "Nueva señal BUY para BTCUSDT",
|
|
"message": "Señal de compra con confianza 75% - TP: $89,900 | SL: $89,150",
|
|
"data": {
|
|
"signal_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
"symbol": "BTCUSDT",
|
|
"action": "BUY",
|
|
"horizon": "scalping",
|
|
"entry_price": 89400.00,
|
|
"tp1": 89650.00,
|
|
"tp2": 89775.00,
|
|
"tp3": 89900.00,
|
|
"stop_loss": 89150.00,
|
|
"confidence": 0.75,
|
|
"score": 8.5
|
|
},
|
|
"actions": [
|
|
{
|
|
"label": "Ver Detalles",
|
|
"url": "/signals/550e8400-e29b-41d4-a716-446655440000"
|
|
},
|
|
{
|
|
"label": "Ver Chart",
|
|
"url": "/trading/BTCUSDT"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
**TP_HIT - Take Profit Alcanzado:**
|
|
```json
|
|
{
|
|
"type": "TP_HIT",
|
|
"timestamp": "2025-12-05T19:25:00.000Z",
|
|
"priority": "MEDIUM",
|
|
"title": "TP1 alcanzado - BTCUSDT",
|
|
"message": "Tu señal alcanzó TP1 ($89,650) con ganancia de +0.28%",
|
|
"data": {
|
|
"signal_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
"symbol": "BTCUSDT",
|
|
"tp_level": "TP1",
|
|
"tp_price": 89650.00,
|
|
"entry_price": 89400.00,
|
|
"current_price": 89655.00,
|
|
"gain_pct": 0.28
|
|
}
|
|
}
|
|
```
|
|
|
|
**SL_HIT - Stop Loss Alcanzado:**
|
|
```json
|
|
{
|
|
"type": "SL_HIT",
|
|
"timestamp": "2025-12-05T19:15:00.000Z",
|
|
"priority": "HIGH",
|
|
"title": "Stop Loss activado - BTCUSDT",
|
|
"message": "Tu señal alcanzó SL ($89,150) con pérdida de -0.28%",
|
|
"data": {
|
|
"signal_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
"symbol": "BTCUSDT",
|
|
"stop_loss": 89150.00,
|
|
"entry_price": 89400.00,
|
|
"current_price": 89145.00,
|
|
"loss_pct": -0.28
|
|
}
|
|
}
|
|
```
|
|
|
|
### RF-ML-005.5: Notificaciones In-App
|
|
|
|
El sistema debe:
|
|
- Mostrar badge counter en el navbar con número de notificaciones no leídas
|
|
- Panel de notificaciones accesible con click en icono campana
|
|
- Listar notificaciones ordenadas por fecha (más recientes primero)
|
|
- Marcar como leída al hacer click
|
|
- Opción "Marcar todas como leídas"
|
|
- Eliminar notificaciones antiguas (>30 días)
|
|
|
|
**UI del Panel:**
|
|
```
|
|
┌─────────────────────────────────────────┐
|
|
│ Notificaciones [x] │
|
|
├─────────────────────────────────────────┤
|
|
│ [•] Nueva señal BUY - BTCUSDT │
|
|
│ Confianza 75% | Hace 5 min │
|
|
│ │
|
|
│ [•] TP1 alcanzado - ETHUSDT │
|
|
│ Ganancia +0.5% | Hace 15 min │
|
|
│ │
|
|
│ [ ] Modelo re-entrenado │
|
|
│ Nuevo MAE: 0.0012 | Hace 2h │
|
|
│ │
|
|
│ [ ] Señal expirada - BTCUSDT │
|
|
│ No ejecutada | Hace 5h │
|
|
├─────────────────────────────────────────┤
|
|
│ [Marcar todas como leídas] │
|
|
│ [Ver todas las notificaciones] │
|
|
└─────────────────────────────────────────┘
|
|
```
|
|
|
|
### RF-ML-005.6: Rate Limiting y Anti-Spam
|
|
|
|
El sistema debe:
|
|
- Limitar a máximo 20 notificaciones por hora por usuario (configurable)
|
|
- Agrupar notificaciones similares (ej: múltiples señales del mismo símbolo)
|
|
- Respetar horarios de silencio (quiet hours) configurados por el usuario
|
|
- No enviar notificaciones duplicadas
|
|
|
|
**Agrupación de Notificaciones:**
|
|
```json
|
|
{
|
|
"type": "NEW_SIGNAL_BATCH",
|
|
"timestamp": "2025-12-05T19:00:00.000Z",
|
|
"title": "3 nuevas señales generadas",
|
|
"message": "BTCUSDT (BUY), ETHUSDT (BUY), BNBUSDT (SELL)",
|
|
"data": {
|
|
"count": 3,
|
|
"signals": [
|
|
{ "symbol": "BTCUSDT", "action": "BUY" },
|
|
{ "symbol": "ETHUSDT", "action": "BUY" },
|
|
{ "symbol": "BNBUSDT", "action": "SELL" }
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Datos de Entrada
|
|
|
|
### Crear/Actualizar Preferencias
|
|
|
|
| Campo | Tipo | Descripción | Requerido |
|
|
|-------|------|-------------|-----------|
|
|
| channels | object | Canales activos | Sí |
|
|
| filters | object | Filtros de señales | Sí |
|
|
| quiet_hours | object | Horarios de silencio | No |
|
|
| max_notifications_per_hour | number | Rate limit | No |
|
|
|
|
---
|
|
|
|
## Datos de Salida
|
|
|
|
### Obtener Notificaciones
|
|
|
|
```typescript
|
|
interface Notification {
|
|
id: string;
|
|
user_id: string;
|
|
type: NotificationType;
|
|
priority: 'HIGH' | 'MEDIUM' | 'LOW';
|
|
title: string;
|
|
message: string;
|
|
data: any;
|
|
actions?: NotificationAction[];
|
|
read: boolean;
|
|
created_at: string;
|
|
}
|
|
|
|
interface NotificationList {
|
|
notifications: Notification[];
|
|
unread_count: number;
|
|
total_count: number;
|
|
page: number;
|
|
page_size: number;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Reglas de Negocio
|
|
|
|
1. **Notificaciones Críticas:** SL_HIT siempre se envía, ignorando quiet hours
|
|
2. **Deduplicación:** Una señal genera solo 1 notificación NEW_SIGNAL
|
|
3. **Expiración:** Notificaciones mayores a 30 días se eliminan automáticamente
|
|
4. **Batch Processing:** Señales generadas en batch (cada 5 min) se agrupan
|
|
5. **Prioridad de Canales:** In-app > Push > Email > Webhook
|
|
6. **Límite de Webhook:** Máximo 3 webhooks por usuario
|
|
|
|
---
|
|
|
|
## Criterios de Aceptación
|
|
|
|
```gherkin
|
|
Escenario: Recibir notificación de nueva señal HIGH
|
|
DADO que el usuario tiene alertas activas
|
|
Y min_priority = "HIGH"
|
|
CUANDO se genera una señal BUY de prioridad HIGH
|
|
ENTONCES recibe notificación in-app
|
|
Y el badge counter incrementa en 1
|
|
Y la notificación incluye detalles de la señal
|
|
|
|
Escenario: Respetar quiet hours
|
|
DADO que el usuario configuró quiet_hours de 22:00 a 8:00
|
|
Y son las 23:30
|
|
CUANDO se genera una señal MEDIUM
|
|
ENTONCES NO recibe notificación
|
|
Y la notificación se almacena para verla después
|
|
|
|
Escenario: Notificación de Stop Loss (crítica)
|
|
DADO que el usuario configuró quiet_hours
|
|
Y son las 2:00 AM
|
|
CUANDO una señal alcanza el Stop Loss
|
|
ENTONCES recibe notificación SL_HIT (ignora quiet hours)
|
|
Y la prioridad es HIGH
|
|
|
|
Escenario: Marcar notificación como leída
|
|
DADO que el usuario tiene 5 notificaciones no leídas
|
|
CUANDO hace click en una notificación
|
|
ENTONCES se marca como leída (read = true)
|
|
Y el unread_count disminuye a 4
|
|
Y navega a la página de detalles
|
|
```
|
|
|
|
---
|
|
|
|
## Dependencias
|
|
|
|
### Técnicas:
|
|
- **WebSocket:** Para notificaciones in-app en tiempo real
|
|
- **Web Push API:** Para notificaciones push de navegador
|
|
- **Email Service (SendGrid/AWS SES):** Para emails
|
|
- **PostgreSQL:** Almacenamiento de notificaciones
|
|
- **Redis:** Caché de preferencias y rate limiting
|
|
|
|
### Funcionales:
|
|
- **RF-ML-002:** Generación de señales (trigger de notificaciones)
|
|
- **RF-AUTH-005:** Sessions (identificar usuario activo)
|
|
|
|
---
|
|
|
|
## Notas Técnicas
|
|
|
|
### Arquitectura de Notificaciones
|
|
|
|
```python
|
|
# apps/ml-services/src/notifications/notification_service.py
|
|
|
|
class NotificationService:
|
|
"""
|
|
Servicio de notificaciones multi-canal
|
|
"""
|
|
|
|
async def send_notification(
|
|
self,
|
|
user_id: str,
|
|
notification: Notification
|
|
):
|
|
# 1. Obtener preferencias del usuario
|
|
prefs = await self.get_user_preferences(user_id)
|
|
|
|
# 2. Filtrar según preferencias
|
|
if not self.should_send(notification, prefs):
|
|
return
|
|
|
|
# 3. Verificar quiet hours
|
|
if self.is_quiet_hours(prefs) and notification.priority != 'HIGH':
|
|
await self.queue_for_later(user_id, notification)
|
|
return
|
|
|
|
# 4. Verificar rate limit
|
|
if await self.is_rate_limited(user_id):
|
|
await self.queue_for_later(user_id, notification)
|
|
return
|
|
|
|
# 5. Enviar a canales activos
|
|
tasks = []
|
|
if prefs.channels.in_app:
|
|
tasks.append(self.send_in_app(user_id, notification))
|
|
if prefs.channels.push:
|
|
tasks.append(self.send_push(user_id, notification))
|
|
if prefs.channels.email:
|
|
tasks.append(self.send_email(user_id, notification))
|
|
if prefs.channels.webhook:
|
|
tasks.append(self.send_webhook(prefs.channels.webhook, notification))
|
|
|
|
await asyncio.gather(*tasks)
|
|
|
|
# 6. Guardar en base de datos
|
|
await self.save_notification(user_id, notification)
|
|
```
|
|
|
|
### Base de Datos - Tabla `notifications`
|
|
|
|
```sql
|
|
CREATE TABLE notifications (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users(id),
|
|
type VARCHAR(50) NOT NULL,
|
|
priority VARCHAR(10) NOT NULL,
|
|
title VARCHAR(200) NOT NULL,
|
|
message TEXT NOT NULL,
|
|
data JSONB,
|
|
read BOOLEAN DEFAULT FALSE,
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
|
|
INDEX idx_user_id (user_id),
|
|
INDEX idx_created_at (created_at),
|
|
INDEX idx_read (read)
|
|
);
|
|
|
|
CREATE TABLE user_alert_preferences (
|
|
user_id UUID PRIMARY KEY REFERENCES users(id),
|
|
channels JSONB NOT NULL,
|
|
filters JSONB NOT NULL,
|
|
quiet_hours JSONB,
|
|
max_notifications_per_hour INTEGER DEFAULT 20,
|
|
updated_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
### WebSocket Event
|
|
|
|
```typescript
|
|
// Cliente se suscribe a notificaciones
|
|
socket.on('subscribe_notifications', (userId) => {
|
|
// Join room personal
|
|
socket.join(`notifications:${userId}`);
|
|
});
|
|
|
|
// Servidor envía notificación
|
|
io.to(`notifications:${userId}`).emit('new_notification', notification);
|
|
```
|
|
|
|
---
|
|
|
|
## Referencias
|
|
|
|
- [Web Push API](https://developer.mozilla.org/en-US/docs/Web/API/Push_API)
|
|
- [SendGrid Email API](https://docs.sendgrid.com/api-reference/mail-send/mail-send)
|
|
- [Socket.io Rooms](https://socket.io/docs/v4/rooms/)
|
|
|
|
---
|
|
|
|
**Creado por:** Requirements-Analyst
|
|
**Fecha:** 2025-12-05
|
|
**Última actualización:** 2025-12-05
|