[TASK-2026-01-25-FRONTEND-ANALYSIS] docs: Add frontend specifications and user stories
- Add 5 frontend specification documents (ET-*-frontend.md): - ET-AUTH-006: Authentication module frontend spec - ET-ML-008: ML Signals module frontend spec - ET-LLM-007: LLM Agent module frontend spec - ET-PFM-008: Portfolio Manager frontend spec (design) - ET-MKT-003: Marketplace frontend spec (design) - Add 8 new user stories: - US-AUTH-013: Global logout - US-AUTH-014: Device management - US-ML-008: Ensemble signal view - US-ML-009: ICT analysis view - US-ML-010: Multi-symbol scan - US-LLM-011: Execute trade from chat - US-PFM-013: Rebalance alerts - US-PFM-014: PDF report generation - Update task index with completed analysis Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
3be1d324db
commit
cdec253b02
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,514 @@
|
|||||||
|
---
|
||||||
|
id: "US-AUTH-013"
|
||||||
|
title: "Logout Global"
|
||||||
|
type: "User Story"
|
||||||
|
status: "To Do"
|
||||||
|
priority: "Alta"
|
||||||
|
epic: "OQI-001"
|
||||||
|
story_points: 3
|
||||||
|
created_date: "2026-01-25"
|
||||||
|
updated_date: "2026-01-25"
|
||||||
|
---
|
||||||
|
|
||||||
|
# US-AUTH-013: Logout Global
|
||||||
|
|
||||||
|
**Version:** 1.0.0
|
||||||
|
**Fecha:** 2026-01-25
|
||||||
|
**Estado:** Pendiente
|
||||||
|
**Story Points:** 3
|
||||||
|
**Prioridad:** P1 (Alta)
|
||||||
|
**Épica:** [OQI-001](../_MAP.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Historia de Usuario
|
||||||
|
|
||||||
|
**Como** usuario de Trading Platform
|
||||||
|
**Quiero** poder cerrar sesión en todos mis dispositivos simultáneamente
|
||||||
|
**Para** asegurarme de que mi cuenta está completamente segura, especialmente si creo que ha sido comprometida o si pierdo un dispositivo
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Criterios de Aceptación
|
||||||
|
|
||||||
|
### AC-001: Opción de logout global en perfil
|
||||||
|
|
||||||
|
**Dado** que estoy autenticado y en mi perfil
|
||||||
|
**Cuando** accedo a Configuración > Seguridad > Sesiones
|
||||||
|
**Entonces** debería ver un botón "Cerrar sesión en todos los dispositivos" o similar
|
||||||
|
|
||||||
|
### AC-002: Confirmación de logout global
|
||||||
|
|
||||||
|
**Dado** que quiero cerrar sesión en todos mis dispositivos
|
||||||
|
**Cuando** hago click en el botón de logout global
|
||||||
|
**Entonces** debería ver un modal de confirmación que indique:
|
||||||
|
- "Esto cerrará sesión en TODOS tus dispositivos"
|
||||||
|
- "Incluida esta sesión actual"
|
||||||
|
- "Tendrás que volver a iniciar sesión"
|
||||||
|
- Botón "Cancelar" y "Cerrar todas las sesiones"
|
||||||
|
|
||||||
|
### AC-003: Invalidación de todos los tokens
|
||||||
|
|
||||||
|
**Dado** que confirmo el logout global
|
||||||
|
**Cuando** se ejecuta la acción
|
||||||
|
**Entonces** debería:
|
||||||
|
1. Invalidar TODOS los tokens JWT de todas las sesiones
|
||||||
|
2. Invalidar TODOS los refresh tokens
|
||||||
|
3. Marcar todas las sesiones como "closed" en la DB
|
||||||
|
4. No permitir que ningún token anterior funcione
|
||||||
|
|
||||||
|
### AC-004: Redirección a login
|
||||||
|
|
||||||
|
**Dado** que logout global se completó exitosamente
|
||||||
|
**Cuando** se cierre la sesión
|
||||||
|
**Entonces** debería:
|
||||||
|
1. Mostrar mensaje "Sesión cerrada en todos los dispositivos"
|
||||||
|
2. Redirigir a la página de login después de 2 segundos
|
||||||
|
3. Limpiar localStorage y cookies
|
||||||
|
4. No permitir acceso a rutas protegidas
|
||||||
|
|
||||||
|
### AC-005: Email de notificación
|
||||||
|
|
||||||
|
**Dado** que ejecuté logout global
|
||||||
|
**Cuando** se cierre la sesión
|
||||||
|
**Entonces** debería recibir email en la dirección registrada indicando:
|
||||||
|
- "Se cerró sesión en todos tus dispositivos"
|
||||||
|
- Fecha y hora
|
||||||
|
- Si no fuiste tú, link para cambiar contraseña
|
||||||
|
|
||||||
|
### AC-006: Impossibilidad de continuar con tokens antiguos
|
||||||
|
|
||||||
|
**Dado** que hice logout global
|
||||||
|
**Cuando** intento usar un token anterior en cualquier dispositivo
|
||||||
|
**Entonces** debería recibir:
|
||||||
|
- Código 401 Unauthorized
|
||||||
|
- Mensaje "Sesión inválida"
|
||||||
|
- Redirección a login
|
||||||
|
|
||||||
|
### AC-007: Operación atómica
|
||||||
|
|
||||||
|
**Dado** que inicio logout global
|
||||||
|
**Cuando** hay una falla de red durante el proceso
|
||||||
|
**Entonces** debería:
|
||||||
|
- Reintentar automáticamente 3 veces
|
||||||
|
- Si continúa fallando, mostrar error al usuario
|
||||||
|
- Los tokens deben estar invalidados (garantizar atomicidad)
|
||||||
|
|
||||||
|
### AC-008: Auditoria y registro
|
||||||
|
|
||||||
|
**Dado** que ejecuté logout global
|
||||||
|
**Cuando** se complete la acción
|
||||||
|
**Entonces** debería:
|
||||||
|
- Quedar registrado en logs de auditoria
|
||||||
|
- Incluir timestamp, user_id, tipo de acción
|
||||||
|
- Ser visible en historial de sesiones como "Cerrado por logout global"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Mockup
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ Configuración > Seguridad > Sesiones │
|
||||||
|
├─────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ Sesiones activas │
|
||||||
|
│ Gestiona dónde está abierta tu cuenta │
|
||||||
|
│ │
|
||||||
|
│ ┌─────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ 💻 Chrome en macOS 🟢 Esta sesión │ │
|
||||||
|
│ │ San Francisco, Estados Unidos • 201.45.67.89 │ │
|
||||||
|
│ │ Última actividad: Hace 2 minutos │ │
|
||||||
|
│ └─────────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌─────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ 📱 Safari en iPhone [Cerrar sesión]│ │
|
||||||
|
│ │ Ciudad de México, México • 189.203.45.12 │ │
|
||||||
|
│ │ Última actividad: Hace 3 horas │ │
|
||||||
|
│ └─────────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌─────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ 🔒 CERRAR SESIÓN EN TODOS LOS DISPOSITIVOS │ │
|
||||||
|
│ └─────────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ⚠️ Esto cerrará sesión en esta y todas las otras │
|
||||||
|
│ sesiones. Tendrás que volver a iniciar sesión. │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
Modal de confirmación:
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ │
|
||||||
|
│ ⚠️ Cerrar sesión en todos los dispositivos │
|
||||||
|
│ │
|
||||||
|
│ Esto cerrará sesión INMEDIATAMENTE en: │
|
||||||
|
│ │
|
||||||
|
│ • Esta sesión (Chrome en macOS) │
|
||||||
|
│ • Safari en iPhone │
|
||||||
|
│ • Firefox en Windows │
|
||||||
|
│ │
|
||||||
|
│ Se cerrarán 3 sesiones en total. │
|
||||||
|
│ │
|
||||||
|
│ Si no fuiste tú, considera cambiar tu contraseña después. │
|
||||||
|
│ │
|
||||||
|
│ ┌──────────────────────┐ ┌──────────────────────┐ │
|
||||||
|
│ │ Cancelar │ │ Cerrar todo │ │
|
||||||
|
│ └──────────────────────┘ └──────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
Email de notificación:
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ De: Trading Platform Security <security@trading.com> │
|
||||||
|
│ Asunto: Se cerró sesión en todos tus dispositivos │
|
||||||
|
│ │
|
||||||
|
│ Hola Juan, │
|
||||||
|
│ │
|
||||||
|
│ Se cerró sesión en TODOS tus dispositivos. │
|
||||||
|
│ │
|
||||||
|
│ 🕐 25 de Enero, 2026 a las 14:30 CST │
|
||||||
|
│ 📍 Desde: San Francisco, Estados Unidos │
|
||||||
|
│ │
|
||||||
|
│ ¿Fuiste tú? │
|
||||||
|
│ Si reconoces esta actividad, puedes ignorar este email. │
|
||||||
|
│ │
|
||||||
|
│ ¿No fuiste tú? │
|
||||||
|
│ ┌─────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ Cambiar contraseña inmediatamente │ │
|
||||||
|
│ └─────────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ Para seguridad adicional, considera: │
|
||||||
|
│ • Verificar tu email de recuperación │
|
||||||
|
│ • Revisar actividades recientes │
|
||||||
|
│ • Habilitar autenticación de dos factores │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
Mensaje después de logout:
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ │
|
||||||
|
│ ✓ Sesión cerrada en todos los dispositivos │
|
||||||
|
│ │
|
||||||
|
│ Se cerrará sesión automáticamente en 2 segundos... │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tareas Técnicas
|
||||||
|
|
||||||
|
### Database (DB)
|
||||||
|
|
||||||
|
- [ ] Tabla `user_sessions` (ya existe en US-AUTH-012)
|
||||||
|
- Verificar columnas: `expires_at`, `status`
|
||||||
|
- Agregar índice: `idx_user_active` (user_id, WHERE status = 'active')
|
||||||
|
|
||||||
|
- [ ] Tabla `session_audit_log`:
|
||||||
|
```sql
|
||||||
|
CREATE TABLE session_audit_log (
|
||||||
|
id UUID PRIMARY KEY,
|
||||||
|
user_id UUID REFERENCES users(id) NOT NULL,
|
||||||
|
action VARCHAR(100) NOT NULL, -- 'logout_global', 'logout_single', etc.
|
||||||
|
session_id UUID REFERENCES user_sessions(id),
|
||||||
|
ip_address VARCHAR(45),
|
||||||
|
user_agent TEXT,
|
||||||
|
created_at TIMESTAMP DEFAULT NOW(),
|
||||||
|
INDEX idx_user_created (user_id, created_at),
|
||||||
|
INDEX idx_action_created (action, created_at)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Backend (BE)
|
||||||
|
|
||||||
|
- [ ] Endpoint `POST /api/v1/auth/logout-all`
|
||||||
|
- Autenticación requerida
|
||||||
|
- Parámetro: `confirmation: boolean`
|
||||||
|
- Respuesta: `{ success: boolean, message: string, redirectUrl: string }`
|
||||||
|
- Rate limit: 1 request/min por usuario
|
||||||
|
|
||||||
|
- [ ] Service `LogoutService`
|
||||||
|
- `logoutGlobal(userId: string)`
|
||||||
|
- Buscar todas las sesiones activas del usuario
|
||||||
|
- Invalidar todos los tokens
|
||||||
|
- Marcar sesiones como 'closed'
|
||||||
|
- Registrar en auditoria
|
||||||
|
- Retornar conteo de sesiones cerradas
|
||||||
|
|
||||||
|
- [ ] Invalidación de tokens:
|
||||||
|
- Crear tabla/cache de tokens invalidados (blacklist)
|
||||||
|
- Middleware: verificar si token está en blacklist
|
||||||
|
- Usar Redis para expiración automática
|
||||||
|
|
||||||
|
- [ ] Email notification:
|
||||||
|
- Template: `logout-all.html`
|
||||||
|
- Enviar a dirección registrada
|
||||||
|
- Incluir: fecha, hora, ubicación, link de seguridad
|
||||||
|
- Service: `EmailService.sendLogoutAllNotification()`
|
||||||
|
|
||||||
|
- [ ] Logging y auditoria:
|
||||||
|
- Registrar en `session_audit_log`
|
||||||
|
- Incluir: timestamp, user_id, acción, IP, user_agent
|
||||||
|
- Service: `AuditService.logLogoutAll()`
|
||||||
|
|
||||||
|
- [ ] Tests unitarios (8 casos)
|
||||||
|
- Logout exitoso
|
||||||
|
- Logout con error de DB
|
||||||
|
- Email enviado correctamente
|
||||||
|
- Tokens invalidados
|
||||||
|
- Auditoria registrada
|
||||||
|
- Rate limiting
|
||||||
|
- Concurrencia (múltiples requests simultáneos)
|
||||||
|
- Validación de autenticación
|
||||||
|
|
||||||
|
- [ ] Tests de integración (5 escenarios)
|
||||||
|
- Logout global desde múltiples dispositivos
|
||||||
|
- Verificar 401 con tokens anteriores
|
||||||
|
- Email recibido
|
||||||
|
- Auditoria registrada
|
||||||
|
- Redirección correcta
|
||||||
|
|
||||||
|
### Frontend (FE)
|
||||||
|
|
||||||
|
- [ ] Componente `LogoutAllButton.tsx`
|
||||||
|
- Botón de logout global
|
||||||
|
- En Settings > Security > Sessions
|
||||||
|
|
||||||
|
- [ ] Modal `LogoutAllConfirmationModal.tsx`
|
||||||
|
- Mostrar lista de sesiones que se cerrarán
|
||||||
|
- Advertencia clara
|
||||||
|
- Botones Cancelar/Confirmar
|
||||||
|
|
||||||
|
- [ ] Integración en `Sessions.tsx`
|
||||||
|
- Agregar botón y modal
|
||||||
|
- Manejar respuesta de API
|
||||||
|
- Mostrar mensaje de éxito
|
||||||
|
- Redirigir a login
|
||||||
|
|
||||||
|
- [ ] Servicio `AuthService`
|
||||||
|
- Método `logoutAll(): Promise<{ success: boolean }>`
|
||||||
|
- Llamar a POST /api/v1/auth/logout-all
|
||||||
|
- Limpiar tokens locales
|
||||||
|
- Redirigir a login
|
||||||
|
|
||||||
|
- [ ] Tests con React Testing Library
|
||||||
|
- Renderizar componente
|
||||||
|
- Click en botón
|
||||||
|
- Mostrar modal
|
||||||
|
- Enviar confirmación
|
||||||
|
- Verificar redirección
|
||||||
|
|
||||||
|
### Testing (QA)
|
||||||
|
|
||||||
|
- [ ] E2E: Logout global exitoso
|
||||||
|
- [ ] E2E: Modal de confirmación
|
||||||
|
- [ ] E2E: Email recibido
|
||||||
|
- [ ] E2E: Tokens invalidados en todos los dispositivos
|
||||||
|
- [ ] E2E: Redirección a login
|
||||||
|
- [ ] E2E: Auditoria registrada
|
||||||
|
- [ ] Test de concurrencia: Múltiples logouts globales simultáneos
|
||||||
|
- [ ] Test de seguridad: Tokens antiguos no funcionan
|
||||||
|
- [ ] Performance: Logout global < 500ms
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Dependencias
|
||||||
|
|
||||||
|
- **Bloqueantes:**
|
||||||
|
- US-AUTH-012: Gestión de Sesiones (para tablas y flujos)
|
||||||
|
- Servicio de email configurado
|
||||||
|
|
||||||
|
- **Relacionadas:**
|
||||||
|
- US-AUTH-002: Login genera tokens
|
||||||
|
- US-AUTH-014: Cambio de contraseña por seguridad
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Definition of Ready (DoR)
|
||||||
|
|
||||||
|
- [ ] Mockups aprobados
|
||||||
|
- [ ] API contract definido
|
||||||
|
- [ ] Estrategia de invalidación de tokens definida
|
||||||
|
- [ ] Template de email diseñado
|
||||||
|
- [ ] Esquema de auditoria revisado
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Definition of Done (DoD)
|
||||||
|
|
||||||
|
- [ ] Código implementado y revisado
|
||||||
|
- [ ] Tests unitarios con 80%+ cobertura
|
||||||
|
- [ ] Tests de integración pasando
|
||||||
|
- [ ] Tests E2E implementados
|
||||||
|
- [ ] Email de notificación funcional
|
||||||
|
- [ ] Auditoria registrada correctamente
|
||||||
|
- [ ] Rate limiting configurado
|
||||||
|
- [ ] Tokens invalidados en todos los dispositivos
|
||||||
|
- [ ] Redirección a login funcional
|
||||||
|
- [ ] Documentación actualizada
|
||||||
|
- [ ] QA aprobado en staging
|
||||||
|
- [ ] Deploy a producción exitoso
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Notas Técnicas
|
||||||
|
|
||||||
|
### Estrategia de Invalidación de Tokens
|
||||||
|
|
||||||
|
**Opción 1: Token Blacklist (Recomendado)**
|
||||||
|
```typescript
|
||||||
|
// En Redis, crear entrada para cada token invalidado
|
||||||
|
await redis.setex(
|
||||||
|
`token_blacklist:${jti}`,
|
||||||
|
tokenExpirationTime,
|
||||||
|
'revoked'
|
||||||
|
);
|
||||||
|
|
||||||
|
// En middleware, verificar si token está en blacklist
|
||||||
|
const isBlacklisted = await redis.exists(`token_blacklist:${jti}`);
|
||||||
|
if (isBlacklisted) {
|
||||||
|
throw new UnauthorizedException('Token revoked');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Opción 2: Token Version (Alternativa)**
|
||||||
|
```typescript
|
||||||
|
// En tabla users, guardar version_token
|
||||||
|
// Al hacer logout global, incrementar version
|
||||||
|
UPDATE users SET token_version = token_version + 1 WHERE id = ?
|
||||||
|
|
||||||
|
// En JWT, incluir token_version
|
||||||
|
const token = generateJWT({
|
||||||
|
sub: user.id,
|
||||||
|
token_version: user.token_version,
|
||||||
|
...
|
||||||
|
});
|
||||||
|
|
||||||
|
// En middleware, validar que token_version coincida
|
||||||
|
const user = await findUser(jti.sub);
|
||||||
|
if (token.token_version !== user.token_version) {
|
||||||
|
throw new UnauthorizedException('Token revoked');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Recomendamos Opción 1 (Redis) por precisión inmediata.
|
||||||
|
|
||||||
|
### Flujo de Logout Global
|
||||||
|
|
||||||
|
```
|
||||||
|
1. Usuario hace click en "Cerrar sesión en todos los dispositivos"
|
||||||
|
↓
|
||||||
|
2. Frontend muestra modal de confirmación
|
||||||
|
↓
|
||||||
|
3. Usuario confirma
|
||||||
|
↓
|
||||||
|
4. Frontend envía POST /api/v1/auth/logout-all
|
||||||
|
↓
|
||||||
|
5. Backend:
|
||||||
|
a. Verifica autenticación
|
||||||
|
b. Busca todas las sesiones del usuario
|
||||||
|
c. Marca todas como 'closed'
|
||||||
|
d. Invalida todos los tokens en Redis/Cache
|
||||||
|
e. Registra acción en auditoria
|
||||||
|
f. Envía email de notificación
|
||||||
|
↓
|
||||||
|
6. Frontend recibe respuesta exitosa
|
||||||
|
↓
|
||||||
|
7. Frontend limpia tokens locales
|
||||||
|
↓
|
||||||
|
8. Frontend muestra mensaje "Sesión cerrada"
|
||||||
|
↓
|
||||||
|
9. Frontend redirige a /login después de 2 segundos
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rate Limiting
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Usar express-rate-limit o similar
|
||||||
|
const logoutAllLimiter = rateLimit({
|
||||||
|
windowMs: 60 * 1000, // 1 minuto
|
||||||
|
max: 1, // 1 request por minuto
|
||||||
|
message: 'Solo puedes hacer logout global 1 vez por minuto'
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post('/api/v1/auth/logout-all', logoutAllLimiter, logoutAllHandler);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Transacción en DB
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
async function logoutGlobal(userId: string) {
|
||||||
|
return await db.$transaction(async (tx) => {
|
||||||
|
// 1. Buscar sesiones
|
||||||
|
const sessions = await tx.userSessions.findMany({
|
||||||
|
where: { user_id: userId, status: 'active' }
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. Marcar como closed
|
||||||
|
await tx.userSessions.updateMany({
|
||||||
|
where: { user_id: userId, status: 'active' },
|
||||||
|
data: { status: 'closed', expires_at: new Date() }
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3. Registrar auditoria
|
||||||
|
await tx.sessionAuditLog.create({
|
||||||
|
data: {
|
||||||
|
user_id: userId,
|
||||||
|
action: 'logout_global',
|
||||||
|
created_at: new Date()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return sessions.length;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Email Template
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
body { font-family: Arial, sans-serif; }
|
||||||
|
.container { max-width: 600px; margin: 0 auto; }
|
||||||
|
.header { background-color: #f44336; color: white; padding: 20px; }
|
||||||
|
.content { padding: 20px; }
|
||||||
|
.warning { background-color: #fff3cd; padding: 10px; border-left: 4px solid #ffc107; }
|
||||||
|
.button { background-color: #2196f3; color: white; padding: 10px 20px; text-decoration: none; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<h1>Sesión cerrada en todos tus dispositivos</h1>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<p>Hola {{ user_name }},</p>
|
||||||
|
<p>Se cerró sesión en TODOS tus dispositivos.</p>
|
||||||
|
<div class="warning">
|
||||||
|
<strong>Detalles:</strong><br>
|
||||||
|
Fecha: {{ date }} a las {{ time }} {{ timezone }}<br>
|
||||||
|
Ubicación: {{ location }}<br>
|
||||||
|
</div>
|
||||||
|
<p>¿Fuiste tú? Si reconoces esta actividad, puedes ignorar este email.</p>
|
||||||
|
<p>¿No fuiste tú? Considera cambiar tu contraseña inmediatamente:</p>
|
||||||
|
<a href="{{ password_reset_url }}" class="button">Cambiar contraseña</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Requerimientos Relacionados
|
||||||
|
|
||||||
|
- [RF-AUTH-007: Logout Global](../requerimientos/RF-AUTH-007-logout-global.md)
|
||||||
|
|
||||||
|
## Especificaciones Relacionadas
|
||||||
|
|
||||||
|
- [ET-AUTH-002: JWT Tokens](../especificaciones/ET-AUTH-002-jwt.md)
|
||||||
|
- [ET-AUTH-003: Database](../especificaciones/ET-AUTH-003-database.md)
|
||||||
|
- [ET-AUTH-006: Session Management](../especificaciones/ET-AUTH-006-sessions.md)
|
||||||
@ -0,0 +1,505 @@
|
|||||||
|
---
|
||||||
|
id: "US-AUTH-014"
|
||||||
|
title: "Gestión de Dispositivos"
|
||||||
|
type: "User Story"
|
||||||
|
status: "To Do"
|
||||||
|
priority: "Media"
|
||||||
|
epic: "OQI-001"
|
||||||
|
story_points: 5
|
||||||
|
created_date: "2026-01-25"
|
||||||
|
updated_date: "2026-01-25"
|
||||||
|
---
|
||||||
|
|
||||||
|
# US-AUTH-014: Gestión de Dispositivos
|
||||||
|
|
||||||
|
**Version:** 1.0.0
|
||||||
|
**Fecha:** 2026-01-25
|
||||||
|
**Estado:** Pendiente
|
||||||
|
**Story Points:** 5
|
||||||
|
**Prioridad:** P2 (Media)
|
||||||
|
**Épica:** [OQI-001](../_MAP.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Historia de Usuario
|
||||||
|
|
||||||
|
**Como** usuario de Trading Platform
|
||||||
|
**Quiero** ver y administrar los dispositivos donde tengo sesión activa
|
||||||
|
**Para** mantener control total sobre mi cuenta y detectar accesos no autorizados
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Criterios de Aceptación
|
||||||
|
|
||||||
|
### AC-001: Listado de dispositivos activos
|
||||||
|
|
||||||
|
**Dado** que estoy autenticado
|
||||||
|
**Cuando** accedo a Configuración > Seguridad > Dispositivos
|
||||||
|
**Entonces** debería ver una lista de todos mis dispositivos con sesión activa
|
||||||
|
|
||||||
|
### AC-002: Información de cada dispositivo
|
||||||
|
|
||||||
|
**Dado** que estoy viendo mi lista de dispositivos
|
||||||
|
**Cuando** observo cada dispositivo listado
|
||||||
|
**Entonces** debería ver:
|
||||||
|
- Icono del navegador (Chrome, Safari, Firefox, etc.)
|
||||||
|
- Sistema operativo (Windows, macOS, iOS, Android, Linux)
|
||||||
|
- Modelo/Nombre del dispositivo (si está disponible)
|
||||||
|
- Ubicación aproximada (Ciudad, País)
|
||||||
|
- Dirección IP
|
||||||
|
- Fecha y hora de última actividad
|
||||||
|
- Badge "Dispositivo actual" si es la sesión activa
|
||||||
|
|
||||||
|
### AC-003: Cierre de sesión individual
|
||||||
|
|
||||||
|
**Dado** que identifico un dispositivo que no reconozco
|
||||||
|
**Cuando** hago click en "Cerrar sesión"
|
||||||
|
**Entonces** debería:
|
||||||
|
1. Ver diálogo de confirmación
|
||||||
|
2. Al confirmar, cerrar sesión en ese dispositivo
|
||||||
|
3. Ver notificación "Dispositivo desconectado"
|
||||||
|
4. El dispositivo desaparece de la lista
|
||||||
|
5. Si ese dispositivo intenta hacer request, recibe 401 Unauthorized
|
||||||
|
|
||||||
|
### AC-004: Información detallada del navegador
|
||||||
|
|
||||||
|
**Dado** que quiero investigar un dispositivo sospechoso
|
||||||
|
**Cuando** hago click en el dispositivo para ver más detalles
|
||||||
|
**Entonces** debería ver:
|
||||||
|
- Versión exacta del navegador
|
||||||
|
- Versión exacta del SO
|
||||||
|
- User Agent (para auditoría)
|
||||||
|
- Historial de actividad en últimas 7 días (gráfico de línea)
|
||||||
|
- Ubicaciones desde las que se accedió
|
||||||
|
- Primer acceso registrado
|
||||||
|
- Último acceso registrado
|
||||||
|
|
||||||
|
### AC-005: Indicador de dispositivo actual
|
||||||
|
|
||||||
|
**Dado** que estoy viendo mis dispositivos
|
||||||
|
**Cuando** identifico el dispositivo desde el cual estoy accediendo
|
||||||
|
**Entonces** debería estar claramente marcado con:
|
||||||
|
- Badge "🟢 Este dispositivo"
|
||||||
|
- Ubicado al inicio de la lista
|
||||||
|
- Color diferenciado (verde o azul)
|
||||||
|
- SIN botón "Cerrar sesión"
|
||||||
|
|
||||||
|
### AC-006: Ordenes de dispositivos
|
||||||
|
|
||||||
|
**Dado** que tengo múltiples dispositivos activos
|
||||||
|
**Cuando** veo la lista
|
||||||
|
**Entonces** debería:
|
||||||
|
- Mostrar "Este dispositivo" primero
|
||||||
|
- Los demás ordenados por última actividad (descendente)
|
||||||
|
- Mostrar fecha/hora relativa (Hace 2 min, Hace 3 horas, etc.)
|
||||||
|
|
||||||
|
### AC-007: Detección de navegadores
|
||||||
|
|
||||||
|
**Dado** que accedo desde diferentes navegadores
|
||||||
|
**Cuando** reviso mis dispositivos
|
||||||
|
**Entonces** debería ver iconos apropiados:
|
||||||
|
- 🌐 Chrome
|
||||||
|
- 🔴 Firefox
|
||||||
|
- 🧭 Safari
|
||||||
|
- 🌌 Edge
|
||||||
|
- 📱 Mobile browsers
|
||||||
|
- ❓ Desconocido
|
||||||
|
|
||||||
|
### AC-008: Datos de sesión activa
|
||||||
|
|
||||||
|
**Dado** que tengo una sesión activa
|
||||||
|
**Cuando** veo los detalles del dispositivo
|
||||||
|
**Entonces** debería ver:
|
||||||
|
- Token actual (primeros 10 caracteres) para referencia
|
||||||
|
- Fecha de creación de la sesión
|
||||||
|
- Próxima fecha de expiración
|
||||||
|
- Último refresh de token (si aplica)
|
||||||
|
|
||||||
|
### AC-009: Patrón de actividad
|
||||||
|
|
||||||
|
**Dado** que quiero monitorear actividad
|
||||||
|
**Cuando** hago click en "Ver actividad" de un dispositivo
|
||||||
|
**Entonces** debería ver:
|
||||||
|
- Gráfico de actividad por hora/día
|
||||||
|
- Número de requests por período
|
||||||
|
- Últimas 10 acciones (endpoint, timestamp)
|
||||||
|
- Horarios de pico de uso
|
||||||
|
|
||||||
|
### AC-010: Alertas de nuevos dispositivos
|
||||||
|
|
||||||
|
**Dado** que inicio sesión desde un nuevo dispositivo
|
||||||
|
**Cuando** completo el login
|
||||||
|
**Entonces** debería:
|
||||||
|
1. Ver notificación in-app "Nuevo dispositivo conectado"
|
||||||
|
2. Recibir email con detalles: navegador, SO, ubicación, IP
|
||||||
|
3. Link en email para cerrar sesión inmediatamente si no fui yo
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Mockup
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────┐
|
||||||
|
│ Configuración > Seguridad > Dispositivos │
|
||||||
|
├─────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ Dispositivos activos │
|
||||||
|
│ Administra dónde está abierta tu sesión │
|
||||||
|
│ │
|
||||||
|
│ ┌─────────────────────────────────────────────────┐ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ 🌐 Chrome en Windows 🟢 Este │ │
|
||||||
|
│ │ dispositivo │ │
|
||||||
|
│ │ San Francisco, Estados Unidos • 201.45.67.89 │ │
|
||||||
|
│ │ Última actividad: Hace 2 minutos │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ [Ver más detalles] │ │
|
||||||
|
│ └─────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌─────────────────────────────────────────────────┐ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ 📱 Safari en iPhone [Cerrar] │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ Ciudad de México, México • 189.203.45.12 │ │
|
||||||
|
│ │ Última actividad: Hace 3 horas │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ [Ver más detalles] │ │
|
||||||
|
│ └─────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌─────────────────────────────────────────────────┐ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ 🔴 Firefox en Linux [Cerrar] │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ Madrid, España • 85.123.45.67 │ │
|
||||||
|
│ │ Última actividad: Hace 2 días │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ [Ver más detalles] │ │
|
||||||
|
│ └─────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ──────────────────────────────────────────────────── │
|
||||||
|
│ │
|
||||||
|
│ Información de seguridad │
|
||||||
|
│ • Total de sesiones activas: 3 │
|
||||||
|
│ • Última sesión nueva: 3 horas atrás │
|
||||||
|
│ • Si ves un dispositivo desconocido, ciérralo │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
Modal de detalles:
|
||||||
|
┌─────────────────────────────────────────────────────────┐
|
||||||
|
│ │
|
||||||
|
│ Detalles del dispositivo │
|
||||||
|
│ │
|
||||||
|
│ 🌐 Chrome 130.0.0.0 │
|
||||||
|
│ Windows 11 (Build 23631) │
|
||||||
|
│ │
|
||||||
|
│ Ubicación: │
|
||||||
|
│ San Francisco, Estados Unidos (37.7749°, -122.4194°) │
|
||||||
|
│ │
|
||||||
|
│ IP: 201.45.67.89 │
|
||||||
|
│ ISP: Verizon Communications │
|
||||||
|
│ │
|
||||||
|
│ Sesión: │
|
||||||
|
│ Creada: 25 Ene 2026, 14:30 CST │
|
||||||
|
│ Última actividad: Hace 2 minutos │
|
||||||
|
│ Próxima expiración: 24 Feb 2026, 14:30 CST │
|
||||||
|
│ Token: abc123def... │
|
||||||
|
│ │
|
||||||
|
│ Actividad (últimas 24 horas): │
|
||||||
|
│ [Gráfico de línea con actividad por hora] │
|
||||||
|
│ │
|
||||||
|
│ Últimos requests: │
|
||||||
|
│ 14:45 - GET /api/portfolio │
|
||||||
|
│ 14:43 - GET /api/market/BTC │
|
||||||
|
│ 14:41 - POST /api/orders │
|
||||||
|
│ │
|
||||||
|
│ ┌──────────────────────┐ ┌──────────────────────┐ │
|
||||||
|
│ │ Volver │ │ Cerrar sesión │ │
|
||||||
|
│ └──────────────────────┘ └──────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
Modal de confirmación:
|
||||||
|
┌─────────────────────────────────────────────────────────┐
|
||||||
|
│ │
|
||||||
|
│ ⚠️ Cerrar sesión en dispositivo? │
|
||||||
|
│ │
|
||||||
|
│ Se cerrará la sesión de: │
|
||||||
|
│ 📱 Safari en iPhone │
|
||||||
|
│ Ciudad de México, México │
|
||||||
|
│ │
|
||||||
|
│ Esta acción no se puede deshacer. El dispositivo │
|
||||||
|
│ deberá iniciar sesión nuevamente. │
|
||||||
|
│ │
|
||||||
|
│ ┌──────────────────────┐ ┌──────────────────────┐ │
|
||||||
|
│ │ Cancelar │ │ Cerrar sesión │ │
|
||||||
|
│ └──────────────────────┘ └──────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
Email de nuevo dispositivo:
|
||||||
|
┌─────────────────────────────────────────────────────────┐
|
||||||
|
│ De: Trading Platform Security <security@trading.com> │
|
||||||
|
│ Asunto: Nuevo dispositivo conectado a tu cuenta │
|
||||||
|
│ │
|
||||||
|
│ Hola Juan, │
|
||||||
|
│ │
|
||||||
|
│ Se inició sesión en tu cuenta desde un nuevo │
|
||||||
|
│ dispositivo: │
|
||||||
|
│ │
|
||||||
|
│ 🌐 Chrome en Windows │
|
||||||
|
│ 📍 San Francisco, Estados Unidos │
|
||||||
|
│ 🕐 25 de Enero, 2026 a las 14:30 CST │
|
||||||
|
│ 🌐 IP: 201.45.67.89 │
|
||||||
|
│ │
|
||||||
|
│ ¿Fuiste tú? │
|
||||||
|
│ Si reconoces esta actividad, puedes ignorar este │
|
||||||
|
│ email. │
|
||||||
|
│ │
|
||||||
|
│ ¿No fuiste tú? │
|
||||||
|
│ ┌──────────────────────────────────────────────┐ │
|
||||||
|
│ │ Cerrar sesión en este dispositivo │ │
|
||||||
|
│ └──────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ Ver todos mis dispositivos: │
|
||||||
|
│ [https://trading.example.com/settings/devices] │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tareas Técnicas
|
||||||
|
|
||||||
|
### Database (DB)
|
||||||
|
|
||||||
|
- [ ] Tabla `user_sessions` (ver US-AUTH-012):
|
||||||
|
- Verificar campos: device_type, device_name, browser, os, ip_address, location_city, location_country
|
||||||
|
- Agregar si falta: user_agent (TEXT para auditoría)
|
||||||
|
- Agregar si falta: last_activity_details (JSONB para tracking fino)
|
||||||
|
|
||||||
|
- [ ] Tabla `device_activity_log`:
|
||||||
|
```sql
|
||||||
|
CREATE TABLE device_activity_log (
|
||||||
|
id UUID PRIMARY KEY,
|
||||||
|
session_id UUID REFERENCES user_sessions(id) ON DELETE CASCADE,
|
||||||
|
endpoint VARCHAR(255),
|
||||||
|
method VARCHAR(10), -- GET, POST, PUT, DELETE, etc.
|
||||||
|
status_code INTEGER,
|
||||||
|
response_time_ms INTEGER,
|
||||||
|
created_at TIMESTAMP DEFAULT NOW(),
|
||||||
|
INDEX idx_session_created (session_id, created_at),
|
||||||
|
INDEX idx_created (created_at)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Backend (BE)
|
||||||
|
|
||||||
|
- [ ] Endpoint `GET /api/auth/devices`
|
||||||
|
- Listar todos los dispositivos activos del usuario
|
||||||
|
- Marcar dispositivo actual
|
||||||
|
- Ordenar por última actividad DESC
|
||||||
|
- Incluir: device_type, device_name, os, browser, ip, location, last_activity
|
||||||
|
- Response: HTTP 200
|
||||||
|
|
||||||
|
- [ ] Endpoint `GET /api/auth/devices/:id`
|
||||||
|
- Obtener detalles completos de un dispositivo
|
||||||
|
- Incluir: user_agent, histórico de actividad (7 días), primero/último acceso
|
||||||
|
- Incluir: gráfico de actividad por hora
|
||||||
|
- Incluir: últimos 10 requests
|
||||||
|
- Response: HTTP 200 o 404
|
||||||
|
|
||||||
|
- [ ] Endpoint `DELETE /api/auth/devices/:id`
|
||||||
|
- Cerrar sesión en dispositivo específico
|
||||||
|
- Invalidar tokens asociados
|
||||||
|
- Loguear la acción en audit trail
|
||||||
|
- Enviar email al usuario
|
||||||
|
- Response: HTTP 204 o 401/404
|
||||||
|
|
||||||
|
- [ ] Service `DeviceService`
|
||||||
|
- `getAllDevices(userId)`
|
||||||
|
- `getDeviceDetails(userId, deviceId)`
|
||||||
|
- `terminateDevice(userId, deviceId)`
|
||||||
|
- `isCurrentDevice(userId, sessionId)`
|
||||||
|
- `getDeviceActivitySummary(deviceId, days)`
|
||||||
|
- `getLastNRequests(deviceId, limit)`
|
||||||
|
|
||||||
|
- [ ] Service `DeviceDetectionService` (mejorado)
|
||||||
|
- `parseUserAgent()` - ya existe en US-AUTH-012
|
||||||
|
- `extractBrowserIcon()` - nuevo
|
||||||
|
- `getDeviceFingerprint()` - nuevo para consistencia
|
||||||
|
|
||||||
|
- [ ] Logging de actividad
|
||||||
|
- Hook/middleware para loguear cada request en `device_activity_log`
|
||||||
|
- Incluir: endpoint, método, status code, tiempo de respuesta
|
||||||
|
- Asociar a session_id actual
|
||||||
|
|
||||||
|
- [ ] Email de notificación
|
||||||
|
- Template "Nuevo dispositivo conectado"
|
||||||
|
- Incluir link para cerrar sesión desde email
|
||||||
|
- Usar SendGrid o similar
|
||||||
|
|
||||||
|
- [ ] Tests unitarios (12 casos)
|
||||||
|
- [ ] Tests de integración (8 escenarios)
|
||||||
|
|
||||||
|
### Frontend (FE)
|
||||||
|
|
||||||
|
- [ ] Página `Settings/Security/Devices.tsx` (nueva)
|
||||||
|
- [ ] Componente `DeviceCard.tsx` (nuevo)
|
||||||
|
- [ ] Componente `DeviceDetailsModal.tsx` (nuevo)
|
||||||
|
- [ ] Modal de confirmación de cierre
|
||||||
|
- [ ] Gráfico de actividad (line chart con lightweight-charts)
|
||||||
|
- [ ] Tabla de últimos requests
|
||||||
|
- [ ] Función para obtener icono de navegador
|
||||||
|
- [ ] Badge "Este dispositivo" con lógica de sesión actual
|
||||||
|
- [ ] Ordenamiento automático por última actividad
|
||||||
|
- [ ] Tests con React Testing Library
|
||||||
|
|
||||||
|
### Testing (QA)
|
||||||
|
|
||||||
|
- [ ] E2E: Ver lista de dispositivos
|
||||||
|
- [ ] E2E: Ver detalles de dispositivo
|
||||||
|
- [ ] E2E: Cerrar sesión en dispositivo individual
|
||||||
|
- [ ] E2E: Confirmar cierre desde modal
|
||||||
|
- [ ] E2E: Dispositivo desaparece después de cerrar
|
||||||
|
- [ ] E2E: Email se envía en nuevo dispositivo
|
||||||
|
- [ ] Performance: Lista de dispositivos < 300ms
|
||||||
|
- [ ] Performance: Detalles de dispositivo < 500ms
|
||||||
|
- [ ] Seguridad: No mostrar tokens completos
|
||||||
|
- [ ] Seguridad: Solo usuario propietario puede ver/cerrar
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Dependencias
|
||||||
|
|
||||||
|
- **Bloqueantes:**
|
||||||
|
- US-AUTH-012: Gestión de Sesiones (tabla user_sessions)
|
||||||
|
- Sistema de geolocalización (de US-AUTH-012)
|
||||||
|
|
||||||
|
- **Relacionadas:**
|
||||||
|
- US-AUTH-002: Login (sesión inicial)
|
||||||
|
- US-AUTH-013: Cambio de contraseña (seguridad complementaria)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Definition of Ready (DoR)
|
||||||
|
|
||||||
|
- [ ] API contract definido y aprobado
|
||||||
|
- [ ] Mockups validados con UX
|
||||||
|
- [ ] Esquema de BD detallado
|
||||||
|
- [ ] Servicio de email configurado
|
||||||
|
- [ ] Estructura de logs de actividad definida
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Definition of Done (DoD)
|
||||||
|
|
||||||
|
- [ ] Endpoint GET /api/auth/devices implementado
|
||||||
|
- [ ] Endpoint GET /api/auth/devices/:id implementado
|
||||||
|
- [ ] Endpoint DELETE /api/auth/devices/:id implementado
|
||||||
|
- [ ] Tabla device_activity_log creada
|
||||||
|
- [ ] Logging de actividad funcional
|
||||||
|
- [ ] Página Settings/Security/Devices.tsx implementada
|
||||||
|
- [ ] Componentes DeviceCard y DeviceDetailsModal implementados
|
||||||
|
- [ ] Gráfico de actividad funcional
|
||||||
|
- [ ] Email de notificación configurable
|
||||||
|
- [ ] Tests unitarios con 80%+ cobertura
|
||||||
|
- [ ] Tests de integración pasando
|
||||||
|
- [ ] Tests E2E implementados
|
||||||
|
- [ ] Documentación actualizada
|
||||||
|
- [ ] QA aprobado en staging
|
||||||
|
- [ ] Deploy a producción exitoso
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Notas Técnicas
|
||||||
|
|
||||||
|
### Device Identification
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface DeviceInfo {
|
||||||
|
id: string; // session_id
|
||||||
|
browser: string; // "Chrome 130"
|
||||||
|
os: string; // "Windows 11"
|
||||||
|
device_type: "desktop" | "mobile" | "tablet" | "unknown";
|
||||||
|
device_name: string; // "Chrome on Windows"
|
||||||
|
ip_address: string;
|
||||||
|
location: {
|
||||||
|
city: string;
|
||||||
|
country: string;
|
||||||
|
latitude?: number;
|
||||||
|
longitude?: number;
|
||||||
|
};
|
||||||
|
user_agent: string;
|
||||||
|
is_current: boolean;
|
||||||
|
last_activity: Date;
|
||||||
|
created_at: Date;
|
||||||
|
expires_at: Date;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Browser Icons Mapping
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const BROWSER_ICONS = {
|
||||||
|
'Chrome': '🌐',
|
||||||
|
'Firefox': '🔴',
|
||||||
|
'Safari': '🧭',
|
||||||
|
'Edge': '🌌',
|
||||||
|
'Opera': '⭕',
|
||||||
|
'Mobile': '📱',
|
||||||
|
'Unknown': '❓'
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Activity Graph Data
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface ActivityDataPoint {
|
||||||
|
hour: number; // 0-23
|
||||||
|
date: string; // "2026-01-25"
|
||||||
|
request_count: number;
|
||||||
|
timestamp: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Graficar: últimas 24 horas con actividad por hora
|
||||||
|
// Líneas suave, mostrar puntos de máxima actividad
|
||||||
|
```
|
||||||
|
|
||||||
|
### Current Device Detection
|
||||||
|
|
||||||
|
El dispositivo "actual" se detecta comparando:
|
||||||
|
1. Session token actual en localStorage/cookie
|
||||||
|
2. Session ID del request
|
||||||
|
3. El primer dispositivo con `expires_at > now()` y session_id === actual es "Este dispositivo"
|
||||||
|
|
||||||
|
### Email Trigger
|
||||||
|
|
||||||
|
Se dispara email de "Nuevo dispositivo" cuando:
|
||||||
|
```typescript
|
||||||
|
const isNewDevice = !existingSession ||
|
||||||
|
(existingSession.device_name !== currentDevice.device_name) ||
|
||||||
|
(existingSession.ip_address !== currentDevice.ip_address);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Audit Trail
|
||||||
|
|
||||||
|
Cada acción se registra:
|
||||||
|
```
|
||||||
|
- Dispositivo X cerrado por usuario Y
|
||||||
|
- Timestamp: 2026-01-25 14:30:00
|
||||||
|
- Reason: manual_close
|
||||||
|
- IP del usuario que cerró: admin_ip
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Requerimientos Relacionados
|
||||||
|
|
||||||
|
- [RF-AUTH-007: Gestión de Dispositivos](../requerimientos/RF-AUTH-007-devices.md)
|
||||||
|
|
||||||
|
## Especificaciones Relacionadas
|
||||||
|
|
||||||
|
- [ET-AUTH-002: JWT Tokens](../especificaciones/ET-AUTH-002-jwt.md)
|
||||||
|
- [ET-AUTH-003: Database](../especificaciones/ET-AUTH-003-database.md)
|
||||||
|
- [ET-AUTH-006: Session Management](../especificaciones/ET-AUTH-006-sessions.md)
|
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,442 @@
|
|||||||
|
---
|
||||||
|
id: "US-ML-008"
|
||||||
|
title: "Ver Señal Ensemble"
|
||||||
|
type: "User Story"
|
||||||
|
status: "Pending"
|
||||||
|
priority: "Alta"
|
||||||
|
epic: "OQI-006"
|
||||||
|
project: "trading-platform"
|
||||||
|
story_points: 5
|
||||||
|
created_date: "2026-01-25"
|
||||||
|
updated_date: "2026-01-25"
|
||||||
|
---
|
||||||
|
|
||||||
|
# US-ML-008: Ver Señal Ensemble
|
||||||
|
|
||||||
|
## Metadata
|
||||||
|
|
||||||
|
| Campo | Valor |
|
||||||
|
|-------|-------|
|
||||||
|
| **ID** | US-ML-008 |
|
||||||
|
| **Épica** | OQI-006 - Señales ML y Predicciones |
|
||||||
|
| **Módulo** | ml-signals |
|
||||||
|
| **Prioridad** | P0 (Alta) |
|
||||||
|
| **Story Points** | 5 |
|
||||||
|
| **Sprint** | Sprint 8 |
|
||||||
|
| **Estado** | Pendiente |
|
||||||
|
| **Asignado a** | Por asignar |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Historia de Usuario
|
||||||
|
|
||||||
|
**Como** trader/inversor,
|
||||||
|
**quiero** ver la señal combinada (ensemble) de múltiples modelos ML con sus contribuciones individuales,
|
||||||
|
**para** tomar decisiones más informadas y confiar en predicciones robustas de múltiples fuentes.
|
||||||
|
|
||||||
|
## Descripción Detallada
|
||||||
|
|
||||||
|
El usuario debe poder ver una vista integrada que combina las predicciones de múltiples modelos ML (LSTM, RandomForest, SVM, etc.) en una señal consolidada (ensemble). La vista debe mostrar:
|
||||||
|
|
||||||
|
1. **Señal Ensemble Principal:** La decisión combinada (BUY/SELL/HOLD) con confianza agregada
|
||||||
|
2. **Confianza Combinada:** Porcentaje agregado considerando pesos de cada modelo
|
||||||
|
3. **Contribuciones Individuales:** Lista de modelos participantes con sus predicciones y pesos
|
||||||
|
4. **Scoring Detallado:** Score de cada modelo, peso asignado, contribución a la decisión final
|
||||||
|
5. **Votación de Modelos:** Cuántos modelos votaron por cada acción
|
||||||
|
6. **Consenso:** Nivel de acuerdo entre modelos
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Mockups/Wireframes
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────────────────────────┐
|
||||||
|
│ ENSEMBLE SIGNAL BTCUSDT │
|
||||||
|
├──────────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ ┌────────────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ ENSEMBLE DECISION │ │
|
||||||
|
│ ├────────────────────────────────────────────────────────────┤ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ Signal: 🟢 BUY │ │
|
||||||
|
│ │ Confidence: ████████░ 82% │ │
|
||||||
|
│ │ Score: 8.2/10 │ │
|
||||||
|
│ │ Consensus: STRONG (4/5 models agree) │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ Entry Price Avg: $89,450.00 │ │
|
||||||
|
│ │ TP1: $89,650 (+0.22%) | TP2: $89,850 (+0.45%) │ │
|
||||||
|
│ │ TP3: $90,050 (+0.67%) | SL: $89,150 (-0.33%) │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ Generated: 2026-01-25 10:30:15 UTC │ │
|
||||||
|
│ │ Expires: 2026-01-25 14:30:15 UTC (4h horizon) │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ └────────────────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌────────────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ MODEL CONTRIBUTIONS │ │
|
||||||
|
│ ├────────────────────────────────────────────────────────────┤ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ [Model Distribution] │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ 🟢 BUY: 4 models (80%) ████████░░ │ │
|
||||||
|
│ │ 🔴 SELL: 1 model (20%) ██░░░░░░░░ │ │
|
||||||
|
│ │ 🔵 HOLD: 0 models (0%) ░░░░░░░░░░ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ └────────────────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌────────────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ DETAILED MODEL VOTING │ │
|
||||||
|
│ ├────────────────────────────────────────────────────────────┤ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ Model Name Prediction Score Weight Contribution│ │
|
||||||
|
│ │ ─────────────────────────────────────────────────────────│ │
|
||||||
|
│ │ ✓ LSTM 🟢 BUY 87% 25% 21.75% │ │
|
||||||
|
│ │ ✓ RandomForest 🟢 BUY 85% 30% 25.50% │ │
|
||||||
|
│ │ ✓ SVM 🟢 BUY 78% 20% 15.60% │ │
|
||||||
|
│ │ ✓ Transformer 🟢 BUY 81% 15% 12.15% │ │
|
||||||
|
│ │ ✗ GradientBoosting 🔴 SELL 72% 10% -7.20%* │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ *Negative contribution indicates minority prediction │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ CONSENSUS ANALYSIS: │ │
|
||||||
|
│ │ • 4 out of 5 models predict BUY (strong agreement) │ │
|
||||||
|
│ │ • Confidence scores range from 72% to 87% (tight) │ │
|
||||||
|
│ │ • 1 outlier (GradientBoosting) predicts SELL │ │
|
||||||
|
│ │ • Overall consensus strength: 80% (STRONG) │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ └────────────────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌────────────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ HISTORICAL MODEL PERFORMANCE │ │
|
||||||
|
│ ├────────────────────────────────────────────────────────────┤ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ Model Name Accuracy Win Rate Last 7d Trend │ │
|
||||||
|
│ │ ─────────────────────────────────────────────────────────│ │
|
||||||
|
│ │ LSTM 84.2% 71.5% 72% ↑ Good │ │
|
||||||
|
│ │ RandomForest 82.1% 68.9% 69% ↑ Good │ │
|
||||||
|
│ │ SVM 79.8% 65.3% 66% → Stable │ │
|
||||||
|
│ │ Transformer 81.5% 70.2% 71% ↑ Good │ │
|
||||||
|
│ │ GradientBoosting 76.2% 61.4% 62% ↓ Declining│ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ Ensemble (Combined) 85.6% 75.1% 76% ↑ Excellent│ │
|
||||||
|
│ │ │ │
|
||||||
|
│ └────────────────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ [View Technical Context] [Compare Models] [View Signal History] │
|
||||||
|
│ │
|
||||||
|
└──────────────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
┌──────────────────────────────────────────────────────────────────┐
|
||||||
|
│ MODEL COMPARISON CHART │
|
||||||
|
├──────────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ Prediction Heatmap: │
|
||||||
|
│ │
|
||||||
|
│ LSTM RF SVM TRF GB ENS │
|
||||||
|
│ BTCUSDT [87%] [85%][78%][81%][72%][82%] 🟢 BUY │
|
||||||
|
│ ETHUSDT [84%] [86%][75%][80%][70%][81%] 🟢 BUY │
|
||||||
|
│ XRPUSDT [81%] [79%][72%][76%][68%][77%] 🟢 BUY │
|
||||||
|
│ SOLUSDT [76%] [74%][69%][71%][65%][72%] 🟡 HOLD (Weak) │
|
||||||
|
│ │
|
||||||
|
│ Legend: ENS = Ensemble Vote │
|
||||||
|
│ │
|
||||||
|
│ Consensus Strength Graph: │
|
||||||
|
│ │
|
||||||
|
│ 100% │ │
|
||||||
|
│ │ ╱─╲ │
|
||||||
|
│ 80% │ ╱─ ╲╱╲ │
|
||||||
|
│ │ ╱─ ╲─╲ │
|
||||||
|
│ 60% │╱─ ╲─── │
|
||||||
|
│ │────────────────────► time │
|
||||||
|
│ 40% │ │
|
||||||
|
│ └────────────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
└──────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Criterios de Aceptación
|
||||||
|
|
||||||
|
**Escenario 1: Ver señal ensemble**
|
||||||
|
```gherkin
|
||||||
|
DADO que el usuario está autenticado en Trading Platform
|
||||||
|
CUANDO navega a "ML Signals > Ensemble" para un símbolo (ej: BTCUSDT)
|
||||||
|
ENTONCES ve la señal ensemble con:
|
||||||
|
- Decisión principal (BUY/SELL/HOLD)
|
||||||
|
- Confianza combinada (%)
|
||||||
|
- Score de 0 a 10
|
||||||
|
- Nivel de consenso (STRONG/MODERATE/WEAK)
|
||||||
|
- Número de modelos votando por cada acción
|
||||||
|
Y se muestra el timestamp de generación y expiración
|
||||||
|
```
|
||||||
|
|
||||||
|
**Escenario 2: Ver contribuciones de modelos**
|
||||||
|
```gherkin
|
||||||
|
DADO que el usuario está viendo una señal ensemble
|
||||||
|
CUANDO expande la sección "Model Contributions"
|
||||||
|
ENTONCES ve una tabla con:
|
||||||
|
- Nombre de cada modelo
|
||||||
|
- Predicción individual (BUY/SELL/HOLD)
|
||||||
|
- Score/confianza de cada modelo (%)
|
||||||
|
- Peso asignado a cada modelo (%)
|
||||||
|
- Contribución calculada al resultado final
|
||||||
|
Y las filas están ordenadas por peso (mayor a menor)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Escenario 3: Ver votación de modelos**
|
||||||
|
```gherkin
|
||||||
|
DADO que existen 5 modelos en el ensemble
|
||||||
|
CUANDO veo la distribución de votos
|
||||||
|
ENTONCES se muestra:
|
||||||
|
- Gráfico de barras: cuántos modelos votaron por cada acción
|
||||||
|
- Porcentaje: 80% BUY, 20% SELL, 0% HOLD
|
||||||
|
- Indicador visual de consenso (fuerte/moderado/débil)
|
||||||
|
- Descripción: "4 out of 5 models agree on BUY"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Escenario 4: Ver performance histórico de modelos**
|
||||||
|
```gherkin
|
||||||
|
DADO que el usuario quiere evaluar fiabilidad de cada modelo
|
||||||
|
CUANDO expande "Historical Model Performance"
|
||||||
|
ENTONCES ve para cada modelo:
|
||||||
|
- Accuracy (%)
|
||||||
|
- Win Rate en los últimos 7 días (%)
|
||||||
|
- Tendencia (↑/→/↓)
|
||||||
|
- Comparación con ensemble
|
||||||
|
```
|
||||||
|
|
||||||
|
**Escenario 5: Detectar outliers**
|
||||||
|
```gherkin
|
||||||
|
DADO que 1 modelo predice diferente al ensemble (ej: SELL vs BUY)
|
||||||
|
CUANDO veo la tabla de contribuciones
|
||||||
|
ENTONCES:
|
||||||
|
- Se destaca visualmente con icono ✗ (en lugar de ✓)
|
||||||
|
- Se muestra con color diferente (rojo vs verde)
|
||||||
|
- Se incluye nota: "Minority prediction"
|
||||||
|
- Contribución se resta del total (negativa)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Escenario 6: Comparar modelos lado a lado**
|
||||||
|
```gherkin
|
||||||
|
DADO que quiero evaluar predicciones de todos los modelos
|
||||||
|
CUANDO hago click en "Compare Models"
|
||||||
|
ENTONCES se abre una vista con:
|
||||||
|
- Heatmap de predicciones por símbolo
|
||||||
|
- Scores de cada modelo lado a lado
|
||||||
|
- Tendencia de consenso en tiempo (gráfico)
|
||||||
|
- Botón para exportar comparación
|
||||||
|
```
|
||||||
|
|
||||||
|
## Criterios Adicionales
|
||||||
|
|
||||||
|
- [ ] Debe cargar en menos de 1.5 segundos
|
||||||
|
- [ ] Soportar 5-10 modelos simultáneamente
|
||||||
|
- [ ] Actualizar automáticamente cada 30 segundos (si hay nuevas señales)
|
||||||
|
- [ ] Ser responsive en mobile
|
||||||
|
- [ ] Mostrar claramente cuál es el modelo más confiable
|
||||||
|
- [ ] Detectar y resaltar predicciones outliers
|
||||||
|
- [ ] Permitir ponderar dinámicamente los modelos (admin)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tareas Técnicas
|
||||||
|
|
||||||
|
**Backend:**
|
||||||
|
- [ ] BE-ML-016: Crear endpoint GET /api/ml/ensemble/:symbol
|
||||||
|
- [ ] BE-ML-017: Implementar cálculo de confianza combinada (weighted average)
|
||||||
|
- [ ] BE-ML-018: Implementar votación y consenso de modelos
|
||||||
|
- [ ] BE-ML-019: Obtener scores individuales de cada modelo
|
||||||
|
- [ ] BE-ML-020: Calcular performance histórico de modelos
|
||||||
|
- [ ] BE-ML-021: Endpoint GET /api/ml/ensemble/:symbol/models (detalle)
|
||||||
|
- [ ] BE-ML-022: Endpoint GET /api/ml/ensemble/comparison (heatmap)
|
||||||
|
|
||||||
|
**Frontend:**
|
||||||
|
- [ ] FE-ML-035: Crear componente `EnsembleSignal.tsx`
|
||||||
|
- [ ] FE-ML-036: Crear componente `ModelVoting.tsx`
|
||||||
|
- [ ] FE-ML-037: Crear componente `ModelContributions.tsx` (tabla)
|
||||||
|
- [ ] FE-ML-038: Crear componente `ConsensusIndicator.tsx`
|
||||||
|
- [ ] FE-ML-039: Crear componente `ModelPerformance.tsx`
|
||||||
|
- [ ] FE-ML-040: Crear componente `ModelComparison.tsx` (heatmap)
|
||||||
|
- [ ] FE-ML-041: Implementar auto-refresh cada 30 segundos
|
||||||
|
- [ ] FE-ML-042: Agregar indicadores visuales para outliers
|
||||||
|
|
||||||
|
**Tests:**
|
||||||
|
- [ ] TEST-ML-016: Test de cálculo de confianza combinada
|
||||||
|
- [ ] TEST-ML-017: Test de votación de modelos
|
||||||
|
- [ ] TEST-ML-018: Test E2E de vista ensemble
|
||||||
|
- [ ] TEST-ML-019: Test de detección de outliers
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Dependencias
|
||||||
|
|
||||||
|
**Depende de:**
|
||||||
|
- [ ] RF-ML-003: Múltiples modelos ML entrenados (LSTM, RF, SVM, Transformer, GB)
|
||||||
|
- [ ] RF-ML-004: Sistema de pesos y scoring de modelos
|
||||||
|
- [ ] US-ML-001: Ver señal básica - Estado: Completado
|
||||||
|
|
||||||
|
**Bloquea:**
|
||||||
|
- [ ] US-ML-009: Configurar ensemble (admin puede cambiar pesos)
|
||||||
|
- [ ] US-ML-010: Histórico de ensemble signals
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Notas Técnicas
|
||||||
|
|
||||||
|
**Endpoints involucrados:**
|
||||||
|
| Método | Endpoint | Descripción |
|
||||||
|
|--------|----------|-------------|
|
||||||
|
| GET | /api/ml/ensemble/:symbol | Obtener señal ensemble actual |
|
||||||
|
| GET | /api/ml/ensemble/:symbol/models | Detalle de cada modelo |
|
||||||
|
| GET | /api/ml/ensemble/comparison | Heatmap de predicciones |
|
||||||
|
| GET | /api/ml/ensemble/:symbol/performance | Performance histórico |
|
||||||
|
|
||||||
|
**Query Parameters - Ensemble Signal:**
|
||||||
|
```
|
||||||
|
GET /api/ml/ensemble/BTCUSDT?
|
||||||
|
include_performance=true&
|
||||||
|
include_comparison=false
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response esperado:**
|
||||||
|
```typescript
|
||||||
|
interface EnsembleSignalResponse {
|
||||||
|
ensemble: {
|
||||||
|
symbol: string;
|
||||||
|
action: 'BUY' | 'SELL' | 'HOLD';
|
||||||
|
confidence: number; // weighted average, 0-100
|
||||||
|
score: number; // 0-10
|
||||||
|
consensus_strength: 'STRONG' | 'MODERATE' | 'WEAK';
|
||||||
|
timestamp: string;
|
||||||
|
expires_at: string;
|
||||||
|
|
||||||
|
entry_price: number;
|
||||||
|
tp1: number;
|
||||||
|
tp2: number;
|
||||||
|
tp3: number;
|
||||||
|
sl: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
model_votes: {
|
||||||
|
buy_count: number;
|
||||||
|
sell_count: number;
|
||||||
|
hold_count: number;
|
||||||
|
total_models: number;
|
||||||
|
buy_percentage: number;
|
||||||
|
sell_percentage: number;
|
||||||
|
hold_percentage: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
models: ModelContribution[];
|
||||||
|
|
||||||
|
consensus_analysis: {
|
||||||
|
all_agree: boolean;
|
||||||
|
minority_models: string[];
|
||||||
|
agreement_percentage: number;
|
||||||
|
description: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ModelContribution {
|
||||||
|
model_id: string;
|
||||||
|
model_name: string;
|
||||||
|
prediction: 'BUY' | 'SELL' | 'HOLD';
|
||||||
|
score: number; // 0-100
|
||||||
|
confidence: number; // 0-100
|
||||||
|
weight: number; // 0-100, suma con otros = 100
|
||||||
|
contribution: number; // score * weight
|
||||||
|
is_outlier: boolean;
|
||||||
|
|
||||||
|
performance?: ModelPerformance;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ModelPerformance {
|
||||||
|
accuracy: number; // last 100 predictions
|
||||||
|
win_rate: number; // last 7 days
|
||||||
|
total_signals: number;
|
||||||
|
correct_signals: number;
|
||||||
|
trend: 'UP' | 'STABLE' | 'DOWN';
|
||||||
|
last_updated: string;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Cálculos:**
|
||||||
|
```typescript
|
||||||
|
// Confianza combinada (weighted average)
|
||||||
|
ensemble_confidence = sum(model.score * model.weight) / 100
|
||||||
|
|
||||||
|
// Votación
|
||||||
|
buy_percentage = (buy_count / total_models) * 100
|
||||||
|
|
||||||
|
// Contribución de cada modelo
|
||||||
|
contribution = model.score * model.weight / 100
|
||||||
|
|
||||||
|
// Consenso
|
||||||
|
consensus_strength = all_models_agree ? 'STRONG' :
|
||||||
|
buy_percentage > 60 ? 'MODERATE' : 'WEAK'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Componentes UI:**
|
||||||
|
- `EnsembleSignal`: Container principal
|
||||||
|
- `EnsembleHeader`: Decisión + confianza + consenso
|
||||||
|
- `ModelVoting`: Distribución de votos (barras)
|
||||||
|
- `ModelContributions`: Tabla detallada
|
||||||
|
- `ConsensusIndicator`: Visual de acuerdo
|
||||||
|
- `ModelPerformance`: Stats históricos
|
||||||
|
- `ModelComparison`: Heatmap
|
||||||
|
- `OutlierBadge`: Indicador de predicción minoritaria
|
||||||
|
|
||||||
|
**Estado (Zustand):**
|
||||||
|
```typescript
|
||||||
|
interface EnsembleStore {
|
||||||
|
ensemble: EnsembleSignalResponse | null;
|
||||||
|
selectedSymbol: string;
|
||||||
|
isLoading: boolean;
|
||||||
|
lastUpdated: string;
|
||||||
|
autoRefreshInterval: number; // 30000 ms
|
||||||
|
|
||||||
|
fetchEnsemble: (symbol: string) => Promise<void>;
|
||||||
|
setAutoRefresh: (enabled: boolean) => void;
|
||||||
|
compareModels: () => void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Definition of Ready (DoR)
|
||||||
|
|
||||||
|
- [x] Historia claramente escrita (quién, qué, por qué)
|
||||||
|
- [x] Criterios de aceptación definidos
|
||||||
|
- [x] Story points estimados
|
||||||
|
- [x] Dependencias identificadas
|
||||||
|
- [x] Modelos ML disponibles (LSTM, RF, SVM, Transformer, GB)
|
||||||
|
- [x] Diseño/mockup disponible
|
||||||
|
|
||||||
|
## Definition of Done (DoD)
|
||||||
|
|
||||||
|
- [ ] Código implementado según criterios
|
||||||
|
- [ ] Tests unitarios escritos y pasando (>80% coverage)
|
||||||
|
- [ ] Tests E2E pasando
|
||||||
|
- [ ] Code review aprobado
|
||||||
|
- [ ] Documentación actualizada
|
||||||
|
- [ ] QA aprobado en staging
|
||||||
|
- [ ] Auto-refresh funciona sin memory leaks
|
||||||
|
- [ ] Performance: carga < 1.5s
|
||||||
|
- [ ] Mobile responsive
|
||||||
|
- [ ] Outliers detectados y resaltados correctamente
|
||||||
|
- [ ] Desplegado en producción
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Historial de Cambios
|
||||||
|
|
||||||
|
| Fecha | Cambio | Autor |
|
||||||
|
|-------|--------|-------|
|
||||||
|
| 2026-01-25 | Creación | Requirements-Analyst |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Creada por:** Requirements-Analyst
|
||||||
|
**Fecha:** 2026-01-25
|
||||||
|
**Última actualización:** 2026-01-25
|
||||||
@ -0,0 +1,495 @@
|
|||||||
|
---
|
||||||
|
id: "US-ML-009"
|
||||||
|
title: "Ver Análisis ICT"
|
||||||
|
type: "User Story"
|
||||||
|
status: "Pending"
|
||||||
|
priority: "Alta"
|
||||||
|
epic: "OQI-006"
|
||||||
|
project: "trading-platform"
|
||||||
|
story_points: 5
|
||||||
|
created_date: "2026-01-25"
|
||||||
|
updated_date: "2026-01-25"
|
||||||
|
---
|
||||||
|
|
||||||
|
# US-ML-009: Ver Análisis ICT
|
||||||
|
|
||||||
|
## Metadata
|
||||||
|
|
||||||
|
| Campo | Valor |
|
||||||
|
|-------|-------|
|
||||||
|
| **ID** | US-ML-009 |
|
||||||
|
| **Épica** | OQI-006 - Señales ML y Predicciones |
|
||||||
|
| **Módulo** | ml-signals |
|
||||||
|
| **Prioridad** | P0 (Alta) |
|
||||||
|
| **Story Points** | 5 |
|
||||||
|
| **Sprint** | Sprint 8 |
|
||||||
|
| **Estado** | Pendiente |
|
||||||
|
| **Asignado a** | Por asignar |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Historia de Usuario
|
||||||
|
|
||||||
|
**Como** trader/inversor,
|
||||||
|
**quiero** ver el análisis ICT (Inner Circle Trader) que incluye zonas de liquidez, fair value gaps, y order blocks,
|
||||||
|
**para** identificar puntos de entrada/salida de alto potencial basados en la metodología ICT.
|
||||||
|
|
||||||
|
## Descripción Detallada
|
||||||
|
|
||||||
|
El usuario debe poder visualizar en el chart de precios un análisis completo de la metodología ICT que incluya:
|
||||||
|
|
||||||
|
1. **Zonas de Liquidez (Liquidity Zones)**: Áreas donde se acumulan órdenes de compra/venta
|
||||||
|
2. **Fair Value Gaps (FVGs)**: Brechas de precio donde no hay transacciones (gaps de aire)
|
||||||
|
3. **Order Blocks (OB)**: Bloques de órdenes que actúan como soporte/resistencia
|
||||||
|
4. **Puntos de Interés (POI)**: Zonas clave de reversión y manipulación de precios
|
||||||
|
|
||||||
|
Cada elemento debe ser interactivo mostrando detalles como tamaño de la zona, potencia, fechas de formación, y probabilidad de inversión de precio.
|
||||||
|
|
||||||
|
## Mockups/Wireframes
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────────────────────────┐
|
||||||
|
│ ICT ANALYSIS - BTCUSDT 4h │
|
||||||
|
├──────────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ Controls: │
|
||||||
|
│ [Liquidity Zones ☑] [FVGs ☑] [Order Blocks ☑] [POI ☑] │
|
||||||
|
│ [Bias: Bullish ▼] [Threshold: Medium ▼] [Legend] │
|
||||||
|
│ │
|
||||||
|
│ ══════════════════════════════════════════════════════════════ │
|
||||||
|
│ │
|
||||||
|
│ ┌────────────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ BTCUSDT / 4H Chart │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ ╱╲ │ │
|
||||||
|
│ │ ╱┌──┐╲ 🔵 Order Block │ │
|
||||||
|
│ │ ╱╲ ╱│ 🔵 │╲ (Bullish, 8.2/10) │ │
|
||||||
|
│ │ ╱┌──┐╲ ╱ │ │ ╲ Entry: $89,200 │ │
|
||||||
|
│ │ ╱ │ 🟢│ ╲ ╱ │ │ ╲ Strength: 95% │ │
|
||||||
|
│ │ ╱┌───┼────┼───╲╱ │ │ ╲ │ │
|
||||||
|
│ │ ╱ │ 🟡 │ 🔴 │ ║ │ │ ╲ ☑ Liquidity Zone │ │
|
||||||
|
│ │╱───┼────┼────┼──║──────┼────┼──────╲ Above: $90,100 │ │
|
||||||
|
│ │─────┼────┼────┼──║──────┼────┼─────── Strength: High │ │
|
||||||
|
│ │ │ │ │ ║ │ │ Probability: 75% │ │
|
||||||
|
│ │ │ │ │ ║ │ 🔘 │ │ │
|
||||||
|
│ │ │ │ │ ║ │ 🟣 │ 🟡 Fair Value Gap │
|
||||||
|
│ │ └────┴────┴──║──────┴────┴── $89,850 - $89,650 │
|
||||||
|
│ │ ║ Gap Size: 0.2% │
|
||||||
|
│ │ ║ Type: Bullish Breaker │
|
||||||
|
│ │ │
|
||||||
|
│ │ [Zoom] [Reset] [Compare] [Backtest This] [Alerts] │
|
||||||
|
│ │ │
|
||||||
|
│ └────────────────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ICT ELEMENTS FOUND (16) │
|
||||||
|
│ ═════════════════════════════════════════════════════════════ │
|
||||||
|
│ │
|
||||||
|
│ ┌─ LIQUIDITY ZONES (4) ─────────────────────────────────────┐ │
|
||||||
|
│ │ ☑ Above Current Price: │ │
|
||||||
|
│ │ • $90,100 - $90,500 (Strength: 94%) - Last 3H ago │ │
|
||||||
|
│ │ • $91,200 - $91,800 (Strength: 87%) - Last 8H ago │ │
|
||||||
|
│ │ ☑ Below Current Price: │ │
|
||||||
|
│ │ • $88,500 - $88,200 (Strength: 91%) - Last 2H ago │ │
|
||||||
|
│ └────────────────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌─ FAIR VALUE GAPS (3) ──────────────────────────────────────┐ │
|
||||||
|
│ │ • $89,850 - $89,650 (Bullish Breaker, Gap Size: 0.2%) │ │
|
||||||
|
│ │ ├─ Formed: 45 min ago │ │
|
||||||
|
│ │ ├─ Status: Unmitigated (Not touched by price) │ │
|
||||||
|
│ │ └─ Probability: High that will be filled in 4-8H │ │
|
||||||
|
│ │ • $87,900 - $87,500 (Bearish Breaker, Gap Size: 0.4%) │ │
|
||||||
|
│ │ ├─ Formed: 6H ago │ │
|
||||||
|
│ │ ├─ Status: Partially Mitigated │ │
|
||||||
|
│ │ └─ Probability: Medium that will be retested │ │
|
||||||
|
│ │ • $90,200 - $89,950 (Bullish Breaker, Gap Size: 0.25%) │ │
|
||||||
|
│ │ ├─ Formed: 2H ago │ │
|
||||||
|
│ │ ├─ Status: Unmitigated │ │
|
||||||
|
│ │ └─ Probability: Very High (needs mitigation) │ │
|
||||||
|
│ └────────────────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌─ ORDER BLOCKS (5) ────────────────────────────────────────┐ │
|
||||||
|
│ │ ☑ Bullish Order Block: │ │
|
||||||
|
│ │ • Entry: $89,200 - $89,350 (Strength: 95%, Score: 8.2) │ │
|
||||||
|
│ │ • Formed during strong uptrend 3H ago │ │
|
||||||
|
│ │ • Touch points: 2 (May retry) │ │
|
||||||
|
│ │ • Next resistance: $90,100 (Liquidity Zone) │ │
|
||||||
|
│ │ ☑ Bearish Order Block: │ │
|
||||||
|
│ │ • Entry: $90,500 - $90,650 (Strength: 88%, Score: 7.9) │ │
|
||||||
|
│ │ • Formed during downtrend 6H ago │ │
|
||||||
|
│ │ • Touch points: 1 (Fresh block) │ │
|
||||||
|
│ │ • Next support: $89,500 (FVG area) │ │
|
||||||
|
│ └────────────────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌─ POINTS OF INTEREST (4) ──────────────────────────────────┐ │
|
||||||
|
│ │ • $90,100 (Swing High + Liquidity Zone) │ │
|
||||||
|
│ │ ├─ Type: Supply/Resistance │ │
|
||||||
|
│ │ ├─ Confluence: 3 levels │ │
|
||||||
|
│ │ └─ Probability of reversal: 82% │ │
|
||||||
|
│ │ • $88,500 (Swing Low + Liquidity Zone) │ │
|
||||||
|
│ │ ├─ Type: Demand/Support │ │
|
||||||
|
│ │ ├─ Confluence: 2 levels │ │
|
||||||
|
│ │ └─ Probability of bounce: 76% │ │
|
||||||
|
│ └────────────────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
└──────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Criterios de Aceptación
|
||||||
|
|
||||||
|
**Escenario 1: Mostrar análisis ICT completo**
|
||||||
|
```gherkin
|
||||||
|
DADO que el usuario está en la pantalla de chart
|
||||||
|
CUANDO abre la sección "ICT Analysis"
|
||||||
|
ENTONCES se visualizan en el chart:
|
||||||
|
- Zonas de liquidez (coloreadas y etiquetadas)
|
||||||
|
- Fair Value Gaps (dibujados como rectángulos vacíos)
|
||||||
|
- Order Blocks (rectángulos rellenos con poder)
|
||||||
|
- Puntos de Interés (círculos/estrelas destacadas)
|
||||||
|
Y cada elemento tiene color diferenciado (bullish/bearish)
|
||||||
|
Y se muestra un panel lateral con listado de todos los elementos
|
||||||
|
```
|
||||||
|
|
||||||
|
**Escenario 2: Filtrar elementos ICT**
|
||||||
|
```gherkin
|
||||||
|
DADO que el usuario está viendo el análisis ICT
|
||||||
|
CUANDO desactiva un tipo de elemento (ej: FVGs)
|
||||||
|
ENTONCES desaparecen del chart
|
||||||
|
Y se actualiza el contador en el panel lateral
|
||||||
|
Y el resto de elementos se mantienen visibles
|
||||||
|
```
|
||||||
|
|
||||||
|
**Escenario 3: Ver detalles de zona de liquidez**
|
||||||
|
```gherkin
|
||||||
|
DADO que el usuario está en ICT Analysis
|
||||||
|
CUANDO hace hover/click en una zona de liquidez
|
||||||
|
ENTONCES aparece un tooltip/modal con:
|
||||||
|
- Rango de precios (high-low)
|
||||||
|
- Fortaleza/poder de la zona (%)
|
||||||
|
- Fecha de formación
|
||||||
|
- Número de toques/veces que fue tocada
|
||||||
|
- Probabilidad de que el precio vuelva a esa zona
|
||||||
|
- Botón para crear orden en esa zona
|
||||||
|
```
|
||||||
|
|
||||||
|
**Escenario 4: Ver detalles de Fair Value Gap**
|
||||||
|
```gherkin
|
||||||
|
DADO que el usuario está en ICT Analysis
|
||||||
|
CUANDO hace click en un FVG
|
||||||
|
ENTONCES aparece un modal con:
|
||||||
|
- Rango del gap (precio alto - precio bajo)
|
||||||
|
- Tipo de gap (Bullish Breaker / Bearish Breaker / Continuation)
|
||||||
|
- Tamaño del gap (en pips/%)
|
||||||
|
- Estado (Unmitigated / Partially mitigated / Fully mitigated)
|
||||||
|
- Fecha de formación
|
||||||
|
- Probabilidad de que se cierre el gap (%)
|
||||||
|
- Momento estimado de cierre
|
||||||
|
- [Ver en chart] [Crear alerta] [Crear orden]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Escenario 5: Ver detalles de Order Block**
|
||||||
|
```gherkin
|
||||||
|
DADO que el usuario está en ICT Analysis
|
||||||
|
CUANDO hace click en un Order Block
|
||||||
|
ENTONCES aparece un modal con:
|
||||||
|
- Rango de entrada del bloque
|
||||||
|
- Tipo (Bullish / Bearish)
|
||||||
|
- Score de fortaleza (1-10)
|
||||||
|
- Potencia del bloque (%)
|
||||||
|
- Número de veces que fue tocado
|
||||||
|
- Formación: qué vela/candle lo creó
|
||||||
|
- Próxima resistencia/soporte
|
||||||
|
- [Crear orden en este bloque] [Crear alerta]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Escenario 6: Cambiar timeframe**
|
||||||
|
```gherkin
|
||||||
|
DADO que el usuario está en ICT Analysis
|
||||||
|
CUANDO cambia el timeframe (ej: 1H → 4H → 1D)
|
||||||
|
ENTONCES se recalcula y redibuja todo el análisis ICT
|
||||||
|
Y los elementos cambian según el timeframe elegido
|
||||||
|
Y el API actualiza con los datos del nuevo timeframe
|
||||||
|
```
|
||||||
|
|
||||||
|
**Escenario 7: Crear alerta en zona ICT**
|
||||||
|
```gherkin
|
||||||
|
DADO que el usuario está viendo un elemento ICT
|
||||||
|
CUANDO hace click en "Crear alerta" desde el modal
|
||||||
|
ENTONCES se abre un formulario para crear una alerta:
|
||||||
|
- Precio de trigger (pre-rellenado)
|
||||||
|
- Tipo de alerta (email/push/SMS)
|
||||||
|
- Mensaje personalizado
|
||||||
|
CUANDO confirma
|
||||||
|
ENTONCES se crea la alerta
|
||||||
|
Y se muestra confirmación "Alerta creada para $90,100"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Escenario 8: Comparar análisis entre timeframes**
|
||||||
|
```gherkin
|
||||||
|
DADO que el usuario está en ICT Analysis
|
||||||
|
CUANDO hace click en "Compare"
|
||||||
|
ENTONCES se abre una vista con dos charts lado a lado:
|
||||||
|
- Izquierda: timeframe actual (ej: 4H)
|
||||||
|
- Derecha: timeframe diferente (ej: 1D)
|
||||||
|
Y ambos muestran su análisis ICT correspondiente
|
||||||
|
Y se resaltan confluyencias entre timeframes
|
||||||
|
```
|
||||||
|
|
||||||
|
**Escenario 9: Backtesting de zona ICT**
|
||||||
|
```gherkin
|
||||||
|
DADO que el usuario está viendo un Order Block o FVG
|
||||||
|
CUANDO hace click en "Backtest This"
|
||||||
|
ENTONCES se abre un análisis histórico mostrando:
|
||||||
|
- Cuántas veces el precio tocó esta zona en el pasado
|
||||||
|
- Cuántas veces resultó en reversión
|
||||||
|
- Ganancia promedio si se hubiera operado en esta zona
|
||||||
|
- Tasa de éxito (%)
|
||||||
|
- Gráfico histórico de touch points
|
||||||
|
```
|
||||||
|
|
||||||
|
## Criterios Adicionales
|
||||||
|
|
||||||
|
- [ ] El análisis debe cargar en menos de 1.5 segundos
|
||||||
|
- [ ] Debe soportar todos los timeframes (1m, 5m, 15m, 1h, 4h, 1d, 1w)
|
||||||
|
- [ ] Debe recalcularse automáticamente cada nueva vela
|
||||||
|
- [ ] Los colores deben ser customizables por tema (light/dark)
|
||||||
|
- [ ] Bullish elements en verde/azul, Bearish en rojo/naranja
|
||||||
|
- [ ] Responsive en tablets y mobile (scrollable chart)
|
||||||
|
- [ ] Performance: máximo 50 elementos ICT dibujados simultáneamente
|
||||||
|
- [ ] Debe funcionar sin valores reales de market data si es necesario (mock data)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tareas Técnicas
|
||||||
|
|
||||||
|
**Backend (FastAPI - ml-engine):**
|
||||||
|
- [ ] BE-ML-016: Crear endpoint GET /api/ml/ict/:symbol
|
||||||
|
- [ ] BE-ML-017: Implementar análisis de Liquidity Zones
|
||||||
|
- [ ] BE-ML-018: Implementar detección de Fair Value Gaps
|
||||||
|
- [ ] BE-ML-019: Implementar detección de Order Blocks
|
||||||
|
- [ ] BE-ML-020: Implementar cálculo de Points of Interest
|
||||||
|
- [ ] BE-ML-021: Implementar score/strength para cada elemento
|
||||||
|
- [ ] BE-ML-022: Endpoint GET /api/ml/ict/:symbol/compare (compare timeframes)
|
||||||
|
- [ ] BE-ML-023: Implementar caching de análisis ICT
|
||||||
|
|
||||||
|
**Python ML Service:**
|
||||||
|
- [ ] ML-ICT-001: Desarrollar módulo ICT detector
|
||||||
|
- [ ] ML-ICT-002: Algoritmo de identificación de Liquidity Zones
|
||||||
|
- [ ] ML-ICT-003: Algoritmo de detección de FVGs
|
||||||
|
- [ ] ML-ICT-004: Algoritmo de detección de Order Blocks
|
||||||
|
- [ ] ML-ICT-005: Algoritmo de cálculo de POI (Points of Interest)
|
||||||
|
- [ ] ML-ICT-006: Validación y backtesting de elementos ICT
|
||||||
|
- [ ] ML-ICT-007: Tests unitarios de detección
|
||||||
|
|
||||||
|
**Frontend (React):**
|
||||||
|
- [ ] FE-ML-035: Crear componente `ICTAnalysis.tsx`
|
||||||
|
- [ ] FE-ML-036: Crear componente `ICTChart.tsx` (integración lightweight-charts)
|
||||||
|
- [ ] FE-ML-037: Crear componente `ICTControls.tsx` (filtros y toggles)
|
||||||
|
- [ ] FE-ML-038: Crear componente `ICTSidebar.tsx` (listado de elementos)
|
||||||
|
- [ ] FE-ML-039: Crear componente `LiquidityZoneModal.tsx`
|
||||||
|
- [ ] FE-ML-040: Crear componente `FVGModal.tsx`
|
||||||
|
- [ ] FE-ML-041: Crear componente `OrderBlockModal.tsx`
|
||||||
|
- [ ] FE-ML-042: Crear componente `POIModal.tsx`
|
||||||
|
- [ ] FE-ML-043: Crear componente `ComparisonChart.tsx`
|
||||||
|
- [ ] FE-ML-044: Crear componente `BacktestModal.tsx`
|
||||||
|
- [ ] FE-ML-045: Implementar dibujado en lightweight-charts
|
||||||
|
- [ ] FE-ML-046: Implementar state management (Zustand) para ICT data
|
||||||
|
- [ ] FE-ML-047: Implementar tooltips interactivos
|
||||||
|
- [ ] FE-ML-048: Implementar colores customizables por tema
|
||||||
|
|
||||||
|
**Tests:**
|
||||||
|
- [ ] TEST-ML-016: Test detección de Liquidity Zones
|
||||||
|
- [ ] TEST-ML-017: Test detección de FVGs
|
||||||
|
- [ ] TEST-ML-018: Test detección de Order Blocks
|
||||||
|
- [ ] TEST-ML-019: Test E2E de análisis completo
|
||||||
|
- [ ] TEST-ML-020: Test de performance (<1.5s load)
|
||||||
|
|
||||||
|
**Documentación:**
|
||||||
|
- [ ] DOC-ICT-001: Guía de análisis ICT en la plataforma
|
||||||
|
- [ ] DOC-ICT-002: Explicación de cada elemento ICT
|
||||||
|
- [ ] DOC-ICT-003: Cómo interpretar las métricas
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Dependencias
|
||||||
|
|
||||||
|
**Depende de:**
|
||||||
|
- [ ] RF-ML-001: Integración de datos de mercado (OHLCV)
|
||||||
|
- [ ] US-ML-001: Ver chart de trading
|
||||||
|
- [ ] US-ML-002: Ver señal en chart
|
||||||
|
- [ ] OQI-003: Trading Charts (infraestructura base)
|
||||||
|
|
||||||
|
**Bloquea:**
|
||||||
|
- [ ] US-ML-010: Alertas ICT
|
||||||
|
- [ ] US-ML-011: Backtesting de estrategia ICT
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Notas Técnicas
|
||||||
|
|
||||||
|
**Endpoints involucrados:**
|
||||||
|
| Método | Endpoint | Descripción |
|
||||||
|
|--------|----------|-------------|
|
||||||
|
| GET | /api/ml/ict/:symbol | Obtener análisis ICT para símbolo |
|
||||||
|
| GET | /api/ml/ict/:symbol/timeframe/:tf | Análisis ICT por timeframe |
|
||||||
|
| GET | /api/ml/ict/:symbol/compare | Comparar análisis entre TFs |
|
||||||
|
| POST | /api/ml/ict/:symbol/backtest | Backtesting de zona ICT |
|
||||||
|
| GET | /api/alerts/:symbol/ict | Obtener alertas ICT activas |
|
||||||
|
|
||||||
|
**Query Parameters - ICT Analysis:**
|
||||||
|
```
|
||||||
|
GET /api/ml/ict/BTCUSDT?
|
||||||
|
timeframe=4h&
|
||||||
|
include=liquidity_zones,fvgs,order_blocks,poi&
|
||||||
|
threshold=medium&
|
||||||
|
bias=bullish
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response esperado:**
|
||||||
|
```typescript
|
||||||
|
interface ICTAnalysisResponse {
|
||||||
|
symbol: string;
|
||||||
|
timeframe: string;
|
||||||
|
timestamp: string;
|
||||||
|
analysis: {
|
||||||
|
liquidity_zones: LiquidityZone[];
|
||||||
|
fair_value_gaps: FairValueGap[];
|
||||||
|
order_blocks: OrderBlock[];
|
||||||
|
points_of_interest: PointOfInterest[];
|
||||||
|
};
|
||||||
|
summary: {
|
||||||
|
total_elements: number;
|
||||||
|
bullish_elements: number;
|
||||||
|
bearish_elements: number;
|
||||||
|
strongest_level: number;
|
||||||
|
nearest_liquidity_up: number;
|
||||||
|
nearest_liquidity_down: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LiquidityZone {
|
||||||
|
id: string;
|
||||||
|
type: 'BUY' | 'SELL';
|
||||||
|
price_high: number;
|
||||||
|
price_low: number;
|
||||||
|
strength: number; // 0-100
|
||||||
|
formed_at: string;
|
||||||
|
touch_count: number;
|
||||||
|
probability_return: number; // 0-100
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FairValueGap {
|
||||||
|
id: string;
|
||||||
|
type: 'BULLISH_BREAKER' | 'BEARISH_BREAKER' | 'CONTINUATION';
|
||||||
|
gap_high: number;
|
||||||
|
gap_low: number;
|
||||||
|
gap_size_pips: number;
|
||||||
|
gap_size_percent: number;
|
||||||
|
status: 'UNMITIGATED' | 'PARTIALLY_MITIGATED' | 'FULLY_MITIGATED';
|
||||||
|
formed_at: string;
|
||||||
|
probability_fill: number; // 0-100
|
||||||
|
estimated_fill_time: string; // ISO duration
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OrderBlock {
|
||||||
|
id: string;
|
||||||
|
type: 'BULLISH' | 'BEARISH';
|
||||||
|
entry_high: number;
|
||||||
|
entry_low: number;
|
||||||
|
strength_score: number; // 1-10
|
||||||
|
power_percent: number; // 0-100
|
||||||
|
touch_count: number;
|
||||||
|
formed_at: string;
|
||||||
|
candle_formation: string;
|
||||||
|
next_target: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PointOfInterest {
|
||||||
|
id: string;
|
||||||
|
price: number;
|
||||||
|
type: 'SWING_HIGH' | 'SWING_LOW' | 'CONFLUENCE';
|
||||||
|
confluence_count: number;
|
||||||
|
reversal_probability: number; // 0-100
|
||||||
|
bounce_probability: number; // 0-100
|
||||||
|
elements: string[]; // IDs de elementos que confluyen
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Componentes UI principales:**
|
||||||
|
- `ICTAnalysis`: Container del análisis
|
||||||
|
- `ICTChart`: Canvas del chart con dibujado de elementos
|
||||||
|
- `ICTControls`: Controles de filtrado y display
|
||||||
|
- `ICTSidebar`: Listado de elementos detectados
|
||||||
|
- `ICTModals`: Familia de modales (Liquidity, FVG, OB, POI)
|
||||||
|
- `ComparisonChart`: Vista comparativa de timeframes
|
||||||
|
- `BacktestView`: Análisis histórico de una zona
|
||||||
|
|
||||||
|
**State Management (Zustand):**
|
||||||
|
```typescript
|
||||||
|
interface ICTStore {
|
||||||
|
ictData: ICTAnalysisResponse | null;
|
||||||
|
selectedElement: ICTElement | null;
|
||||||
|
timeframe: string;
|
||||||
|
visibleElements: {
|
||||||
|
liquidity_zones: boolean;
|
||||||
|
fvgs: boolean;
|
||||||
|
order_blocks: boolean;
|
||||||
|
poi: boolean;
|
||||||
|
};
|
||||||
|
bias: 'BULLISH' | 'BEARISH' | 'NEUTRAL';
|
||||||
|
threshold: 'LOW' | 'MEDIUM' | 'HIGH';
|
||||||
|
|
||||||
|
fetchICTAnalysis: (symbol: string, tf: string) => Promise<void>;
|
||||||
|
selectElement: (element: ICTElement) => void;
|
||||||
|
toggleElementType: (type: keyof visibleElements) => void;
|
||||||
|
setBias: (bias: string) => void;
|
||||||
|
setThreshold: (threshold: string) => void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Integración con Chart:**
|
||||||
|
- Usar series personalizada de lightweight-charts
|
||||||
|
- Plugins para dibujado de zonas y elementos
|
||||||
|
- Overlay layers para diferentes tipos de elementos
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Definition of Ready (DoR)
|
||||||
|
|
||||||
|
- [x] Historia claramente escrita (quién, qué, por qué)
|
||||||
|
- [x] Criterios de aceptación detallados (9 escenarios)
|
||||||
|
- [x] Story points estimados (5)
|
||||||
|
- [x] Dependencias identificadas
|
||||||
|
- [x] Sin bloqueadores
|
||||||
|
- [x] Diseño/mockup disponible
|
||||||
|
- [x] APIs especificadas
|
||||||
|
|
||||||
|
## Definition of Done (DoD)
|
||||||
|
|
||||||
|
- [ ] Código Python implementado (ICT detectors)
|
||||||
|
- [ ] Código TypeScript implementado (endpoints + frontend)
|
||||||
|
- [ ] Tests unitarios escritos y pasando (Python y TS)
|
||||||
|
- [ ] Tests E2E pasando
|
||||||
|
- [ ] Code review aprobado
|
||||||
|
- [ ] Documentación actualizada
|
||||||
|
- [ ] Backtesting validado (accuracy > 80%)
|
||||||
|
- [ ] Rendimiento verificado (<1.5s load)
|
||||||
|
- [ ] Responsive en mobile/tablet
|
||||||
|
- [ ] QA aprobado en staging
|
||||||
|
- [ ] Desplegado en producción
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Historial de Cambios
|
||||||
|
|
||||||
|
| Fecha | Cambio | Autor |
|
||||||
|
|-------|--------|-------|
|
||||||
|
| 2026-01-25 | Creación | Claude Code |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Creada por:** Claude Code
|
||||||
|
**Fecha:** 2026-01-25
|
||||||
|
**Última actualización:** 2026-01-25
|
||||||
@ -0,0 +1,448 @@
|
|||||||
|
---
|
||||||
|
id: "US-ML-010"
|
||||||
|
title: "Scan Multi-Símbolo"
|
||||||
|
type: "User Story"
|
||||||
|
status: "Pending"
|
||||||
|
priority: "Media"
|
||||||
|
epic: "OQI-006"
|
||||||
|
project: "trading-platform"
|
||||||
|
story_points: 5
|
||||||
|
created_date: "2026-01-25"
|
||||||
|
updated_date: "2026-01-25"
|
||||||
|
---
|
||||||
|
|
||||||
|
# US-ML-010: Scan Multi-Símbolo
|
||||||
|
|
||||||
|
## Metadata
|
||||||
|
|
||||||
|
| Campo | Valor |
|
||||||
|
|-------|-------|
|
||||||
|
| **ID** | US-ML-010 |
|
||||||
|
| **Épica** | OQI-006 - Señales ML y Predicciones |
|
||||||
|
| **Módulo** | ml-signals |
|
||||||
|
| **Prioridad** | P2 (Media) |
|
||||||
|
| **Story Points** | 5 |
|
||||||
|
| **Sprint** | Por asignar |
|
||||||
|
| **Estado** | Pendiente |
|
||||||
|
| **Asignado a** | Por asignar |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Historia de Usuario
|
||||||
|
|
||||||
|
**Como** trader/inversor,
|
||||||
|
**quiero** escanear múltiples símbolos simultáneamente en busca de señales de trading,
|
||||||
|
**para** encontrar las mejores oportunidades disponibles en el mercado de forma eficiente.
|
||||||
|
|
||||||
|
## Descripción Detallada
|
||||||
|
|
||||||
|
El usuario debe poder seleccionar una lista de símbolos (desde una lista predefinida, watchlist personal, o subir lista personalizada), ejecutar un escaneo que genere señales ML para todos ellos en paralelo, y visualizar los resultados filtrados y ordenados por confianza. El escaneo debe completarse en menos de 10 segundos para máximo 50 símbolos.
|
||||||
|
|
||||||
|
## Mockups/Wireframes
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────────────────────────┐
|
||||||
|
│ MULTI-SYMBOL SCAN │
|
||||||
|
├──────────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ Symbol Selection: │
|
||||||
|
│ ┌────────────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ ▼ Select from: [Popular ▼] [My Watchlist ▼] [Custom ▼] │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ ☑ BTCUSDT ☑ ETHUSDT ☑ BNBUSDT │ │
|
||||||
|
│ │ ☑ ADAUSDT ☑ XRPUSDT ☑ DOGEUSDT │ │
|
||||||
|
│ │ ☑ MATICUSDT ☑ SOLUSDT ☑ LINKUSDT │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ [Clear All] [Select All] [Select: 9 symbols] │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ Or upload CSV: [Choose File] upload-symbols.csv │ │
|
||||||
|
│ └────────────────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ Filter & Sort: │
|
||||||
|
│ ┌────────────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ Signal Type: [All ▼] Action: [BUY ▼] Horizon: [All ▼] │ │
|
||||||
|
│ │ Min Confidence: [50% ▼] Sort by: [Confidence ▼] │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ [🔄 Scan Now] [⏸️ Cancel] Scanning: 7/9 │ │
|
||||||
|
│ └────────────────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ══════════════════════════════════════════════════════════════ │
|
||||||
|
│ │
|
||||||
|
│ SCAN RESULTS - 9 Symbols, 12 Signals Found │
|
||||||
|
│ │
|
||||||
|
│ ┌────────────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ Symbol Action Horizon Conf. Score Entry Status │ │
|
||||||
|
│ ├────────────────────────────────────────────────────────────┤ │
|
||||||
|
│ │ BTCUSDT 🟢 BUY Scalping 85% 9.2/10 $89,400 ✅ Ready│ │
|
||||||
|
│ │ ETHUSDT 🟢 BUY Intraday 78% 8.8/10 $3,240 ✅ Ready│ │
|
||||||
|
│ │ BNBUSDT 🟢 BUY Swing 72% 8.1/10 $620 ✅ Ready│ │
|
||||||
|
│ │ ADAUSDT 🔴 SELL Intraday 68% 7.6/10 $1.05 ✅ Ready│ │
|
||||||
|
│ │ LINKUSDT 🟢 BUY Swing 65% 7.3/10 $28.50 ✅ Ready│ │
|
||||||
|
│ │ SOLUSDT 🔵 HOLD Position 58% 6.9/10 $190.50 ⚠️ Weak │ │
|
||||||
|
│ │ XRPUSDT 🔴 SELL Scalping 55% 6.2/10 $2.45 ⚠️ Weak │ │
|
||||||
|
│ │ MATICUSDT - - - - - - ❌ NoSig│ │
|
||||||
|
│ │ DOGEUSDT - - - - - - ❌ NoSig│ │
|
||||||
|
│ └────────────────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ [View Details] [Add to Watchlist] [Export Results] │
|
||||||
|
│ │
|
||||||
|
│ SUMMARY │
|
||||||
|
│ ══════════════════════════════════════════════════════════════ │
|
||||||
|
│ Total Symbols Scanned: 9 │
|
||||||
|
│ Signals Found: 7 (77.8%) │
|
||||||
|
│ Strong Signals (>70%): 4 │
|
||||||
|
│ Weak Signals (50-70%): 3 │
|
||||||
|
│ Avg Confidence: 68.3% │
|
||||||
|
│ │
|
||||||
|
│ [Create Portfolio from BUY Signals] [Refresh Scan] │
|
||||||
|
│ │
|
||||||
|
└──────────────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
┌──────────────────────────────────────────────────────────────────┐
|
||||||
|
│ SCAN PROGRESS [✕ Cancel] │
|
||||||
|
├──────────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ Scanning 9 symbols in parallel... │
|
||||||
|
│ │
|
||||||
|
│ ✅ BTCUSDT (2.1s) │
|
||||||
|
│ ✅ ETHUSDT (1.8s) │
|
||||||
|
│ ✅ BNBUSDT (1.5s) │
|
||||||
|
│ ⏳ ADAUSDT (Generating signal...) │
|
||||||
|
│ ⏳ XRPUSDT (Generating signal...) │
|
||||||
|
│ ⏳ LINKUSDT (Analyzing indicators...) │
|
||||||
|
│ ⏳ SOLUSDT (Analyzing indicators...) │
|
||||||
|
│ ⏳ MATICUSDT (Analyzing indicators...) │
|
||||||
|
│ ⏳ DOGEUSDT (Analyzing indicators...) │
|
||||||
|
│ │
|
||||||
|
│ Completed: 3/9 • Elapsed: 5.2s • Est. time: ~7.8s │
|
||||||
|
│ ████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 33% │
|
||||||
|
│ │
|
||||||
|
│ [View Real-time Feed] [Pause] [Cancel] │
|
||||||
|
│ │
|
||||||
|
└──────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Criterios de Aceptación
|
||||||
|
|
||||||
|
**Escenario 1: Seleccionar símbolos desde lista predefinida**
|
||||||
|
```gherkin
|
||||||
|
DADO que el usuario está en la pantalla Multi-Symbol Scan
|
||||||
|
CUANDO hace click en "Popular"
|
||||||
|
ENTONCES se muestran los 50 símbolos más populares/líquidos
|
||||||
|
Y puede seleccionar hasta 50 símbolos con checkboxes
|
||||||
|
Y se muestra el contador "Select: X symbols"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Escenario 2: Usar watchlist personal**
|
||||||
|
```gherkin
|
||||||
|
DADO que el usuario tiene una watchlist guardada
|
||||||
|
CUANDO selecciona "My Watchlist"
|
||||||
|
ENTONCES se cargan automáticamente los símbolos de la watchlist
|
||||||
|
Y puede desseleccionar símbolos si lo desea
|
||||||
|
Y se mantiene la selección al cambiar de filtro
|
||||||
|
```
|
||||||
|
|
||||||
|
**Escenario 3: Subir lista personalizada en CSV**
|
||||||
|
```gherkin
|
||||||
|
DADO que el usuario está en Symbol Selection
|
||||||
|
CUANDO hace click en "Choose File" y selecciona un CSV
|
||||||
|
ENTONCES el sistema parsea el archivo (formato: 1 símbolo por línea)
|
||||||
|
Y agrega los símbolos a la selección
|
||||||
|
Y muestra cantidad de símbolos cargados (validando que sean correctos)
|
||||||
|
Y permite deseleccionar los que no desea
|
||||||
|
```
|
||||||
|
|
||||||
|
**Escenario 4: Ejecutar escaneo multi-símbolo**
|
||||||
|
```gherkin
|
||||||
|
DADO que el usuario ha seleccionado 9 símbolos
|
||||||
|
Y ha configurado filtros (tipo de señal, confianza mínima)
|
||||||
|
CUANDO hace click en "Scan Now"
|
||||||
|
ENTONCES inicia escaneo paralelo en todos los símbolos
|
||||||
|
Y muestra progreso en tiempo real (símbolos completados)
|
||||||
|
Y el escaneo completa en menos de 10 segundos para 50 símbolos
|
||||||
|
Y muestra "Scanning: 3/9" mientras se ejecuta
|
||||||
|
```
|
||||||
|
|
||||||
|
**Escenario 5: Ver resultados en tabla**
|
||||||
|
```gherkin
|
||||||
|
DADO que el escaneo se ha completado
|
||||||
|
CUANDO se muestran los resultados
|
||||||
|
ENTONCES se visualiza tabla con columnas:
|
||||||
|
- Símbolo
|
||||||
|
- Acción (BUY/SELL/HOLD)
|
||||||
|
- Horizonte
|
||||||
|
- Confianza %
|
||||||
|
- Score (0-10)
|
||||||
|
- Precio de entrada
|
||||||
|
- Estado (✅ Ready / ⚠️ Weak / ❌ No Signal)
|
||||||
|
Y cada fila es clickeable para ver detalles completos
|
||||||
|
```
|
||||||
|
|
||||||
|
**Escenario 6: Filtrar resultados por confianza**
|
||||||
|
```gherkin
|
||||||
|
DADO que se muestran los resultados del escaneo
|
||||||
|
CUANDO selecciona "Min Confidence: 70%"
|
||||||
|
ENTONCES se muestran solo señales con confianza >= 70%
|
||||||
|
Y se actualiza el contador "7 of 12 signals"
|
||||||
|
Y el resumen se recalcula solo para señales filtradas
|
||||||
|
```
|
||||||
|
|
||||||
|
**Escenario 7: Filtrar por tipo de señal**
|
||||||
|
```gherkin
|
||||||
|
DADO que se muestran los resultados
|
||||||
|
CUANDO selecciona "Action: BUY"
|
||||||
|
ENTONCES muestra solo señales de compra
|
||||||
|
Y se pueden combinar filtros (ej: Action=BUY AND Horizon=Scalping)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Escenario 8: Ordenar por confianza**
|
||||||
|
```gherkin
|
||||||
|
DADO que se muestran resultados
|
||||||
|
CUANDO hace click en "Sort by: Confidence" (descendente)
|
||||||
|
ENTONCES la tabla se ordena con señales más confiables primero
|
||||||
|
Y se puede invertir el orden (ascendente/descendente)
|
||||||
|
Y se pueden ordenar también por Score, Símbolo, etc.
|
||||||
|
```
|
||||||
|
|
||||||
|
**Escenario 9: Exportar resultados**
|
||||||
|
```gherkin
|
||||||
|
DADO que se muestran los resultados del escaneo
|
||||||
|
CUANDO hace click en "Export Results"
|
||||||
|
ENTONCES descarga archivo CSV con:
|
||||||
|
- Symbol, Action, Horizon, Confidence, Score, Entry Price
|
||||||
|
- Nombre: "scan-results-2026-01-25-09-30.csv"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Escenario 10: Crear portfolio desde resultados**
|
||||||
|
```gherkin
|
||||||
|
DADO que se muestran resultados del escaneo
|
||||||
|
CUANDO hace click en "Create Portfolio from BUY Signals"
|
||||||
|
ENTONCES se abre modal para crear nuevo portfolio
|
||||||
|
Y se preseleccionan automáticamente todos los BUY signals
|
||||||
|
Y el usuario puede ajustar pesos y confirmar creación
|
||||||
|
```
|
||||||
|
|
||||||
|
## Criterios Adicionales
|
||||||
|
|
||||||
|
- [ ] El escaneo debe soportar máximo 50 símbolos simultáneamente
|
||||||
|
- [ ] El tiempo máximo debe ser menor a 10 segundos
|
||||||
|
- [ ] Los resultados deben mostrarse ordenados por confianza descendente
|
||||||
|
- [ ] El CSV debe ser válido y con header
|
||||||
|
- [ ] Debe mostrar progreso en tiempo real durante el escaneo
|
||||||
|
- [ ] Debe manejar símbolos inválidos (ignorarlos o marcar con error)
|
||||||
|
- [ ] La tabla debe ser responsive en móvil
|
||||||
|
- [ ] Los filtros deben ser combinables
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tareas Técnicas
|
||||||
|
|
||||||
|
**Backend:**
|
||||||
|
- [ ] BE-ML-020: Crear endpoint POST /api/ml/scan
|
||||||
|
- [ ] BE-ML-021: Implementar worker para escaneo paralelo (queue-based)
|
||||||
|
- [ ] BE-ML-022: Validar símbolos de entrada
|
||||||
|
- [ ] BE-ML-023: Parsear CSV de símbolos
|
||||||
|
- [ ] BE-ML-024: Implementar filtros (action, horizon, confidence_min)
|
||||||
|
- [ ] BE-ML-025: Generar respuesta con resultados ordenados
|
||||||
|
- [ ] BE-ML-026: Implementar export a CSV
|
||||||
|
|
||||||
|
**Frontend:**
|
||||||
|
- [ ] FE-ML-040: Crear componente `MultiSymbolScan.tsx`
|
||||||
|
- [ ] FE-ML-041: Crear componente `SymbolSelector.tsx`
|
||||||
|
- [ ] FE-ML-042: Crear componente `ScanFilters.tsx`
|
||||||
|
- [ ] FE-ML-043: Crear componente `ScanResults.tsx`
|
||||||
|
- [ ] FE-ML-044: Crear componente `ScanProgress.tsx`
|
||||||
|
- [ ] FE-ML-045: Implementar upload de CSV
|
||||||
|
- [ ] FE-ML-046: Implementar tabla con sorting y filtros
|
||||||
|
- [ ] FE-ML-047: Implementar export a CSV
|
||||||
|
- [ ] FE-ML-048: Integración con TanStack Query (react-query)
|
||||||
|
- [ ] FE-ML-049: Real-time progress updates (WebSocket/SSE)
|
||||||
|
|
||||||
|
**ML Engine:**
|
||||||
|
- [ ] ML-020: Optimizar generación de señales para múltiples símbolos
|
||||||
|
- [ ] ML-021: Implementar ejecución paralela de análisis
|
||||||
|
- [ ] ML-022: Cachear indicadores técnicos por símbolo
|
||||||
|
|
||||||
|
**Tests:**
|
||||||
|
- [ ] TEST-ML-020: Test de validación de símbolos
|
||||||
|
- [ ] TEST-ML-021: Test de parseo CSV
|
||||||
|
- [ ] TEST-ML-022: Test de escaneo paralelo (timeout, errores)
|
||||||
|
- [ ] TEST-ML-023: Test de filtrado y ordenamiento
|
||||||
|
- [ ] TEST-ML-024: Test E2E completo del multi-scan
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Dependencias
|
||||||
|
|
||||||
|
**Depende de:**
|
||||||
|
- [ ] US-ML-002: Ver señal - Estado: Pendiente
|
||||||
|
- [ ] RF-ML-001: Generación de señales ML
|
||||||
|
- [ ] Infrastructure: Worker queue (Bull/RabbitMQ)
|
||||||
|
|
||||||
|
**Bloquea:**
|
||||||
|
- [ ] US-ML-011: Alertas de señales (requiere resultados del scan)
|
||||||
|
- [ ] US-ML-012: Portfolio builder (requiere scan multi-símbolo)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Notas Técnicas
|
||||||
|
|
||||||
|
**Endpoints involucrados:**
|
||||||
|
| Método | Endpoint | Descripción |
|
||||||
|
|--------|----------|-------------|
|
||||||
|
| POST | /api/ml/scan | Ejecutar escaneo multi-símbolo |
|
||||||
|
| GET | /api/ml/scan/:scanId/progress | Obtener progreso en tiempo real |
|
||||||
|
| GET | /api/ml/scan/:scanId/results | Obtener resultados del escaneo |
|
||||||
|
| GET | /api/ml/scan/:scanId/export.csv | Exportar resultados a CSV |
|
||||||
|
| GET | /api/symbols/popular | Lista de símbolos populares |
|
||||||
|
| GET | /api/watchlists/:id/symbols | Símbolos de watchlist |
|
||||||
|
|
||||||
|
**Request esperado:**
|
||||||
|
```typescript
|
||||||
|
interface MultiSymbolScanRequest {
|
||||||
|
symbols: string[]; // Array de símbolos (ej: ['BTCUSDT', 'ETHUSDT'])
|
||||||
|
filters: {
|
||||||
|
signal_type?: 'BUY' | 'SELL' | 'HOLD' | 'ALL';
|
||||||
|
horizon?: string;
|
||||||
|
confidence_min?: number; // 0-100
|
||||||
|
action?: 'BUY' | 'SELL';
|
||||||
|
};
|
||||||
|
sort_by?: 'confidence' | 'score' | 'symbol';
|
||||||
|
sort_order?: 'asc' | 'desc';
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response esperado:**
|
||||||
|
```typescript
|
||||||
|
interface MultiSymbolScanResponse {
|
||||||
|
scan_id: string;
|
||||||
|
status: 'completed' | 'in_progress' | 'failed';
|
||||||
|
timestamp: string;
|
||||||
|
symbols_scanned: number;
|
||||||
|
signals_found: number;
|
||||||
|
results: SignalResult[];
|
||||||
|
summary: {
|
||||||
|
total_symbols: number;
|
||||||
|
signals_found: number;
|
||||||
|
strong_signals: number; // confidence >= 70%
|
||||||
|
weak_signals: number; // 50% <= confidence < 70%
|
||||||
|
avg_confidence: number;
|
||||||
|
};
|
||||||
|
execution_time_ms: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SignalResult {
|
||||||
|
symbol: string;
|
||||||
|
action: 'BUY' | 'SELL' | 'HOLD' | null;
|
||||||
|
horizon?: string;
|
||||||
|
confidence?: number;
|
||||||
|
score?: number;
|
||||||
|
entry_price?: number;
|
||||||
|
status: 'ready' | 'weak' | 'no_signal' | 'error';
|
||||||
|
error_message?: string;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**CSV Upload Parser:**
|
||||||
|
```typescript
|
||||||
|
// Soporta formatos:
|
||||||
|
// 1. Un símbolo por línea (simple)
|
||||||
|
BTCUSDT
|
||||||
|
ETHUSDT
|
||||||
|
BNBUSDT
|
||||||
|
|
||||||
|
// 2. CSV con header
|
||||||
|
Symbol,Exchange,Type
|
||||||
|
BTCUSDT,BINANCE,SPOT
|
||||||
|
ETHUSDT,BINANCE,SPOT
|
||||||
|
```
|
||||||
|
|
||||||
|
**Componentes UI:**
|
||||||
|
- `MultiSymbolScan`: Container principal
|
||||||
|
- `SymbolSelector`: Selector de símbolos con pestañas
|
||||||
|
- `SymbolListPicker`: Selector desde lista predefinida
|
||||||
|
- `WatchlistPicker`: Selector desde watchlist personal
|
||||||
|
- `CSVUploader`: Componente de upload de CSV
|
||||||
|
- `ScanFilters`: Filtros de escaneo
|
||||||
|
- `ScanResults`: Tabla de resultados
|
||||||
|
- `ScanProgress`: Modal de progreso en tiempo real
|
||||||
|
- `ResultsActions`: Acciones sobre resultados (export, create portfolio)
|
||||||
|
|
||||||
|
**Estado (Zustand):**
|
||||||
|
```typescript
|
||||||
|
interface MultiSymbolScanStore {
|
||||||
|
selectedSymbols: string[];
|
||||||
|
scanResults: SignalResult[];
|
||||||
|
scanProgress: {
|
||||||
|
total: number;
|
||||||
|
completed: number;
|
||||||
|
currentSymbol: string;
|
||||||
|
};
|
||||||
|
filters: ScanFilters;
|
||||||
|
isScanning: boolean;
|
||||||
|
sortBy: 'confidence' | 'score' | 'symbol';
|
||||||
|
sortOrder: 'asc' | 'desc';
|
||||||
|
|
||||||
|
setSelectedSymbols: (symbols: string[]) => void;
|
||||||
|
startScan: () => Promise<void>;
|
||||||
|
updateProgress: (progress: ScanProgress) => void;
|
||||||
|
updateFilters: (filters: Partial<ScanFilters>) => void;
|
||||||
|
setSortBy: (field: string, order: 'asc' | 'desc') => void;
|
||||||
|
exportToCSV: () => void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Real-time Progress:**
|
||||||
|
- Usar WebSocket o SSE para enviar actualizaciones de progreso
|
||||||
|
- Endpoint: `/api/ml/scan/:scanId/progress` con streaming
|
||||||
|
- O usar polling cada 500ms si WebSocket no disponible
|
||||||
|
|
||||||
|
**Performance Targets:**
|
||||||
|
- 50 símbolos scanned: < 10 segundos
|
||||||
|
- 20 símbolos scanned: < 5 segundos
|
||||||
|
- 10 símbolos scanned: < 3 segundos
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Definition of Ready (DoR)
|
||||||
|
|
||||||
|
- [x] Historia claramente escrita (quién, qué, por qué)
|
||||||
|
- [x] Criterios de aceptación definidos
|
||||||
|
- [x] Story points estimados
|
||||||
|
- [x] Dependencias identificadas
|
||||||
|
- [x] Diseño/mockup disponible
|
||||||
|
- [x] Targets de performance definidos
|
||||||
|
- [x] Formatos de datos especificados
|
||||||
|
|
||||||
|
## Definition of Done (DoD)
|
||||||
|
|
||||||
|
- [ ] Código implementado según criterios
|
||||||
|
- [ ] Tests unitarios escritos y pasando
|
||||||
|
- [ ] Tests E2E pasando
|
||||||
|
- [ ] Code review aprobado
|
||||||
|
- [ ] Documentación actualizada
|
||||||
|
- [ ] QA aprobado en staging
|
||||||
|
- [ ] Escaneo paralelo funciona correctamente
|
||||||
|
- [ ] Real-time progress updates funcionan
|
||||||
|
- [ ] CSV upload y export validan correctamente
|
||||||
|
- [ ] Filtros y ordenamiento funcionan
|
||||||
|
- [ ] Performance meets targets (< 10s para 50 símbolos)
|
||||||
|
- [ ] Manejo de errores robusto (símbolos inválidos, timeouts)
|
||||||
|
- [ ] Responsive en móvil
|
||||||
|
- [ ] Desplegado en producción
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Historial de Cambios
|
||||||
|
|
||||||
|
| Fecha | Cambio | Autor |
|
||||||
|
|-------|--------|-------|
|
||||||
|
| 2026-01-25 | Creación | Requirements-Analyst |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Creada por:** Requirements-Analyst
|
||||||
|
**Fecha:** 2026-01-25
|
||||||
|
**Última actualización:** 2026-01-25
|
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,357 @@
|
|||||||
|
---
|
||||||
|
id: "US-LLM-011"
|
||||||
|
title: "Ejecutar Trade desde Chat"
|
||||||
|
type: "User Story"
|
||||||
|
status: "Pending"
|
||||||
|
priority: "Alta"
|
||||||
|
epic: "OQI-007"
|
||||||
|
project: "trading-platform"
|
||||||
|
story_points: 5
|
||||||
|
created_date: "2026-01-25"
|
||||||
|
updated_date: "2026-01-25"
|
||||||
|
---
|
||||||
|
|
||||||
|
# US-LLM-011: Ejecutar Trade desde Chat
|
||||||
|
|
||||||
|
**Épica:** OQI-007 - LLM Strategy Agent
|
||||||
|
**Sprint:** TBD
|
||||||
|
**Story Points:** 5
|
||||||
|
**Prioridad:** P0 - Alta
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Historia de Usuario
|
||||||
|
|
||||||
|
**Como** usuario Pro o Premium con capacidad de trading real
|
||||||
|
**Quiero** ejecutar trades directamente desde el chat con el asistente AI mediante comandos naturales
|
||||||
|
**Para** operar en los mercados rápidamente sin cambiar de interfaz
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Criterios de Aceptación
|
||||||
|
|
||||||
|
### AC-1: Interpretar comandos de trading
|
||||||
|
```gherkin
|
||||||
|
Given soy usuario Pro/Premium
|
||||||
|
And tengo acceso a trading real
|
||||||
|
When digo frases como "Vende 50 acciones de AAPL" o "Compra Bitcoin a $43,000"
|
||||||
|
Then el agente interpreta correctamente:
|
||||||
|
- Acción (compra/venta)
|
||||||
|
- Símbolo del activo
|
||||||
|
- Cantidad
|
||||||
|
- Tipo de orden (mercado/límite)
|
||||||
|
- Precio (si es límite)
|
||||||
|
And valida que la orden sea válida
|
||||||
|
```
|
||||||
|
|
||||||
|
### AC-2: Mostrar preview de la orden
|
||||||
|
```gherkin
|
||||||
|
Given el agente interpreta un comando de trading
|
||||||
|
When prepara la orden
|
||||||
|
Then muestra un resumen detallado:
|
||||||
|
- Símbolo y nombre del activo
|
||||||
|
- Acción (BUY/SELL)
|
||||||
|
- Cantidad de unidades
|
||||||
|
- Tipo de orden (market/limit)
|
||||||
|
- Precio unitario estimado
|
||||||
|
- Costo/ingresos totales
|
||||||
|
- Comisiones estimadas
|
||||||
|
- Impacto en el portafolio
|
||||||
|
And NO ejecuta la orden automáticamente
|
||||||
|
```
|
||||||
|
|
||||||
|
### AC-3: Requerir confirmación explícita
|
||||||
|
```gherkin
|
||||||
|
Given el agente muestra el preview de la orden
|
||||||
|
When el usuario revisa los detalles
|
||||||
|
Then el agente pide confirmación explícita:
|
||||||
|
- "¿Confirmas esta orden?" o similar
|
||||||
|
- Botones/acciones claras (Confirmar/Cancelar)
|
||||||
|
And SOLO ejecuta si el usuario confirma
|
||||||
|
And permite cancelar en cualquier momento
|
||||||
|
```
|
||||||
|
|
||||||
|
### AC-4: Validar límites de riesgo
|
||||||
|
```gherkin
|
||||||
|
Given quiero ejecutar una orden
|
||||||
|
When el agente prepara la orden
|
||||||
|
Then valida:
|
||||||
|
- Saldo disponible suficiente
|
||||||
|
- Límite de exposición por activo
|
||||||
|
- Límite de pérdida diaria
|
||||||
|
- Límite de apalancamiento
|
||||||
|
And si hay violación:
|
||||||
|
- Informa qué límite se excede
|
||||||
|
- Muestra valores actuales vs permitidos
|
||||||
|
- NO permite ejecutar
|
||||||
|
```
|
||||||
|
|
||||||
|
### AC-5: Ejecutar y retornar feedback
|
||||||
|
```gherkin
|
||||||
|
Given el usuario confirma la orden
|
||||||
|
When se ejecuta correctamente
|
||||||
|
Then muestra confirmación con:
|
||||||
|
- ID de la orden
|
||||||
|
- Status (filled/pending)
|
||||||
|
- Símbolo y precio ejecutado
|
||||||
|
- Cantidad y costo final
|
||||||
|
- Timestamp exacto
|
||||||
|
- Posición actualizada en el activo
|
||||||
|
And registra en el historial del chat
|
||||||
|
```
|
||||||
|
|
||||||
|
### AC-6: Manejo de errores
|
||||||
|
```gherkin
|
||||||
|
Given intento ejecutar una orden
|
||||||
|
When ocurre error (conexión, mercado cerrado, etc.)
|
||||||
|
Then el agente:
|
||||||
|
- Informa el tipo de error
|
||||||
|
- Explica por qué no se ejecutó
|
||||||
|
- Sugiere alternativas (ej: orden pendiente)
|
||||||
|
- NO procesa parcialmente la orden
|
||||||
|
```
|
||||||
|
|
||||||
|
### AC-7: Restricción por plan
|
||||||
|
```gherkin
|
||||||
|
Given soy usuario Free o con trading deshabilitado
|
||||||
|
When intento ejecutar una orden
|
||||||
|
Then el agente:
|
||||||
|
- Rechaza la orden
|
||||||
|
- Explica que requiere plan Pro/Premium
|
||||||
|
- Muestra opción de upgrade
|
||||||
|
- NO ejecuta ninguna orden
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Flujo de Confirmación
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Confirmar Orden 📊
|
||||||
|
|
||||||
|
**Acción:** VENTA
|
||||||
|
**Símbolo:** AAPL - Apple Inc.
|
||||||
|
**Cantidad:** 50 acciones
|
||||||
|
**Tipo:** Market Order
|
||||||
|
**Precio actual:** $185.32
|
||||||
|
**Costo total:** ~$9,266.00
|
||||||
|
**Comisión:** ~$9.27
|
||||||
|
**Neto:** ~$9,256.73
|
||||||
|
|
||||||
|
**Tu portafolio:**
|
||||||
|
- Posición actual: 100 acciones @ $184.50
|
||||||
|
- Posición después: 50 acciones
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
⚠️ Esta es una orden REAL de trading
|
||||||
|
|
||||||
|
¿Confirmas esta orden?
|
||||||
|
|
||||||
|
[✅ Confirmar] [❌ Cancelar]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Respuesta Post-Ejecución
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Orden Ejecutada ✅
|
||||||
|
|
||||||
|
**ID:** ORD-20260125-001234
|
||||||
|
**Status:** Filled (Completada)
|
||||||
|
**Símbolo:** AAPL
|
||||||
|
**Acción:** VENTA
|
||||||
|
**Cantidad:** 50 acciones
|
||||||
|
**Precio:** $185.31
|
||||||
|
**Total:** $9,265.50
|
||||||
|
**Comisión:** $9.27
|
||||||
|
**Neto:** $9,256.23
|
||||||
|
**Timestamp:** 15:45:32 ET
|
||||||
|
|
||||||
|
**Posición Actualizada:**
|
||||||
|
- AAPL: 50 acciones @ $185.31 (promedio)
|
||||||
|
- Valor: $9,265.50
|
||||||
|
- Variación: +2.5% hoy
|
||||||
|
|
||||||
|
Registro guardado en tu historial.
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Respuesta con Error
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Error al Ejecutar ❌
|
||||||
|
|
||||||
|
**Razón:** Mercado cerrado (Cierre: 16:00 ET)
|
||||||
|
|
||||||
|
La orden no pudo ejecutarse porque el mercado de NYSE está cerrado.
|
||||||
|
|
||||||
|
**Alternativas:**
|
||||||
|
- Crear orden pendiente para la apertura mañana
|
||||||
|
- Intentar en mercados abiertos (premarket a las 04:00 ET)
|
||||||
|
- Esperar a mañana a las 09:30 ET
|
||||||
|
|
||||||
|
¿Qué prefieres?
|
||||||
|
|
||||||
|
[Orden Pendiente] [Otra acción] [Cancelar]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## API Endpoint
|
||||||
|
|
||||||
|
### POST `/api/llm/execute-trade`
|
||||||
|
|
||||||
|
**Headers:**
|
||||||
|
```
|
||||||
|
Authorization: Bearer {token}
|
||||||
|
Content-Type: application/json
|
||||||
|
```
|
||||||
|
|
||||||
|
**Request Body:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"sessionId": "chat-session-12345",
|
||||||
|
"interpretation": {
|
||||||
|
"action": "BUY|SELL",
|
||||||
|
"symbol": "AAPL",
|
||||||
|
"quantity": 50,
|
||||||
|
"orderType": "market|limit",
|
||||||
|
"limitPrice": null,
|
||||||
|
"timeInForce": "DAY|GTC"
|
||||||
|
},
|
||||||
|
"userId": "user-uuid",
|
||||||
|
"confirmedAt": "2026-01-25T15:45:32Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response (Success):**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"order": {
|
||||||
|
"orderId": "ORD-20260125-001234",
|
||||||
|
"status": "filled",
|
||||||
|
"symbol": "AAPL",
|
||||||
|
"action": "SELL",
|
||||||
|
"quantity": 50,
|
||||||
|
"executedPrice": 185.31,
|
||||||
|
"totalValue": 9265.50,
|
||||||
|
"commission": 9.27,
|
||||||
|
"netValue": 9256.23,
|
||||||
|
"executedAt": "2026-01-25T15:45:32Z"
|
||||||
|
},
|
||||||
|
"message": "Orden ejecutada exitosamente"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response (Error):**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"error": {
|
||||||
|
"code": "MARKET_CLOSED",
|
||||||
|
"message": "El mercado está cerrado",
|
||||||
|
"details": "NYSE cierra a las 16:00 ET"
|
||||||
|
},
|
||||||
|
"alternatives": [
|
||||||
|
{
|
||||||
|
"type": "pending_order",
|
||||||
|
"description": "Crear orden pendiente para apertura"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Notas Técnicas
|
||||||
|
|
||||||
|
- **Tool:** `execute_trade` (en el LLM Agent)
|
||||||
|
- **Servicio:** Trading API Backend (puerto 3080)
|
||||||
|
- **Confirmación:** OBLIGATORIA antes de ejecutar
|
||||||
|
- **Validación:** Límites de riesgo, saldo, mercado abierto
|
||||||
|
- **Logging:** Todas las órdenes registradas con timestamp
|
||||||
|
- **Rate limit:** 20 órdenes/minuto por usuario
|
||||||
|
- **Timeout:** 30 segundos para confirmación
|
||||||
|
- **Rollback:** Si falla ejecución, reversión de cambios
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Flujo Técnico
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TD
|
||||||
|
A["Usuario: 'Vende 50 AAPL'"] -->|Chat Input| B["LLM Agent"]
|
||||||
|
B -->|NLU| C["Interpretar Comando"]
|
||||||
|
C -->|Validar Sintaxis| D{¿Válido?}
|
||||||
|
D -->|No| E["Error: Comando inválido"]
|
||||||
|
D -->|Sí| F["Preparar Preview"]
|
||||||
|
F -->|GET /api/market/price| G["Obtener precio actual"]
|
||||||
|
G -->|GET /api/portfolio/position| H["Obtener posición actual"]
|
||||||
|
H -->|Calcular| I["Preparar detalles orden"]
|
||||||
|
I -->|Mostrar| J["Preview en chat"]
|
||||||
|
J -->|Usuario revisa| K{¿Confirma?}
|
||||||
|
K -->|Cancela| L["Cancelado"]
|
||||||
|
K -->|Confirma| M["POST /api/llm/execute-trade"]
|
||||||
|
M -->|Validar riesgos| N{¿Límites OK?}
|
||||||
|
N -->|No| O["Error: Límite excedido"]
|
||||||
|
N -->|Sí| P["Ejecutar orden"]
|
||||||
|
P -->|Trading System| Q["Orden procesada"]
|
||||||
|
Q -->|Response| R["Mostrar confirmación"]
|
||||||
|
R -->|Actualizar portafolio| S["Feedback final"]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Criterios de Aceptación Técnica
|
||||||
|
|
||||||
|
- [ ] Interpretar al menos 10 variantes de comandos
|
||||||
|
- [ ] Validar todos los campos obligatorios
|
||||||
|
- [ ] Conectar con Trading API backend
|
||||||
|
- [ ] Mostrar preview antes de ejecutar
|
||||||
|
- [ ] Requerir confirmación explícita
|
||||||
|
- [ ] Validar límites de riesgo
|
||||||
|
- [ ] Ejecutar orden correctamente
|
||||||
|
- [ ] Mostrar confirmación con detalles
|
||||||
|
- [ ] Registrar en historial
|
||||||
|
- [ ] Manejo robusto de errores
|
||||||
|
- [ ] Tests unitarios (80%+ coverage)
|
||||||
|
- [ ] Tests E2E del flujo completo
|
||||||
|
- [ ] QA aprobado
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Dependencias
|
||||||
|
|
||||||
|
- RF-LLM-005: Herramienta Trading Execution
|
||||||
|
- RF-LLM-006: Interpretación NLU avanzada
|
||||||
|
- ET-LLM-005: Arquitectura Tools
|
||||||
|
- OQI-001: Autenticación y autorización
|
||||||
|
- OQI-003: Paper Trading System
|
||||||
|
- REC-BACKEND-001: Trading API
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Definición de Done
|
||||||
|
|
||||||
|
- [ ] Endpoint `/api/llm/execute-trade` implementado
|
||||||
|
- [ ] LLM Agent puede interpretar comandos
|
||||||
|
- [ ] Preview de orden funcionando
|
||||||
|
- [ ] Confirmación obligatoria
|
||||||
|
- [ ] Validación de límites de riesgo
|
||||||
|
- [ ] Ejecución de orden
|
||||||
|
- [ ] Feedback post-ejecución
|
||||||
|
- [ ] Manejo de errores
|
||||||
|
- [ ] Restricción por plan
|
||||||
|
- [ ] Tests unitarios
|
||||||
|
- [ ] Tests E2E
|
||||||
|
- [ ] QA aprobado
|
||||||
|
- [ ] Documentación actualizada
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Historia de usuario - Sistema NEXUS*
|
||||||
|
*Trading Platform*
|
||||||
@ -0,0 +1,656 @@
|
|||||||
|
# ET-PFM-008: Especificación Frontend - Módulo Portfolio Manager
|
||||||
|
|
||||||
|
**Módulo:** OQI-008 Portfolio Manager
|
||||||
|
**Documento:** ET-PFM-008
|
||||||
|
**Estado:** A IMPLEMENTAR
|
||||||
|
**Versión:** 1.0.0
|
||||||
|
**Fecha de Creación:** 2026-01-25
|
||||||
|
**Última Actualización:** 2026-01-25
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 Resumen Ejecutivo
|
||||||
|
|
||||||
|
Este documento especifica la arquitectura, estructura de componentes y flujos de interfaz de usuario para el módulo Portfolio Manager en la plataforma de trading. **Este es un diseño a implementar que no cuenta actualmente con código base.**
|
||||||
|
|
||||||
|
El módulo proporciona funcionalidades de visualización, análisis y gestión de portafolios de inversión, incluyendo posiciones, métricas de riesgo, pruebas de estrés y generación de reportes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Objetivos del Frontend
|
||||||
|
|
||||||
|
1. Proporcionar visualización clara y en tiempo real del estado del portafolio
|
||||||
|
2. Facilitar análisis de riesgo mediante métricas consolidadas
|
||||||
|
3. Permitir simulaciones de rebalanceo de posiciones
|
||||||
|
4. Generar reportes analíticos exportables
|
||||||
|
5. Integrar datos de múltiples fuentes de mercado
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📄 Páginas Propuestas
|
||||||
|
|
||||||
|
### 1. PortfolioPage
|
||||||
|
|
||||||
|
**Ruta:** `/portfolio`
|
||||||
|
**Descripción:** Página principal de gestión del portafolio
|
||||||
|
|
||||||
|
**Responsabilidades:**
|
||||||
|
- Mostrar resumen ejecutivo del portafolio
|
||||||
|
- Listar posiciones activas
|
||||||
|
- Mostrar indicadores clave de desempeño (KPIs)
|
||||||
|
- Permitir filtrado y búsqueda de posiciones
|
||||||
|
- Facilitar acceso a rebalanceo
|
||||||
|
|
||||||
|
**Estructura:**
|
||||||
|
```
|
||||||
|
PortfolioPage
|
||||||
|
├── PortfolioSummaryCard
|
||||||
|
├── PerformanceChart
|
||||||
|
├── PositionsTable
|
||||||
|
│ ├── Filtros
|
||||||
|
│ └── Paginación
|
||||||
|
└── Acciones (Rebalancear, Exportar)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Datos Requeridos:**
|
||||||
|
- Valor total del portafolio
|
||||||
|
- Rentabilidad YTD
|
||||||
|
- Rentabilidad acumulada
|
||||||
|
- Composición por activo
|
||||||
|
- Posiciones individuales con métricas
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. StressTestPage
|
||||||
|
|
||||||
|
**Ruta:** `/portfolio/stress-tests`
|
||||||
|
**Descripción:** Página para ejecución y análisis de pruebas de estrés
|
||||||
|
|
||||||
|
**Responsabilidades:**
|
||||||
|
- Crear nuevas pruebas de estrés
|
||||||
|
- Ejecutar escenarios predefinidos
|
||||||
|
- Visualizar resultados de stress tests
|
||||||
|
- Comparar múltiples escenarios
|
||||||
|
- Exportar análisis de sensibilidad
|
||||||
|
|
||||||
|
**Estructura:**
|
||||||
|
```
|
||||||
|
StressTestPage
|
||||||
|
├── ScenarioSelector
|
||||||
|
├── SimulationControls
|
||||||
|
├── RiskMetricsPanel
|
||||||
|
├── ResultsVisualization
|
||||||
|
│ ├── ImpactChart
|
||||||
|
│ └── SensitivityTable
|
||||||
|
└── ExportOptions
|
||||||
|
```
|
||||||
|
|
||||||
|
**Datos Requeridos:**
|
||||||
|
- Escenarios disponibles
|
||||||
|
- Parámetros de simulación
|
||||||
|
- Resultados de ejecución
|
||||||
|
- Metricas de sensibilidad
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. ReportsPage
|
||||||
|
|
||||||
|
**Ruta:** `/portfolio/reports`
|
||||||
|
**Descripción:** Página de generación y consulta de reportes analíticos
|
||||||
|
|
||||||
|
**Responsabilidades:**
|
||||||
|
- Listar reportes generados
|
||||||
|
- Crear nuevos reportes personalizados
|
||||||
|
- Visualizar reportes en tiempo real
|
||||||
|
- Descargar reportes en múltiples formatos
|
||||||
|
- Programar generación automática de reportes
|
||||||
|
|
||||||
|
**Estructura:**
|
||||||
|
```
|
||||||
|
ReportsPage
|
||||||
|
├── ReportsList
|
||||||
|
│ └── ReportFilters
|
||||||
|
├── ReportGenerator
|
||||||
|
│ ├── ParameterSelector
|
||||||
|
│ └── FormatSelector
|
||||||
|
├── ReportViewer
|
||||||
|
│ ├── PaginationControls
|
||||||
|
│ └── ExportOptions
|
||||||
|
└── ScheduleManager
|
||||||
|
```
|
||||||
|
|
||||||
|
**Datos Requeridos:**
|
||||||
|
- Lista de reportes disponibles
|
||||||
|
- Historico de reportes generados
|
||||||
|
- Parámetros configurables
|
||||||
|
- Formato de salida (PDF, Excel, CSV)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧩 Componentes Propuestos
|
||||||
|
|
||||||
|
### 1. PortfolioSummaryCard
|
||||||
|
|
||||||
|
**Tipo:** Componente Presentacional
|
||||||
|
**Ubicación:** `src/components/portfolio/PortfolioSummaryCard.vue`
|
||||||
|
|
||||||
|
**Props:**
|
||||||
|
```typescript
|
||||||
|
interface PortfolioSummaryProps {
|
||||||
|
totalValue: number;
|
||||||
|
ytdReturn: number;
|
||||||
|
cumulativeReturn: number;
|
||||||
|
currency: string;
|
||||||
|
lastUpdated: Date;
|
||||||
|
isLoading?: boolean;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Funcionalidades:**
|
||||||
|
- Mostrar valor total del portafolio
|
||||||
|
- Indicadores de rentabilidad (YTD, acumulada)
|
||||||
|
- Tendencia visual (arriba/abajo)
|
||||||
|
- Moneda de referencia
|
||||||
|
- Timestamp de actualización
|
||||||
|
- Estado de carga
|
||||||
|
|
||||||
|
**Emits:**
|
||||||
|
- `refresh`: Solicitar actualización de datos
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. PositionsTable
|
||||||
|
|
||||||
|
**Tipo:** Componente Presentacional
|
||||||
|
**Ubicación:** `src/components/portfolio/PositionsTable.vue`
|
||||||
|
|
||||||
|
**Props:**
|
||||||
|
```typescript
|
||||||
|
interface PositionsTableProps {
|
||||||
|
positions: Position[];
|
||||||
|
loading?: boolean;
|
||||||
|
sortBy?: string;
|
||||||
|
sortOrder?: 'asc' | 'desc';
|
||||||
|
pageSize?: number;
|
||||||
|
currentPage?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Position {
|
||||||
|
id: string;
|
||||||
|
symbol: string;
|
||||||
|
name: string;
|
||||||
|
quantity: number;
|
||||||
|
entryPrice: number;
|
||||||
|
currentPrice: number;
|
||||||
|
currentValue: number;
|
||||||
|
percentageChange: number;
|
||||||
|
percentageOfPortfolio: number;
|
||||||
|
sector?: string;
|
||||||
|
lastUpdated: Date;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Funcionalidades:**
|
||||||
|
- Visualización de tabla de posiciones
|
||||||
|
- Ordenamiento por columnas
|
||||||
|
- Paginación configurable
|
||||||
|
- Filtrado por sector/tipo
|
||||||
|
- Búsqueda por símbolo o nombre
|
||||||
|
- Indicadores visuales de desempeño
|
||||||
|
- Acciones por fila (detalles, vender, rebalancear)
|
||||||
|
|
||||||
|
**Emits:**
|
||||||
|
- `sort`: Cambio de ordenamiento
|
||||||
|
- `page-change`: Cambio de página
|
||||||
|
- `position-selected`: Selección de posición
|
||||||
|
- `position-action`: Acción sobre posición
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. PerformanceChart
|
||||||
|
|
||||||
|
**Tipo:** Componente Presentacional
|
||||||
|
**Ubicación:** `src/components/portfolio/PerformanceChart.vue`
|
||||||
|
|
||||||
|
**Props:**
|
||||||
|
```typescript
|
||||||
|
interface PerformanceChartProps {
|
||||||
|
data: PerformanceData[];
|
||||||
|
timeframe?: 'D' | 'W' | 'M' | 'Y' | 'ALL';
|
||||||
|
currency?: string;
|
||||||
|
showComparison?: boolean;
|
||||||
|
isLoading?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PerformanceData {
|
||||||
|
date: Date;
|
||||||
|
value: number;
|
||||||
|
return: number;
|
||||||
|
benchmark?: number;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Funcionalidades:**
|
||||||
|
- Gráfico de línea del desempeño del portafolio
|
||||||
|
- Múltiples plazos (Día, Semana, Mes, Año, Todo)
|
||||||
|
- Comparación con benchmark
|
||||||
|
- Visualización de retorno absoluto y porcentual
|
||||||
|
- Interactividad con tooltips
|
||||||
|
- Exportación como imagen
|
||||||
|
|
||||||
|
**Emits:**
|
||||||
|
- `timeframe-change`: Cambio de período
|
||||||
|
- `range-select`: Selección de rango personalizado
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4. RiskMetricsPanel
|
||||||
|
|
||||||
|
**Tipo:** Componente Presentacional
|
||||||
|
**Ubicación:** `src/components/portfolio/RiskMetricsPanel.vue`
|
||||||
|
|
||||||
|
**Props:**
|
||||||
|
```typescript
|
||||||
|
interface RiskMetricsPanelProps {
|
||||||
|
metrics: RiskMetrics;
|
||||||
|
isLoading?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RiskMetrics {
|
||||||
|
volatility: number; // Volatilidad anualizada
|
||||||
|
sharpeRatio: number; // Ratio de Sharpe
|
||||||
|
sortinoRatio: number; // Ratio de Sortino
|
||||||
|
valueAtRisk: number; // VaR 95%
|
||||||
|
expectedShortfall: number; // CVaR (Expected Shortfall)
|
||||||
|
beta: number; // Beta respecto a benchmark
|
||||||
|
correlation: number; // Correlación con benchmark
|
||||||
|
maxDrawdown: number; // Máxima caída histórica
|
||||||
|
diversificationRatio: number; // Ratio de diversificación
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Funcionalidades:**
|
||||||
|
- Visualización de métricas de riesgo clave
|
||||||
|
- Indicadores de riesgo (bajo/medio/alto)
|
||||||
|
- Comparación con benchmarks
|
||||||
|
- Explicación de métricas (tooltips)
|
||||||
|
- Gráficos de sensibilidad
|
||||||
|
- Histórico de evolución de métricas
|
||||||
|
|
||||||
|
**Emits:**
|
||||||
|
- `metric-selected`: Selección de métrica para análisis profundo
|
||||||
|
- `learn-more`: Solicitud de explicación de métrica
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5. RebalanceModal
|
||||||
|
|
||||||
|
**Tipo:** Componente Modal
|
||||||
|
**Ubicación:** `src/components/portfolio/RebalanceModal.vue`
|
||||||
|
|
||||||
|
**Props:**
|
||||||
|
```typescript
|
||||||
|
interface RebalanceModalProps {
|
||||||
|
visible: boolean;
|
||||||
|
portfolio: Portfolio;
|
||||||
|
targetAllocation: AllocationTarget[];
|
||||||
|
currentAllocation: AllocationCurrent[];
|
||||||
|
constraints?: RebalanceConstraints;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AllocationTarget {
|
||||||
|
assetType: string;
|
||||||
|
targetPercentage: number;
|
||||||
|
minPercentage?: number;
|
||||||
|
maxPercentage?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RebalanceConstraints {
|
||||||
|
maxTransactionCost?: number;
|
||||||
|
minTradeSize?: number;
|
||||||
|
excludeAssets?: string[];
|
||||||
|
allowNewPositions?: boolean;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Funcionalidades:**
|
||||||
|
- Mostrar asignación actual vs. objetivo
|
||||||
|
- Calcular transacciones necesarias
|
||||||
|
- Permitir selección de restricciones
|
||||||
|
- Vista previa de cambios
|
||||||
|
- Estimación de costos de transacción
|
||||||
|
- Confirmación antes de ejecutar
|
||||||
|
- Historial de rebalanceos
|
||||||
|
|
||||||
|
**Emits:**
|
||||||
|
- `close`: Cierre del modal
|
||||||
|
- `confirm`: Confirmación de rebalanceo
|
||||||
|
- `cancel`: Cancelación de operación
|
||||||
|
- `preview`: Solicitud de vista previa
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🗂️ Estructura de Store
|
||||||
|
|
||||||
|
### portfolioStore
|
||||||
|
|
||||||
|
**Ubicación:** `src/stores/portfolioStore.ts`
|
||||||
|
|
||||||
|
**Estado:**
|
||||||
|
```typescript
|
||||||
|
interface PortfolioState {
|
||||||
|
portfolio: Portfolio | null;
|
||||||
|
positions: Position[];
|
||||||
|
selectedPosition: Position | null;
|
||||||
|
performanceData: PerformanceData[];
|
||||||
|
riskMetrics: RiskMetrics | null;
|
||||||
|
stressTestResults: StressTestResult[] | null;
|
||||||
|
reports: Report[];
|
||||||
|
loading: {
|
||||||
|
portfolio: boolean;
|
||||||
|
positions: boolean;
|
||||||
|
riskMetrics: boolean;
|
||||||
|
stressTest: boolean;
|
||||||
|
reports: boolean;
|
||||||
|
};
|
||||||
|
filters: {
|
||||||
|
sector?: string;
|
||||||
|
minValue?: number;
|
||||||
|
maxValue?: number;
|
||||||
|
searchTerm?: string;
|
||||||
|
};
|
||||||
|
error: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Portfolio {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
totalValue: number;
|
||||||
|
ytdReturn: number;
|
||||||
|
cumulativeReturn: number;
|
||||||
|
currency: string;
|
||||||
|
positions: Position[];
|
||||||
|
lastUpdated: Date;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Acciones Principales:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Portafolio
|
||||||
|
fetchPortfolio(portfolioId: string): Promise<Portfolio>
|
||||||
|
updatePortfolio(portfolioId: string, data: Partial<Portfolio>): Promise<Portfolio>
|
||||||
|
selectPosition(position: Position): void
|
||||||
|
clearSelection(): void
|
||||||
|
|
||||||
|
// Posiciones
|
||||||
|
fetchPositions(portfolioId: string): Promise<Position[]>
|
||||||
|
updatePosition(positionId: string, data: Partial<Position>): Promise<Position>
|
||||||
|
deletePosition(positionId: string): Promise<void>
|
||||||
|
addPosition(position: Position): Promise<Position>
|
||||||
|
|
||||||
|
// Desempeño
|
||||||
|
fetchPerformanceData(portfolioId: string, timeframe: string): Promise<PerformanceData[]>
|
||||||
|
|
||||||
|
// Métricas de Riesgo
|
||||||
|
fetchRiskMetrics(portfolioId: string): Promise<RiskMetrics>
|
||||||
|
|
||||||
|
// Pruebas de Estrés
|
||||||
|
executeStressTest(portfolioId: string, scenario: StressScenario): Promise<StressTestResult>
|
||||||
|
fetchStressTestResults(portfolioId: string): Promise<StressTestResult[]>
|
||||||
|
|
||||||
|
// Rebalanceo
|
||||||
|
calculateRebalance(portfolioId: string, targets: AllocationTarget[]): Promise<RebalancePlan>
|
||||||
|
executeRebalance(portfolioId: string, plan: RebalancePlan): Promise<RebalanceResult>
|
||||||
|
|
||||||
|
// Reportes
|
||||||
|
fetchReports(portfolioId: string): Promise<Report[]>
|
||||||
|
generateReport(portfolioId: string, config: ReportConfig): Promise<Report>
|
||||||
|
exportReport(reportId: string, format: 'pdf' | 'excel' | 'csv'): Promise<Blob>
|
||||||
|
|
||||||
|
// Filtros
|
||||||
|
setFilter(filterName: string, value: any): void
|
||||||
|
clearFilters(): void
|
||||||
|
```
|
||||||
|
|
||||||
|
**Getters:**
|
||||||
|
```typescript
|
||||||
|
getPortfolioValue(): number
|
||||||
|
getTotalReturn(): number
|
||||||
|
getPositionCount(): number
|
||||||
|
getFilteredPositions(): Position[]
|
||||||
|
getTopPerformer(): Position | null
|
||||||
|
getTopLoser(): Position | null
|
||||||
|
getRiskLevel(): 'low' | 'medium' | 'high'
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Flujos de Datos
|
||||||
|
|
||||||
|
### Flujo 1: Carga Inicial de Portafolio
|
||||||
|
|
||||||
|
```
|
||||||
|
PortfolioPage Montada
|
||||||
|
↓
|
||||||
|
[Dispatch] fetchPortfolio()
|
||||||
|
↓
|
||||||
|
API Backend (GET /api/portfolio/{id})
|
||||||
|
↓
|
||||||
|
[Commit] setPortfolio()
|
||||||
|
↓
|
||||||
|
PortfolioSummaryCard actualizado
|
||||||
|
PositionsTable actualizado
|
||||||
|
```
|
||||||
|
|
||||||
|
### Flujo 2: Búsqueda y Filtrado
|
||||||
|
|
||||||
|
```
|
||||||
|
Usuario escribe en SearchInput
|
||||||
|
↓
|
||||||
|
[Dispatch] setFilter('searchTerm', value)
|
||||||
|
↓
|
||||||
|
Computed getFilteredPositions()
|
||||||
|
↓
|
||||||
|
PositionsTable re-renderiza
|
||||||
|
```
|
||||||
|
|
||||||
|
### Flujo 3: Ejecución de Stress Test
|
||||||
|
|
||||||
|
```
|
||||||
|
Usuario selecciona escenario
|
||||||
|
↓
|
||||||
|
[Dispatch] executeStressTest(scenario)
|
||||||
|
↓
|
||||||
|
API Backend (POST /api/portfolio/stress-test)
|
||||||
|
↓
|
||||||
|
[Commit] setStressTestResults()
|
||||||
|
↓
|
||||||
|
RiskMetricsPanel actualizado
|
||||||
|
ResultsVisualization mostrada
|
||||||
|
```
|
||||||
|
|
||||||
|
### Flujo 4: Rebalanceo
|
||||||
|
|
||||||
|
```
|
||||||
|
Usuario abre RebalanceModal
|
||||||
|
↓
|
||||||
|
[Dispatch] calculateRebalance(targets)
|
||||||
|
↓
|
||||||
|
API Backend (POST /api/portfolio/calculate-rebalance)
|
||||||
|
↓
|
||||||
|
Modal muestra vista previa
|
||||||
|
↓
|
||||||
|
Usuario confirma
|
||||||
|
↓
|
||||||
|
[Dispatch] executeRebalance(plan)
|
||||||
|
↓
|
||||||
|
API Backend (POST /api/portfolio/execute-rebalance)
|
||||||
|
↓
|
||||||
|
[Dispatch] fetchPortfolio() para refrescar
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎨 Estilos y Temas
|
||||||
|
|
||||||
|
### Principios de Diseño
|
||||||
|
|
||||||
|
- **Diseño Responsivo:** Compatible con desktop, tablet y mobile
|
||||||
|
- **Accesibilidad:** WCAG 2.1 AA como mínimo
|
||||||
|
- **Consistencia:** Uso de design system corporativo
|
||||||
|
- **Rendimiento:** Carga y animaciones fluidas
|
||||||
|
|
||||||
|
### Componentes de UI Requeridos
|
||||||
|
|
||||||
|
- Cards para resumen de métricas
|
||||||
|
- Tablas con ordenamiento y paginación
|
||||||
|
- Gráficos interactivos (Chart.js o Echarts)
|
||||||
|
- Modals para acciones confirmables
|
||||||
|
- Alerts y notificaciones Toast
|
||||||
|
- Loaders y spinners
|
||||||
|
- Iconografía consistente
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Integraciones Externas
|
||||||
|
|
||||||
|
### APIs Backend Requeridas
|
||||||
|
|
||||||
|
| Endpoint | Método | Descripción |
|
||||||
|
|----------|--------|-------------|
|
||||||
|
| `/api/portfolio/{id}` | GET | Obtener datos del portafolio |
|
||||||
|
| `/api/portfolio/{id}` | PUT | Actualizar portafolio |
|
||||||
|
| `/api/portfolio/{id}/positions` | GET | Listar posiciones |
|
||||||
|
| `/api/portfolio/{id}/positions` | POST | Crear posición |
|
||||||
|
| `/api/portfolio/position/{id}` | PUT | Actualizar posición |
|
||||||
|
| `/api/portfolio/position/{id}` | DELETE | Eliminar posición |
|
||||||
|
| `/api/portfolio/{id}/performance` | GET | Obtener datos de desempeño |
|
||||||
|
| `/api/portfolio/{id}/risk-metrics` | GET | Calcular métricas de riesgo |
|
||||||
|
| `/api/portfolio/stress-test` | POST | Ejecutar prueba de estrés |
|
||||||
|
| `/api/portfolio/{id}/rebalance/calculate` | POST | Calcular plan de rebalanceo |
|
||||||
|
| `/api/portfolio/{id}/rebalance/execute` | POST | Ejecutar rebalanceo |
|
||||||
|
| `/api/portfolio/{id}/reports` | GET | Listar reportes |
|
||||||
|
| `/api/portfolio/report/generate` | POST | Generar reporte |
|
||||||
|
| `/api/portfolio/report/{id}/export` | GET | Exportar reporte |
|
||||||
|
|
||||||
|
### Servicios Externos
|
||||||
|
|
||||||
|
- **Market Data Provider:** Para precios en tiempo real
|
||||||
|
- **Benchmark Data:** Índices de referencia (S&P 500, etc.)
|
||||||
|
- **Reporting Engine:** Generación de reportes complejos
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙️ Configuración y Constantes
|
||||||
|
|
||||||
|
### Variables de Entorno
|
||||||
|
|
||||||
|
```env
|
||||||
|
VITE_PORTFOLIO_API_BASE_URL=http://localhost:3000/api
|
||||||
|
VITE_CHART_LIBRARY=echarts|chart.js
|
||||||
|
VITE_REPORT_FORMAT_DEFAULT=pdf
|
||||||
|
VITE_AUTO_REFRESH_INTERVAL=30000
|
||||||
|
VITE_MAX_POSITIONS_DISPLAY=50
|
||||||
|
```
|
||||||
|
|
||||||
|
### Constantes de Aplicación
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const CURRENCY_SYMBOLS: Record<string, string> = {
|
||||||
|
USD: '$',
|
||||||
|
EUR: '€',
|
||||||
|
COP: '$',
|
||||||
|
};
|
||||||
|
|
||||||
|
const TIMEFRAMES = ['D', 'W', 'M', 'Y', 'ALL'] as const;
|
||||||
|
|
||||||
|
const RISK_LEVELS = {
|
||||||
|
low: { min: 0, max: 0.08 },
|
||||||
|
medium: { min: 0.08, max: 0.15 },
|
||||||
|
high: { min: 0.15, max: Infinity },
|
||||||
|
};
|
||||||
|
|
||||||
|
const STRESS_SCENARIOS = [
|
||||||
|
{ id: 'market-crash', label: 'Caída de Mercado (-10%)', intensity: -0.1 },
|
||||||
|
{ id: 'rate-shock', label: 'Choque de Tasas (+2%)', intensity: 0.02 },
|
||||||
|
{ id: 'geopolitical', label: 'Evento Geopolítico', intensity: -0.05 },
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📱 Responsive Design
|
||||||
|
|
||||||
|
### Breakpoints
|
||||||
|
|
||||||
|
- **Mobile:** < 640px
|
||||||
|
- **Tablet:** 640px - 1024px
|
||||||
|
- **Desktop:** > 1024px
|
||||||
|
|
||||||
|
### Ajustes por Pantalla
|
||||||
|
|
||||||
|
| Elemento | Mobile | Tablet | Desktop |
|
||||||
|
|----------|--------|--------|---------|
|
||||||
|
| PortfolioSummaryCard | Apilado | 2 columnas | 4 columnas |
|
||||||
|
| PositionsTable | Horizontal scroll | Scroll limitado | Completo |
|
||||||
|
| PerformanceChart | Altura reducida | Altura media | Altura completa |
|
||||||
|
| RiskMetricsPanel | 1 columna | 2 columnas | 3 columnas |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔒 Seguridad
|
||||||
|
|
||||||
|
- **Autenticación:** Bearer token en headers
|
||||||
|
- **Validación Frontend:** Validación de input antes de envío
|
||||||
|
- **HTTPS Obligatorio:** En producción
|
||||||
|
- **CORS:** Configuración restrictiva
|
||||||
|
- **Rate Limiting:** Control de llamadas a API
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 Notas de Implementación
|
||||||
|
|
||||||
|
### Estado Actual
|
||||||
|
|
||||||
|
**Este documento describe un diseño que aún no ha sido implementado.** No existe código base en el repositorio para estos componentes.
|
||||||
|
|
||||||
|
### Próximos Pasos
|
||||||
|
|
||||||
|
1. Crear estructura de directorios de componentes
|
||||||
|
2. Implementar store de Pinia con acciones básicas
|
||||||
|
3. Desarrollar componentes en orden de dependencia
|
||||||
|
4. Integrar con APIs backend
|
||||||
|
5. Implementar pruebas unitarias
|
||||||
|
6. Realizar pruebas de integración
|
||||||
|
7. Optimizar rendimiento
|
||||||
|
|
||||||
|
### Consideraciones Técnicas
|
||||||
|
|
||||||
|
- **Framework:** Vue 3 (Composition API recomendada)
|
||||||
|
- **State Management:** Pinia
|
||||||
|
- **Gráficos:** Echarts o Chart.js según disponibilidad
|
||||||
|
- **Validación:** Vee-validate
|
||||||
|
- **Estilos:** TailwindCSS o SCSS
|
||||||
|
- **Pruebas:** Vitest + Vue Test Utils
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 Referencias Relacionadas
|
||||||
|
|
||||||
|
- OQI-008 Portfolio Manager - Especificación General
|
||||||
|
- ET-PFM-001-backend.md - Especificación Backend
|
||||||
|
- ET-PFM-002-database.md - Especificación Base de Datos
|
||||||
|
- Arquitectura Trading Platform
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Control de Cambios
|
||||||
|
|
||||||
|
| Versión | Fecha | Autor | Cambios |
|
||||||
|
|---------|-------|-------|---------|
|
||||||
|
| 1.0.0 | 2026-01-25 | Sistema | Especificación inicial |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Estado:** A IMPLEMENTAR | **Prioridad:** Alta | **Dependencias:** Backend Portfolio Manager (OQI-008)
|
||||||
@ -0,0 +1,154 @@
|
|||||||
|
---
|
||||||
|
id: "US-PFM-013"
|
||||||
|
title: "Alerta de Rebalanceo"
|
||||||
|
type: "User Story"
|
||||||
|
status: "Pending"
|
||||||
|
priority: "Media"
|
||||||
|
epic: "OQI-008"
|
||||||
|
project: "trading-platform"
|
||||||
|
story_points: 5
|
||||||
|
created_date: "2026-01-25"
|
||||||
|
updated_date: "2026-01-25"
|
||||||
|
---
|
||||||
|
|
||||||
|
# US-PFM-013: Alerta de Rebalanceo
|
||||||
|
|
||||||
|
**Épica:** OQI-008 - Portfolio Manager
|
||||||
|
**Story Points:** 5 | **Prioridad:** P2
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Historia de Usuario
|
||||||
|
|
||||||
|
**Como** inversor
|
||||||
|
**Quiero** recibir alertas cuando mi portafolio se desvíe de la asignación objetivo
|
||||||
|
**Para** mantener mi estrategia de inversión y rebalancear cuando sea necesario
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Criterios de Aceptación
|
||||||
|
|
||||||
|
```gherkin
|
||||||
|
Scenario: Configurar umbral de desviación
|
||||||
|
Given soy usuario Pro/Premium
|
||||||
|
And estoy en la sección de Alertas
|
||||||
|
When configuro un umbral de desviación (ej: 5%)
|
||||||
|
Then el sistema guarda la configuración
|
||||||
|
And veo el umbral configurado en mi dashboard
|
||||||
|
|
||||||
|
Scenario: Recibir alerta cuando se excede el umbral
|
||||||
|
Given tengo un umbral configurado de 5%
|
||||||
|
And mi asignación objetivo es 60% acciones / 40% bonos
|
||||||
|
When mi portafolio alcanza 65% acciones / 35% bonos
|
||||||
|
Then recibo notificación push
|
||||||
|
And recibo notificación email
|
||||||
|
And veo badge de alerta en el dashboard
|
||||||
|
|
||||||
|
Scenario: Ver desviación actual del portafolio
|
||||||
|
Given estoy en la sección de Alertas
|
||||||
|
When consulto mi portafolio
|
||||||
|
Then veo tabla con cada clase de activo
|
||||||
|
And cada fila muestra: asignación objetivo, asignación actual, desviación %
|
||||||
|
And las desviaciones > umbral se resaltan en rojo
|
||||||
|
|
||||||
|
Scenario: Recibir sugerencias de rebalanceo
|
||||||
|
Given mi portafolio está fuera de balance
|
||||||
|
When visualizo la desviación
|
||||||
|
Then veo sugerencias automáticas de rebalanceo
|
||||||
|
And cada sugerencia muestra: qué vender, qué comprar, montos
|
||||||
|
And puedo ejecutar rebalanceo desde la sugerencia (1-click)
|
||||||
|
|
||||||
|
Scenario: Configurar canales de notificación
|
||||||
|
Given estoy en configuración de alertas
|
||||||
|
When selecciono canales (push, email, SMS)
|
||||||
|
Then el sistema respeta mis preferencias
|
||||||
|
And solo recibo en canales habilitados
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Dependencias
|
||||||
|
- RF-PFM-007.1, RF-PFM-007.2, RF-PFM-007.3
|
||||||
|
- ET-PFM-007
|
||||||
|
- US-PFM-009 (Asignación objetivo)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## API Endpoints
|
||||||
|
|
||||||
|
### POST /api/portfolio/alerts
|
||||||
|
**Configurar umbral de alerta**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"userId": "uuid",
|
||||||
|
"threshold": 5.0,
|
||||||
|
"channels": ["push", "email"],
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
**Response:** 201 Created
|
||||||
|
|
||||||
|
### GET /api/portfolio/deviation
|
||||||
|
**Obtener desviación actual del portafolio**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"userId": "uuid",
|
||||||
|
"timestamp": "2026-01-25T10:30:00Z",
|
||||||
|
"assets": [
|
||||||
|
{
|
||||||
|
"assetClass": "stocks",
|
||||||
|
"targetAllocation": 60.0,
|
||||||
|
"currentAllocation": 65.2,
|
||||||
|
"deviation": 5.2,
|
||||||
|
"status": "alert"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"assetClass": "bonds",
|
||||||
|
"targetAllocation": 40.0,
|
||||||
|
"currentAllocation": 34.8,
|
||||||
|
"deviation": -5.2,
|
||||||
|
"status": "alert"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isBalanced": false,
|
||||||
|
"suggestions": [
|
||||||
|
{
|
||||||
|
"action": "sell",
|
||||||
|
"assetClass": "stocks",
|
||||||
|
"amount": 500.00
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "buy",
|
||||||
|
"assetClass": "bonds",
|
||||||
|
"amount": 500.00
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Elementos de UI/UX
|
||||||
|
|
||||||
|
1. **Card de Alertas en Dashboard**
|
||||||
|
- Mostrar estado: ✓ Balanceado / ⚠ Fuera de balance
|
||||||
|
- Badge rojo si hay desviación > umbral
|
||||||
|
- Botón "Configurar"
|
||||||
|
|
||||||
|
2. **Modal de Configuración**
|
||||||
|
- Input slider: umbral 1-10%
|
||||||
|
- Checkboxes: canales de notificación
|
||||||
|
- Botón Guardar
|
||||||
|
|
||||||
|
3. **Página de Desviación**
|
||||||
|
- Tabla con desgloses por activo
|
||||||
|
- Gráfico de dona: objetivo vs actual
|
||||||
|
- Sugerencias de rebalanceo con botón "Ejecutar"
|
||||||
|
|
||||||
|
4. **Notificaciones**
|
||||||
|
- Push: "Tu portafolio se desvió 5.2% de tu objetivo. Rebalancear"
|
||||||
|
- Email: Detalles completos + link a dashboard
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Historia de usuario - Sistema NEXUS*
|
||||||
@ -0,0 +1,177 @@
|
|||||||
|
---
|
||||||
|
id: "US-PFM-014"
|
||||||
|
title: "Generar Reporte PDF"
|
||||||
|
type: "User Story"
|
||||||
|
status: "Ready"
|
||||||
|
priority: "Alta"
|
||||||
|
epic: "OQI-008"
|
||||||
|
project: "trading-platform"
|
||||||
|
story_points: 5
|
||||||
|
created_date: "2026-01-25"
|
||||||
|
updated_date: "2026-01-25"
|
||||||
|
---
|
||||||
|
|
||||||
|
# US-PFM-014: Generar Reporte PDF
|
||||||
|
|
||||||
|
**Épica:** OQI-008 - Portfolio Manager
|
||||||
|
**Story Points:** 5 | **Prioridad:** P0
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Historia de Usuario
|
||||||
|
|
||||||
|
**Como** inversor
|
||||||
|
**Quiero** generar reportes PDF de mi portafolio
|
||||||
|
**Para** análisis offline o compartir con asesores financieros
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Criterios de Aceptación
|
||||||
|
|
||||||
|
```gherkin
|
||||||
|
Scenario: Seleccionar período de reporte
|
||||||
|
Given estoy en la sección de reportes del portafolio
|
||||||
|
And tengo datos de inversión disponibles
|
||||||
|
When selecciono un rango de fechas
|
||||||
|
Then puedo elegir período predeterminado (mensual, trimestral, anual)
|
||||||
|
And puedo ingresar fechas personalizadas
|
||||||
|
And el período es validado contra datos disponibles
|
||||||
|
|
||||||
|
Scenario: Generar reporte con gráficos
|
||||||
|
Given seleccioné un período válido
|
||||||
|
When solicito generar reporte PDF
|
||||||
|
Then el documento incluye gráficos de:
|
||||||
|
- Evolución del valor del portafolio (línea temporal)
|
||||||
|
- Distribución por activos (pie chart)
|
||||||
|
- Comparativo portafolio vs benchmark
|
||||||
|
- Retorno acumulado vs rentabilidad mensual (barras)
|
||||||
|
|
||||||
|
Scenario: Incluir métricas de rendimiento
|
||||||
|
Given el reporte está siendo generado
|
||||||
|
When se incluyen métricas de rendimiento
|
||||||
|
Then veo en el documento:
|
||||||
|
- Rentabilidad total (%)
|
||||||
|
- Rentabilidad anualizada
|
||||||
|
- Volatilidad
|
||||||
|
- Ratio de Sharpe
|
||||||
|
- Máxima pérdida acumulada (drawdown)
|
||||||
|
- Ratio ganancia/pérdida
|
||||||
|
|
||||||
|
Scenario: Detalle de posiciones
|
||||||
|
Given el reporte incluye posiciones
|
||||||
|
When se genera la sección de posiciones
|
||||||
|
Then veo tabla con:
|
||||||
|
- Código/Ticker del activo
|
||||||
|
- Cantidad de unidades
|
||||||
|
- Precio de compra/Precio actual
|
||||||
|
- Valor actual en portafolio
|
||||||
|
- Rendimiento (% y valor absoluto)
|
||||||
|
- Fecha de compra
|
||||||
|
|
||||||
|
Scenario: Descargar reporte
|
||||||
|
Given el reporte PDF está listo
|
||||||
|
When hago clic en "Descargar"
|
||||||
|
Then el archivo se descarga como: Portfolio_[fecha_inicio]_[fecha_fin]_[timestamp].pdf
|
||||||
|
And el formato PDF es optimizado (tamaño <5MB)
|
||||||
|
|
||||||
|
Scenario: Enviar reporte por email
|
||||||
|
Given el reporte PDF está listo
|
||||||
|
When hago clic en "Enviar por Email"
|
||||||
|
And ingreso dirección(es) de correo
|
||||||
|
Then el PDF se envía a los destinatarios
|
||||||
|
And recibo confirmación de envío
|
||||||
|
And el destinatario recibe el correo con adjunto
|
||||||
|
|
||||||
|
Scenario: Reporte multi-cuenta
|
||||||
|
Given tengo múltiples cuentas/portafolios
|
||||||
|
When selecciono la opción "Consolidado"
|
||||||
|
Then el reporte muestra:
|
||||||
|
- Resumen general
|
||||||
|
- Desglose por cada cuenta
|
||||||
|
- Distribución consolidada
|
||||||
|
- Rentabilidades combinadas
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## API Endpoint
|
||||||
|
|
||||||
|
### POST /api/portfolio/reports/pdf
|
||||||
|
|
||||||
|
**Request:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"portfolioId": "uuid",
|
||||||
|
"startDate": "2026-01-01",
|
||||||
|
"endDate": "2026-01-25",
|
||||||
|
"includeGraphs": true,
|
||||||
|
"includeMetrics": true,
|
||||||
|
"includePositions": true,
|
||||||
|
"benchmark": "SPX",
|
||||||
|
"downloadFormat": "pdf",
|
||||||
|
"sendEmail": {
|
||||||
|
"enabled": false,
|
||||||
|
"recipients": []
|
||||||
|
},
|
||||||
|
"consolidated": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response (200):**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "report_uuid",
|
||||||
|
"status": "generated",
|
||||||
|
"downloadUrl": "/api/reports/download/report_uuid",
|
||||||
|
"emailStatus": "pending|sent|failed",
|
||||||
|
"generatedAt": "2026-01-25T10:30:00Z",
|
||||||
|
"fileName": "Portfolio_2026-01-01_2026-01-25_20260125103000.pdf"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response (202 - Async):**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"jobId": "job_uuid",
|
||||||
|
"status": "processing",
|
||||||
|
"estimatedTime": "30s",
|
||||||
|
"checkUrl": "/api/portfolio/reports/pdf/status/job_uuid"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Dependencias
|
||||||
|
|
||||||
|
- RF-PFM-007: Cálculo de métricas de rendimiento
|
||||||
|
- RF-PFM-008: Integración con servicio de reportes
|
||||||
|
- RF-PFM-009: Generación de gráficos embebidos en PDF
|
||||||
|
- ET-PFM-007: Entidad ReportPDF
|
||||||
|
- US-PFM-012: Reporte Fiscal (complementario)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Notas Técnicas
|
||||||
|
|
||||||
|
- Usar librería `pdfkit` o `puppeteer` para generación
|
||||||
|
- Gráficos embebidos con `chart.js` o `lightweight-charts`
|
||||||
|
- Queue asíncrona para reportes grandes (>500MB portafolio)
|
||||||
|
- Implementar retry automático en fallos de email
|
||||||
|
- Almacenar reportes 30 días máximo
|
||||||
|
- Validar tamaño de portafolio antes de generar
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Aceptación
|
||||||
|
|
||||||
|
- [ ] Endpoint POST creado y documentado
|
||||||
|
- [ ] Generación de PDF con gráficos funcional
|
||||||
|
- [ ] Descarga de archivos validada
|
||||||
|
- [ ] Envío por email implementado
|
||||||
|
- [ ] Tests unitarios y de integración
|
||||||
|
- [ ] Documentación en Swagger
|
||||||
|
- [ ] Manual de usuario actualizado
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Historia de usuario - Sistema NEXUS*
|
||||||
@ -0,0 +1,633 @@
|
|||||||
|
# ET-MKT-003: Especificación Frontend - Módulo Marketplace
|
||||||
|
|
||||||
|
**Documento:** ET-MKT-003-frontend.md
|
||||||
|
**Proyecto:** Trading Platform - OQI-009-Marketplace
|
||||||
|
**Versión:** 1.0.0
|
||||||
|
**Estado:** A IMPLEMENTAR
|
||||||
|
**Última Actualización:** 2026-01-25
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Descripción General
|
||||||
|
|
||||||
|
Este documento define la especificación de la capa frontend para el módulo Marketplace (OQI-009) del sistema Trading Platform. El módulo permite a usuarios explorar, comparar y adquirir productos financieros digitales (Signal Packs, sesiones de asesoramiento y complementos de visualización).
|
||||||
|
|
||||||
|
### Nota Importante
|
||||||
|
**Esta especificación describe un diseño a implementar. No existe código frontend actual para este módulo.** Las páginas y componentes listados representan la propuesta de arquitectura que será desarrollada en futuras iteraciones.
|
||||||
|
|
||||||
|
### Productos Soportados
|
||||||
|
- **Signal Packs**: Paquetes de señales de trading pre-configuradas
|
||||||
|
- **Advisory Sessions**: Sesiones de asesoramiento con expertos
|
||||||
|
- **Visualization Addons**: Complementos avanzados de visualización de mercados
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Arquitectura Frontend
|
||||||
|
|
||||||
|
### 2.1 Stack Tecnológico
|
||||||
|
```
|
||||||
|
Framework: React 18.x
|
||||||
|
Lenguaje: TypeScript 5.x
|
||||||
|
Estilos: Tailwind CSS + CSS Modules
|
||||||
|
Gestión Estado: Redux Toolkit
|
||||||
|
HTTP Client: Axios
|
||||||
|
Componentes UI: Headless UI / Radix UI
|
||||||
|
Testing: Jest + React Testing Library
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 Patrones Arquitectónicos
|
||||||
|
- **Componentes Funcionales:** Todos los componentes deben ser funcionales con hooks
|
||||||
|
- **Estructura por Features:** Organización en carpetas por feature (pages, components)
|
||||||
|
- **Custom Hooks:** Lógica reutilizable extraída en hooks personalizados
|
||||||
|
- **Context API:** Para estados compartidos entre componentes distantes
|
||||||
|
- **Lazy Loading:** Carga diferida de componentes con React.lazy()
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Estructura de Directorios
|
||||||
|
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
├── features/
|
||||||
|
│ └── marketplace/
|
||||||
|
│ ├── pages/
|
||||||
|
│ │ ├── MarketplacePage/
|
||||||
|
│ │ ├── ProductDetailPage/
|
||||||
|
│ │ ├── SignalPacksPage/
|
||||||
|
│ │ └── AdvisoryPage/
|
||||||
|
│ ├── components/
|
||||||
|
│ │ ├── ProductCard/
|
||||||
|
│ │ ├── SignalPackCard/
|
||||||
|
│ │ ├── AdvisorCard/
|
||||||
|
│ │ ├── BookingModal/
|
||||||
|
│ │ ├── FilterBar/
|
||||||
|
│ │ ├── ProductGallery/
|
||||||
|
│ │ └── ReviewSection/
|
||||||
|
│ ├── hooks/
|
||||||
|
│ │ ├── useMarketplaceFilters.ts
|
||||||
|
│ │ ├── useProductCart.ts
|
||||||
|
│ │ └── useAdvisorBooking.ts
|
||||||
|
│ ├── services/
|
||||||
|
│ │ ├── marketplaceApi.ts
|
||||||
|
│ │ ├── productApi.ts
|
||||||
|
│ │ └── advisoryApi.ts
|
||||||
|
│ ├── types/
|
||||||
|
│ │ └── marketplace.types.ts
|
||||||
|
│ ├── store/
|
||||||
|
│ │ └── marketplaceSlice.ts
|
||||||
|
│ └── styles/
|
||||||
|
│ └── marketplace.module.css
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Páginas Propuestas
|
||||||
|
|
||||||
|
### 4.1 MarketplacePage
|
||||||
|
**Ruta:** `/marketplace`
|
||||||
|
**Responsabilidad:** Landing principal del marketplace con vista general de todos los productos
|
||||||
|
|
||||||
|
#### Características:
|
||||||
|
- Hero section con introducción al marketplace
|
||||||
|
- Buscador global de productos
|
||||||
|
- Filtros por categoría (Signal Packs, Advisory, Addons)
|
||||||
|
- Grid de productos destacados
|
||||||
|
- Testimonios de usuarios
|
||||||
|
- Llamadas a la acción (CTA)
|
||||||
|
|
||||||
|
#### Responsive Design:
|
||||||
|
- Desktop: Grid 4 columnas
|
||||||
|
- Tablet: Grid 2 columnas
|
||||||
|
- Mobile: Grid 1 columna
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4.2 ProductDetailPage
|
||||||
|
**Ruta:** `/marketplace/product/:productId`
|
||||||
|
**Responsabilidad:** Vista detallada de un producto individual
|
||||||
|
|
||||||
|
#### Contenido Esperado:
|
||||||
|
- Galería de imágenes/screenshots
|
||||||
|
- Nombre, descripción y precio
|
||||||
|
- Especificaciones técnicas
|
||||||
|
- Comparativa con productos similares
|
||||||
|
- Sección de reviews y ratings
|
||||||
|
- Botón de compra/carrito
|
||||||
|
- Información del vendedor
|
||||||
|
- FAQ relacionadas
|
||||||
|
|
||||||
|
#### Datos a Cargar:
|
||||||
|
- Detalles del producto (API GET `/api/products/:id`)
|
||||||
|
- Reviews (API GET `/api/products/:id/reviews`)
|
||||||
|
- Productos relacionados (API GET `/api/products/:id/related`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4.3 SignalPacksPage
|
||||||
|
**Ruta:** `/marketplace/signal-packs`
|
||||||
|
**Responsabilidad:** Página especializada para Signal Packs con funcionalidades específicas
|
||||||
|
|
||||||
|
#### Características:
|
||||||
|
- Filtros avanzados (tipo de señal, precisión, timeframe)
|
||||||
|
- Comparador de paquetes
|
||||||
|
- Gráfico de performance histórico
|
||||||
|
- Tabla de estadísticas en tiempo real
|
||||||
|
- Demos interactivos
|
||||||
|
- Botón "Probar Gratis" y "Comprar Ahora"
|
||||||
|
|
||||||
|
#### Componentes Específicos:
|
||||||
|
- `SignalPackCard` (extendido)
|
||||||
|
- `PerformanceChart`
|
||||||
|
- `ComparisonTable`
|
||||||
|
- `LiveStatisticsWidget`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4.4 AdvisoryPage
|
||||||
|
**Ruta:** `/marketplace/advisory`
|
||||||
|
**Responsabilidad:** Página para sesiones de asesoramiento con expertos
|
||||||
|
|
||||||
|
#### Características:
|
||||||
|
- Directorio de asesores con filtros
|
||||||
|
- Calendarios de disponibilidad
|
||||||
|
- Precios por tipo de sesión
|
||||||
|
- Credenciales y especialidades de asesores
|
||||||
|
- Sistema de booking integrado
|
||||||
|
- Historial de sesiones del usuario
|
||||||
|
- Funcionalidad de reseñas post-sesión
|
||||||
|
|
||||||
|
#### Integración Modal:
|
||||||
|
- Modal de booking (`BookingModal`) se dispara desde tarjetas de asesores
|
||||||
|
- Confirmación y pago dentro del modal
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Componentes Propuestos
|
||||||
|
|
||||||
|
### 5.1 ProductCard
|
||||||
|
**Ubicación:** `components/ProductCard/`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface ProductCardProps {
|
||||||
|
product: Product;
|
||||||
|
onViewDetails: (productId: string) => void;
|
||||||
|
onAddToCart?: (product: Product) => void;
|
||||||
|
variant?: 'default' | 'compact' | 'featured';
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Elementos:**
|
||||||
|
- Imagen de portada
|
||||||
|
- Nombre del producto
|
||||||
|
- Rating (estrellas + número de reviews)
|
||||||
|
- Precio y descuento (si aplica)
|
||||||
|
- Descripción breve (2-3 líneas)
|
||||||
|
- Etiquetas (categoría, trending)
|
||||||
|
- Botones de acción (Ver detalles, Agregar al carrito)
|
||||||
|
|
||||||
|
**Estados:**
|
||||||
|
- Default: Producto disponible
|
||||||
|
- Loading: Cargando datos
|
||||||
|
- Disabled: Producto no disponible
|
||||||
|
- Favorited: Producto agregado a favoritos
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5.2 SignalPackCard
|
||||||
|
**Ubicación:** `components/SignalPackCard/`
|
||||||
|
**Extends:** ProductCard (con extensiones específicas)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface SignalPackCardProps extends ProductCardProps {
|
||||||
|
winRate: number;
|
||||||
|
totalSignals: number;
|
||||||
|
avgReturnPerSignal: number;
|
||||||
|
lastSignalDate: Date;
|
||||||
|
difficultyLevel: 'beginner' | 'intermediate' | 'advanced';
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Elementos Adicionales:**
|
||||||
|
- Badge de dificultad
|
||||||
|
- Win rate en porcentaje
|
||||||
|
- Total de señales generadas
|
||||||
|
- Retorno promedio por señal
|
||||||
|
- Fecha de última señal
|
||||||
|
- Gráfico mini de performance (sparkline)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5.3 AdvisorCard
|
||||||
|
**Ubicación:** `components/AdvisorCard/`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface AdvisorCardProps {
|
||||||
|
advisor: Advisor;
|
||||||
|
onBookSession: (advisorId: string) => void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Elementos:**
|
||||||
|
- Foto de perfil
|
||||||
|
- Nombre y especialidad
|
||||||
|
- Rating y número de sesiones completadas
|
||||||
|
- Descripción breve
|
||||||
|
- Certificaciones y credenciales (badges)
|
||||||
|
- Precio por hora/sesión
|
||||||
|
- Disponibilidad (horarios)
|
||||||
|
- Botón "Reservar Ahora"
|
||||||
|
- Link a perfil completo
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5.4 BookingModal
|
||||||
|
**Ubicación:** `components/BookingModal/`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface BookingModalProps {
|
||||||
|
advisor: Advisor;
|
||||||
|
isOpen: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
onConfirm: (booking: BookingRequest) => Promise<void>;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Flujo:**
|
||||||
|
1. **Selección de Sesión**: Tipo y duración
|
||||||
|
2. **Calendario**: Seleccionar fecha/hora disponible
|
||||||
|
3. **Detalles Personales**: Confirmar información del usuario
|
||||||
|
4. **Resumen de Pago**: Mostrar total y método de pago
|
||||||
|
5. **Confirmación**: Enviar reserva y mostrar confirmación
|
||||||
|
|
||||||
|
**Validaciones:**
|
||||||
|
- Fecha/hora no pueden ser pasadas
|
||||||
|
- Usuario debe estar autenticado
|
||||||
|
- Validar disponibilidad en tiempo real
|
||||||
|
- Procesar pago antes de confirmar
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5.5 Componentes Auxiliares
|
||||||
|
|
||||||
|
#### FilterBar
|
||||||
|
**Ubicación:** `components/FilterBar/`
|
||||||
|
**Responsabilidad:** Filtros dinámicos reutilizables
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface FilterBarProps {
|
||||||
|
filters: FilterDefinition[];
|
||||||
|
onApply: (appliedFilters: Record<string, any>) => void;
|
||||||
|
onReset: () => void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### ProductGallery
|
||||||
|
**Ubicación:** `components/ProductGallery/`
|
||||||
|
**Responsabilidad:** Galería de imágenes con zoom y thumbnails
|
||||||
|
|
||||||
|
#### ReviewSection
|
||||||
|
**Ubicación:** `components/ReviewSection/`
|
||||||
|
**Responsabilidad:** Mostrar y permitir crear nuevas reseñas
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Flujos de Usuario Principales
|
||||||
|
|
||||||
|
### 6.1 Flujo: Explorar y Comprar Signal Pack
|
||||||
|
```
|
||||||
|
MarketplacePage
|
||||||
|
→ (filtrar/buscar)
|
||||||
|
→ SignalPacksPage
|
||||||
|
→ ProductDetailPage (Signal Pack)
|
||||||
|
→ Carrito
|
||||||
|
→ Checkout
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.2 Flujo: Reservar Sesión de Asesoramiento
|
||||||
|
```
|
||||||
|
MarketplacePage
|
||||||
|
→ AdvisoryPage
|
||||||
|
→ (filtrar asesores)
|
||||||
|
→ AdvisorCard
|
||||||
|
→ BookingModal (se abre)
|
||||||
|
→ Confirmación y Pago
|
||||||
|
→ Página de confirmación
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.3 Flujo: Ver Detalles de Complemento
|
||||||
|
```
|
||||||
|
MarketplacePage
|
||||||
|
→ ProductDetailPage (Addon)
|
||||||
|
→ Comparar con similares
|
||||||
|
→ Agregar al carrito
|
||||||
|
→ Checkout
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Integración con API
|
||||||
|
|
||||||
|
### 7.1 Endpoints Consumidos
|
||||||
|
```
|
||||||
|
GET /api/marketplace/products # Listar productos con filtros
|
||||||
|
GET /api/marketplace/products/:id # Detalle de producto
|
||||||
|
GET /api/marketplace/products/:id/reviews
|
||||||
|
POST /api/marketplace/cart # Agregar al carrito
|
||||||
|
GET /api/marketplace/advisors # Listar asesores
|
||||||
|
POST /api/marketplace/bookings # Crear booking
|
||||||
|
GET /api/user/orders # Historial de compras
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.2 Servicios API (Custom Hooks)
|
||||||
|
```typescript
|
||||||
|
// marketplaceApi.ts
|
||||||
|
export const marketplaceApi = {
|
||||||
|
getProducts: (filters?: ProductFilters) => Promise<Product[]>,
|
||||||
|
getProductDetail: (id: string) => Promise<Product>,
|
||||||
|
getAdvisors: (filters?: AdvisorFilters) => Promise<Advisor[]>,
|
||||||
|
createBooking: (booking: BookingRequest) => Promise<BookingResponse>,
|
||||||
|
getReviews: (productId: string) => Promise<Review[]>,
|
||||||
|
submitReview: (review: ReviewSubmission) => Promise<Review>,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Estado Global (Redux)
|
||||||
|
|
||||||
|
### 8.1 Slices del Store
|
||||||
|
```typescript
|
||||||
|
// marketplaceSlice.ts
|
||||||
|
{
|
||||||
|
marketplace: {
|
||||||
|
products: Product[],
|
||||||
|
selectedProduct: Product | null,
|
||||||
|
advisors: Advisor[],
|
||||||
|
cart: CartItem[],
|
||||||
|
filters: {
|
||||||
|
category: string[],
|
||||||
|
priceRange: [number, number],
|
||||||
|
rating: number,
|
||||||
|
// ... más filtros
|
||||||
|
},
|
||||||
|
loading: boolean,
|
||||||
|
error: string | null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.2 Actions Principales
|
||||||
|
- `setProducts(products)`
|
||||||
|
- `setSelectedProduct(product)`
|
||||||
|
- `addToCart(product)`
|
||||||
|
- `removeFromCart(productId)`
|
||||||
|
- `updateFilters(filters)`
|
||||||
|
- `setLoading(boolean)`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Validaciones Frontend
|
||||||
|
|
||||||
|
### 9.1 Búsqueda y Filtros
|
||||||
|
- Campo de búsqueda: mínimo 3 caracteres
|
||||||
|
- Filtros deben reflejar datos del backend
|
||||||
|
- Debounce en búsqueda (300ms)
|
||||||
|
|
||||||
|
### 9.2 Booking de Sesiones
|
||||||
|
- Fecha no puede ser en el pasado
|
||||||
|
- Hora debe coincidir con disponibilidad del asesor
|
||||||
|
- Usuario debe estar autenticado
|
||||||
|
- Validar saldo/método de pago
|
||||||
|
|
||||||
|
### 9.3 Carrito
|
||||||
|
- Validar cantidad disponible
|
||||||
|
- Evitar agregar mismo producto dos veces (aumentar cantidad)
|
||||||
|
- Mostrar total actualizado
|
||||||
|
|
||||||
|
### 9.4 Formularios
|
||||||
|
- Validación en tiempo real
|
||||||
|
- Mensajes de error claros
|
||||||
|
- Estados loading en botones de envío
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Performance y Optimización
|
||||||
|
|
||||||
|
### 10.1 Técnicas
|
||||||
|
- **Code Splitting:** Lazy loading de páginas con React.lazy()
|
||||||
|
- **Memoization:** React.memo() para componentes puros
|
||||||
|
- **Image Optimization:** Optimizar imágenes, WebP, lazy loading
|
||||||
|
- **Virtualization:** Virtualizaciónlist para listas largas (si aplica)
|
||||||
|
- **Bundle Analysis:** Usar webpack-bundle-analyzer
|
||||||
|
|
||||||
|
### 10.2 Métricas Objetivo
|
||||||
|
- LCP (Largest Contentful Paint): < 2.5s
|
||||||
|
- FID (First Input Delay): < 100ms
|
||||||
|
- CLS (Cumulative Layout Shift): < 0.1
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Accesibilidad (A11y)
|
||||||
|
|
||||||
|
### 11.1 Estándares
|
||||||
|
- WCAG 2.1 Nivel AA
|
||||||
|
- Navegación por teclado
|
||||||
|
- Contraste mínimo 4.5:1
|
||||||
|
- Labels explícitos en formularios
|
||||||
|
- ARIA labels donde sea necesario
|
||||||
|
|
||||||
|
### 11.2 Implementación
|
||||||
|
```typescript
|
||||||
|
// Ejemplo: Botón accesible
|
||||||
|
<button
|
||||||
|
aria-label="Agregar producto al carrito"
|
||||||
|
className="btn-primary"
|
||||||
|
onClick={handleAddToCart}
|
||||||
|
>
|
||||||
|
Agregar al Carrito
|
||||||
|
</button>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. Testing
|
||||||
|
|
||||||
|
### 12.1 Cobertura Objetivo
|
||||||
|
- Unit Tests: 80% de componentes
|
||||||
|
- Integration Tests: Flujos principales
|
||||||
|
- E2E Tests: Casos de compra y booking
|
||||||
|
|
||||||
|
### 12.2 Ejemplos de Pruebas
|
||||||
|
```typescript
|
||||||
|
// ProductCard.test.tsx
|
||||||
|
describe('ProductCard', () => {
|
||||||
|
it('should render product information', () => {
|
||||||
|
const product = mockProduct();
|
||||||
|
render(<ProductCard product={product} onViewDetails={jest.fn()} />);
|
||||||
|
expect(screen.getByText(product.name)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call onViewDetails when clicked', () => {
|
||||||
|
const onViewDetails = jest.fn();
|
||||||
|
const product = mockProduct();
|
||||||
|
render(<ProductCard product={product} onViewDetails={onViewDetails} />);
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: /ver detalles/i }));
|
||||||
|
expect(onViewDetails).toHaveBeenCalledWith(product.id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. Tipo de Datos Principales
|
||||||
|
|
||||||
|
### 13.1 Interfaces TypeScript
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// marketplace.types.ts
|
||||||
|
|
||||||
|
interface Product {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
price: number;
|
||||||
|
discount?: number;
|
||||||
|
category: 'signal_pack' | 'advisory' | 'addon';
|
||||||
|
rating: number;
|
||||||
|
reviewCount: number;
|
||||||
|
image: string;
|
||||||
|
images?: string[];
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SignalPack extends Product {
|
||||||
|
winRate: number;
|
||||||
|
totalSignals: number;
|
||||||
|
avgReturnPerSignal: number;
|
||||||
|
difficultyLevel: 'beginner' | 'intermediate' | 'advanced';
|
||||||
|
lastSignalDate: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Advisor {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
photo: string;
|
||||||
|
specialty: string;
|
||||||
|
bio: string;
|
||||||
|
rating: number;
|
||||||
|
completedSessions: number;
|
||||||
|
pricePerHour: number;
|
||||||
|
certifications: Certification[];
|
||||||
|
availability: TimeSlot[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BookingRequest {
|
||||||
|
advisorId: string;
|
||||||
|
userId: string;
|
||||||
|
sessionType: 'consultation' | 'strategy_review';
|
||||||
|
duration: 30 | 60 | 90; // minutos
|
||||||
|
scheduledDate: Date;
|
||||||
|
notes?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Review {
|
||||||
|
id: string;
|
||||||
|
productId: string;
|
||||||
|
userId: string;
|
||||||
|
rating: number;
|
||||||
|
title: string;
|
||||||
|
comment: string;
|
||||||
|
createdAt: Date;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. Responsive Design Breakpoints
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* Tailwind / CSS */
|
||||||
|
Mobile: < 640px (sm)
|
||||||
|
Tablet: 640px+ (md)
|
||||||
|
Desktop: 1024px+ (lg)
|
||||||
|
Wide: 1280px+ (xl)
|
||||||
|
UltraWide: 1536px+ (2xl)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 15. Roadmap de Implementación
|
||||||
|
|
||||||
|
### Fase 1: Estructura Base (Sprint 1-2)
|
||||||
|
- [ ] Setup proyecto React + TypeScript
|
||||||
|
- [ ] Crear estructura de directorios
|
||||||
|
- [ ] Implementar routing y layout base
|
||||||
|
- [ ] Configurar Redux store
|
||||||
|
|
||||||
|
### Fase 2: Componentes (Sprint 3-4)
|
||||||
|
- [ ] ProductCard, SignalPackCard, AdvisorCard
|
||||||
|
- [ ] FilterBar, ProductGallery
|
||||||
|
- [ ] BookingModal
|
||||||
|
|
||||||
|
### Fase 3: Páginas (Sprint 5-6)
|
||||||
|
- [ ] MarketplacePage
|
||||||
|
- [ ] ProductDetailPage
|
||||||
|
- [ ] SignalPacksPage
|
||||||
|
- [ ] AdvisoryPage
|
||||||
|
|
||||||
|
### Fase 4: Integraciones y Polish (Sprint 7-8)
|
||||||
|
- [ ] Consumir APIs
|
||||||
|
- [ ] Testing
|
||||||
|
- [ ] Optimización
|
||||||
|
- [ ] Despliegue
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 16. Referencias y Documentos Relacionados
|
||||||
|
|
||||||
|
- **ET-MKT-001-database.md**: Esquema de base de datos
|
||||||
|
- **ET-MKT-002-api.md**: Especificación de API REST
|
||||||
|
- **Guía de Componentes**: (a crear en futuro)
|
||||||
|
- **Guía de Testing**: (a crear en futuro)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 17. Notas de Desarrollo
|
||||||
|
|
||||||
|
### Prácticas Recomendadas
|
||||||
|
1. Mantener componentes pequeños y enfocados
|
||||||
|
2. Usar TypeScript para toda la capa frontend
|
||||||
|
3. Escribir tests mientras se desarrolla
|
||||||
|
4. Revisar código antes de merge
|
||||||
|
5. Documentar componentes complejos
|
||||||
|
|
||||||
|
### Dependencias NPM Esperadas
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0",
|
||||||
|
"@reduxjs/toolkit": "^1.9.x",
|
||||||
|
"react-redux": "^8.1.x",
|
||||||
|
"axios": "^1.4.x",
|
||||||
|
"tailwindcss": "^3.3.x",
|
||||||
|
"typescript": "^5.1.x"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 18. Cambios Pendientes
|
||||||
|
|
||||||
|
Este documento será actualizado cuando:
|
||||||
|
- Se inicie implementación de componentes
|
||||||
|
- Se definan cambios en UX/UI
|
||||||
|
- Se agreguen nuevas funcionalidades
|
||||||
|
- Se identifiquen patrones de optimización
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Documento Generado:** 2026-01-25
|
||||||
|
**Próxima Revisión:** Tras completar Fase 1 de implementación
|
||||||
|
**Estado:** BORRADOR - A IMPLEMENTAR
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
# TASK-2026-01-25-FRONTEND-ANALYSIS
|
||||||
|
|
||||||
|
**Estado:** COMPLETADA
|
||||||
|
**Fecha:** 2026-01-25
|
||||||
|
**Tipo:** ANALYSIS
|
||||||
|
|
||||||
|
## Resumen
|
||||||
|
|
||||||
|
Analisis y documentacion detallada del frontend de trading-platform.
|
||||||
|
|
||||||
|
## Entregables Completados
|
||||||
|
|
||||||
|
### FASE 1: Inventarios (3/3)
|
||||||
|
- MASTER_INVENTORY.yml actualizado
|
||||||
|
- FRONTEND_INVENTORY.yml completado (48 componentes)
|
||||||
|
- BACKEND_INVENTORY.yml completado (trading + ML modules)
|
||||||
|
|
||||||
|
### FASE 2: Especificaciones Frontend (5/5)
|
||||||
|
- ET-AUTH-006-frontend.md (OQI-001)
|
||||||
|
- ET-ML-008-frontend.md (OQI-006)
|
||||||
|
- ET-LLM-007-frontend.md (OQI-007)
|
||||||
|
- ET-PFM-008-frontend.md (OQI-008)
|
||||||
|
- ET-MKT-003-frontend.md (OQI-009)
|
||||||
|
|
||||||
|
### FASE 3: Historias de Usuario (8 nuevas)
|
||||||
|
- US-AUTH-013, US-AUTH-014
|
||||||
|
- US-ML-008, US-ML-009, US-ML-010
|
||||||
|
- US-LLM-011
|
||||||
|
- US-PFM-013, US-PFM-014
|
||||||
|
|
||||||
|
### FASE 4: Coherencia API-Frontend
|
||||||
|
- API-FRONTEND-COVERAGE-MATRIX.md (85% cobertura)
|
||||||
|
- FILE-GENERATION-SPEC.md
|
||||||
|
|
||||||
|
### FASE 5: Dependencias
|
||||||
|
- DEPENDENCY-GRAPH.yml actualizado con epicas
|
||||||
|
|
||||||
|
## Metricas
|
||||||
|
|
||||||
|
| Metrica | Valor |
|
||||||
|
|---------|-------|
|
||||||
|
| Modulos con doc frontend | 10/10 (100%) |
|
||||||
|
| Cobertura API-Frontend | 85% |
|
||||||
|
| Historias de usuario nuevas | 8 |
|
||||||
|
| Archivos creados/modificados | 20+ |
|
||||||
|
|
||||||
|
---
|
||||||
|
*Completado: 2026-01-25 | Sistema: SIMCO v4.0.0*
|
||||||
@ -6,8 +6,8 @@ created: "2026-01-24"
|
|||||||
updated: "2026-01-24"
|
updated: "2026-01-24"
|
||||||
|
|
||||||
resumen:
|
resumen:
|
||||||
total_tareas: 0
|
total_tareas: 1
|
||||||
completadas: 0
|
completadas: 1
|
||||||
en_progreso: 0
|
en_progreso: 0
|
||||||
pendientes: 0
|
pendientes: 0
|
||||||
|
|
||||||
@ -15,9 +15,21 @@ formato_id:
|
|||||||
patron: "TASK-{YYYY-MM-DD}-{NNN}"
|
patron: "TASK-{YYYY-MM-DD}-{NNN}"
|
||||||
ejemplo: "TASK-2026-01-24-001"
|
ejemplo: "TASK-2026-01-24-001"
|
||||||
|
|
||||||
por_fecha: {}
|
por_fecha:
|
||||||
|
2026-01-25:
|
||||||
|
- id: TASK-2026-01-25-FRONTEND-ANALYSIS
|
||||||
|
titulo: "Analisis y Documentacion Frontend"
|
||||||
|
estado: COMPLETADA
|
||||||
|
tipo: ANALYSIS
|
||||||
|
|
||||||
tareas_activas: []
|
tareas_activas: []
|
||||||
|
|
||||||
|
tareas_completadas:
|
||||||
|
- id: TASK-2026-01-25-FRONTEND-ANALYSIS
|
||||||
|
fecha_inicio: "2026-01-25"
|
||||||
|
fecha_fin: "2026-01-25"
|
||||||
|
entregables: 20+
|
||||||
|
|
||||||
instrucciones:
|
instrucciones:
|
||||||
crear_tarea: |
|
crear_tarea: |
|
||||||
1. Crear carpeta YYYY-MM-DD/ si no existe
|
1. Crear carpeta YYYY-MM-DD/ si no existe
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user