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>
12 KiB
| id | title | type | status | priority | epic | project | version | created_date | updated_date |
|---|---|---|---|---|---|---|---|---|---|
| RF-ML-005 | Notificaciones y Alertas de Senales | Requirement | Done | Alta | OQI-006 | trading-platform | 1.0.0 | 2025-12-05 | 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:
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:
{
"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:
{
"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:
{
"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:
{
"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
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
- Notificaciones Críticas: SL_HIT siempre se envía, ignorando quiet hours
- Deduplicación: Una señal genera solo 1 notificación NEW_SIGNAL
- Expiración: Notificaciones mayores a 30 días se eliminan automáticamente
- Batch Processing: Señales generadas en batch (cada 5 min) se agrupan
- Prioridad de Canales: In-app > Push > Email > Webhook
- Límite de Webhook: Máximo 3 webhooks por usuario
Criterios de Aceptación
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
# 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
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
// 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
Creado por: Requirements-Analyst Fecha: 2025-12-05 Última actualización: 2025-12-05