Changes include: - Updated architecture documentation - Enhanced module definitions (OQI-001 to OQI-008) - ML integration documentation updates - Trading strategies documentation - Orchestration and inventory updates - Docker configuration updates 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
450 lines
16 KiB
Markdown
450 lines
16 KiB
Markdown
---
|
|
id: "US-TRD-013"
|
|
title: "Configurar Alertas de Precio"
|
|
type: "User Story"
|
|
status: "Done"
|
|
priority: "Media"
|
|
epic: "OQI-003"
|
|
story_points: 5
|
|
created_date: "2025-12-05"
|
|
updated_date: "2026-01-04"
|
|
---
|
|
|
|
# US-TRD-013: Configurar Alertas de Precio
|
|
|
|
## Metadata
|
|
|
|
| Campo | Valor |
|
|
|-------|-------|
|
|
| **ID** | US-TRD-013 |
|
|
| **Épica** | OQI-003 - Trading y Charts |
|
|
| **Módulo** | trading |
|
|
| **Prioridad** | P2 |
|
|
| **Story Points** | 3 |
|
|
| **Sprint** | Sprint 6 |
|
|
| **Estado** | Pendiente |
|
|
| **Asignado a** | Por asignar |
|
|
|
|
---
|
|
|
|
## Historia de Usuario
|
|
|
|
**Como** trader,
|
|
**quiero** configurar alertas de precio para símbolos específicos,
|
|
**para** recibir notificaciones cuando el precio alcance niveles importantes sin monitorear constantemente.
|
|
|
|
## Descripción Detallada
|
|
|
|
El usuario debe poder crear alertas de precio para cualquier símbolo, especificando condiciones como "precio mayor que", "precio menor que", o "precio cruza". Cuando la condición se cumpla, el usuario recibe una notificación push y/o email.
|
|
|
|
## Mockups/Wireframes
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ PRICE ALERTS │
|
|
├─────────────────────────────────────────────────────────────────┤
|
|
│ [+ Create Alert] │
|
|
│ │
|
|
│ Active Alerts (3) │
|
|
│ ┌─────────────────────────────────────────────────────────┐ │
|
|
│ │ BTCUSDT [...] │ │
|
|
│ │ When price goes ABOVE $100,000 │ │
|
|
│ │ Current: $97,234.50 | Distance: +2.84% │ │
|
|
│ │ Created: Dec 5, 2025 10:00 AM │ │
|
|
│ │ [🔔 Enabled] │ │
|
|
│ └─────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ ┌─────────────────────────────────────────────────────────┐ │
|
|
│ │ ETHUSDT [...] │ │
|
|
│ │ When price goes BELOW $3,700 │ │
|
|
│ │ Current: $3,845.20 | Distance: -3.92% │ │
|
|
│ │ Created: Dec 4, 2025 02:30 PM │ │
|
|
│ │ [🔕 Disabled] │ │
|
|
│ └─────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ ┌─────────────────────────────────────────────────────────┐ │
|
|
│ │ SOLUSDT [...] │ │
|
|
│ │ When price CROSSES $150 (from either direction) │ │
|
|
│ │ Current: $142.73 | Distance: -5.09% │ │
|
|
│ │ Created: Dec 3, 2025 09:15 AM │ │
|
|
│ │ [🔔 Enabled] │ │
|
|
│ └─────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ Triggered Alerts (5) [View History] │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
|
|
┌─────────────────────────────────────┐
|
|
│ CREATE PRICE ALERT │
|
|
├─────────────────────────────────────┤
|
|
│ Symbol: │
|
|
│ ┌─────────────────────────────────┐ │
|
|
│ │ BTCUSDT [▼] │ │
|
|
│ └─────────────────────────────────┘ │
|
|
│ Current Price: $97,234.50 │
|
|
│ │
|
|
│ Condition: │
|
|
│ ┌─────────────────────────────────┐ │
|
|
│ │ Price goes ABOVE [▼] │ │
|
|
│ └─────────────────────────────────┘ │
|
|
│ Options: Above, Below, Crosses │
|
|
│ │
|
|
│ Target Price: │
|
|
│ ┌─────────────────────────────────┐ │
|
|
│ │ 100,000.00 │ │
|
|
│ └─────────────────────────────────┘ │
|
|
│ Distance: +2.84% │
|
|
│ [Set +1%] [Set +5%] [Set +10%] │
|
|
│ │
|
|
│ Notification Method: │
|
|
│ [✓] Push Notification │
|
|
│ [✓] Email │
|
|
│ [ ] SMS (Premium) │
|
|
│ │
|
|
│ Message (optional): │
|
|
│ ┌─────────────────────────────────┐ │
|
|
│ │ BTC hitting resistance │ │
|
|
│ └─────────────────────────────────┘ │
|
|
│ │
|
|
│ Expires After: │
|
|
│ ┌─────────────────────────────────┐ │
|
|
│ │ Never [▼] │ │
|
|
│ └─────────────────────────────────┘ │
|
|
│ │
|
|
│ [Cancel] [Create Alert] │
|
|
└─────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Criterios de Aceptación
|
|
|
|
**Escenario 1: Crear alerta "price above"**
|
|
```gherkin
|
|
DADO que el usuario está en Price Alerts
|
|
CUANDO hace click en "+ Create Alert"
|
|
Y selecciona símbolo "BTCUSDT"
|
|
Y selecciona condición "Price goes ABOVE"
|
|
Y ingresa precio $100,000
|
|
Y habilita "Push Notification" y "Email"
|
|
Y hace click en "Create Alert"
|
|
ENTONCES se crea la alerta
|
|
Y aparece en "Active Alerts"
|
|
Y muestra distancia actual (+2.84%)
|
|
Y el sistema comienza a monitorear
|
|
```
|
|
|
|
**Escenario 2: Activación de alerta "above"**
|
|
```gherkin
|
|
DADO que existe alerta BTCUSDT ABOVE $100,000
|
|
Y el precio actual es $99,500
|
|
CUANDO el precio sube y alcanza $100,000
|
|
ENTONCES se dispara la alerta
|
|
Y se envía push notification
|
|
Y se envía email
|
|
Y la alerta pasa a "Triggered Alerts"
|
|
Y se deshabilita automáticamente
|
|
```
|
|
|
|
**Escenario 3: Crear alerta "price below"**
|
|
```gherkin
|
|
DADO que el usuario crea alerta
|
|
CUANDO selecciona "Price goes BELOW $3,700" para ETHUSDT
|
|
Y el precio actual es $3,845
|
|
ENTONCES se crea alerta activa
|
|
Y se activa solo cuando el precio baje a $3,700 o menos
|
|
```
|
|
|
|
**Escenario 4: Alerta "crosses" bidireccional**
|
|
```gherkin
|
|
DADO que el usuario crea alerta "CROSSES $150" para SOLUSDT
|
|
CUANDO el precio cruza $150 desde arriba (150.10 → 149.90)
|
|
O desde abajo (149.90 → 150.10)
|
|
ENTONCES se dispara la alerta
|
|
Y notifica al usuario
|
|
```
|
|
|
|
**Escenario 5: Deshabilitar alerta temporalmente**
|
|
```gherkin
|
|
DADO que el usuario tiene alerta activa
|
|
CUANDO hace click en el toggle [🔔 Enabled]
|
|
ENTONCES cambia a [🔕 Disabled]
|
|
Y el sistema deja de monitorear esa alerta
|
|
Y puede re-habilitarla más tarde
|
|
```
|
|
|
|
**Escenario 6: Alerta con expiración**
|
|
```gherkin
|
|
DADO que el usuario crea alerta
|
|
CUANDO selecciona "Expires After: 24 hours"
|
|
Y la alerta no se dispara en 24 horas
|
|
ENTONCES la alerta se elimina automáticamente
|
|
Y se muestra en historial como "Expired"
|
|
```
|
|
|
|
**Escenario 7: Límite de alertas**
|
|
```gherkin
|
|
DADO que el usuario tiene 10 alertas activas (límite)
|
|
CUANDO intenta crear otra
|
|
ENTONCES se muestra error "Maximum 10 active alerts"
|
|
Y sugiere deshabilitar o eliminar alertas existentes
|
|
```
|
|
|
|
## Criterios Adicionales
|
|
|
|
- [ ] Click en alerta abre chart del símbolo
|
|
- [ ] Sonido diferenciado para alertas
|
|
- [ ] Historial de alertas disparadas
|
|
- [ ] Plantillas de alertas (niveles psicológicos, ATH, etc.)
|
|
- [ ] Re-activar alerta después de dispararse
|
|
|
|
---
|
|
|
|
## Tareas Técnicas
|
|
|
|
**Database:**
|
|
- [ ] DB-TRD-021: Crear tabla trading.price_alerts
|
|
- Campos: id, user_id, symbol, condition, target_price, notification_methods, message, expires_at, status, triggered_at
|
|
- [ ] DB-TRD-022: Crear índice en (user_id, status, symbol)
|
|
|
|
**Backend:**
|
|
- [ ] BE-TRD-070: Crear endpoint POST /trading/alerts
|
|
- [ ] BE-TRD-071: Crear endpoint GET /trading/alerts
|
|
- [ ] BE-TRD-072: Crear endpoint PATCH /trading/alerts/:id
|
|
- [ ] BE-TRD-073: Crear endpoint DELETE /trading/alerts/:id
|
|
- [ ] BE-TRD-074: Implementar AlertService.create()
|
|
- [ ] BE-TRD-075: Implementar AlertMonitorService (background job)
|
|
- [ ] BE-TRD-076: Implementar NotificationService (push, email)
|
|
- [ ] BE-TRD-077: Implementar lógica de condiciones (above, below, crosses)
|
|
|
|
**Frontend:**
|
|
- [ ] FE-TRD-070: Crear componente PriceAlertsPanel.tsx
|
|
- [ ] FE-TRD-071: Crear componente CreateAlertDialog.tsx
|
|
- [ ] FE-TRD-072: Crear componente AlertCard.tsx
|
|
- [ ] FE-TRD-073: Crear componente AlertHistory.tsx
|
|
- [ ] FE-TRD-074: Implementar hook useAlerts
|
|
- [ ] FE-TRD-075: Implementar notificaciones push (Web Push API)
|
|
|
|
**Tests:**
|
|
- [ ] TEST-TRD-034: Test unitario condiciones de alertas
|
|
- [ ] TEST-TRD-035: Test integración crear/disparar alerta
|
|
- [ ] TEST-TRD-036: Test E2E flujo completo alertas
|
|
|
|
---
|
|
|
|
## Dependencias
|
|
|
|
**Depende de:**
|
|
- [ ] US-TRD-001: Ver chart - Estado: Pendiente (necesita precios)
|
|
|
|
**Bloquea:**
|
|
- Ninguna
|
|
|
|
---
|
|
|
|
## Notas Técnicas
|
|
|
|
**Endpoints involucrados:**
|
|
| Método | Endpoint | Descripción |
|
|
|--------|----------|-------------|
|
|
| POST | /trading/alerts | Crear alerta |
|
|
| GET | /trading/alerts | Listar alertas |
|
|
| PATCH | /trading/alerts/:id | Actualizar/deshabilitar alerta |
|
|
| DELETE | /trading/alerts/:id | Eliminar alerta |
|
|
| GET | /trading/alerts/history | Historial de alertas disparadas |
|
|
|
|
**Entidades/Tablas:**
|
|
```sql
|
|
CREATE TABLE trading.price_alerts (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
|
symbol VARCHAR(20) NOT NULL,
|
|
condition VARCHAR(20) NOT NULL, -- 'above', 'below', 'crosses'
|
|
target_price DECIMAL(20, 8) NOT NULL,
|
|
notification_methods JSONB DEFAULT '{"push": true, "email": true}',
|
|
message TEXT,
|
|
expires_at TIMESTAMP,
|
|
status VARCHAR(20) DEFAULT 'active', -- 'active', 'disabled', 'triggered', 'expired'
|
|
triggered_at TIMESTAMP,
|
|
triggered_price DECIMAL(20, 8),
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
updated_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_price_alerts_user_status
|
|
ON trading.price_alerts(user_id, status, symbol);
|
|
|
|
CREATE INDEX idx_price_alerts_active
|
|
ON trading.price_alerts(status, symbol)
|
|
WHERE status = 'active';
|
|
```
|
|
|
|
**Componentes UI:**
|
|
- `PriceAlertsPanel`: Panel principal
|
|
- `CreateAlertDialog`: Modal de creación
|
|
- `AlertCard`: Card de alerta individual
|
|
- `AlertHistory`: Historial de disparadas
|
|
- `ConditionSelector`: Selector de condición
|
|
|
|
**Request Body (Create):**
|
|
```typescript
|
|
{
|
|
symbol: "BTCUSDT",
|
|
condition: "above",
|
|
targetPrice: 100000.00,
|
|
notificationMethods: {
|
|
push: true,
|
|
email: true,
|
|
sms: false
|
|
},
|
|
message: "BTC hitting resistance",
|
|
expiresAt: "2025-12-06T10:00:00Z" // null para never
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
```typescript
|
|
{
|
|
alert: {
|
|
id: "uuid",
|
|
symbol: "BTCUSDT",
|
|
condition: "above",
|
|
targetPrice: 100000.00,
|
|
currentPrice: 97234.50,
|
|
distance: 2.84,
|
|
distancePercentage: 2.84,
|
|
notificationMethods: {
|
|
push: true,
|
|
email: true,
|
|
sms: false
|
|
},
|
|
message: "BTC hitting resistance",
|
|
expiresAt: "2025-12-06T10:00:00Z",
|
|
status: "active",
|
|
createdAt: "2025-12-05T10:00:00Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Alert Monitor Logic (Background Job - cada 5 segundos):**
|
|
```typescript
|
|
const activeAlerts = await getActiveAlerts();
|
|
|
|
for (const alert of activeAlerts) {
|
|
const currentPrice = await getCurrentPrice(alert.symbol);
|
|
const previousPrice = await getPreviousPrice(alert.symbol);
|
|
|
|
let shouldTrigger = false;
|
|
|
|
switch (alert.condition) {
|
|
case 'above':
|
|
shouldTrigger = currentPrice >= alert.targetPrice;
|
|
break;
|
|
|
|
case 'below':
|
|
shouldTrigger = currentPrice <= alert.targetPrice;
|
|
break;
|
|
|
|
case 'crosses':
|
|
// Cruce desde arriba o desde abajo
|
|
const crossedFromAbove = previousPrice > alert.targetPrice && currentPrice <= alert.targetPrice;
|
|
const crossedFromBelow = previousPrice < alert.targetPrice && currentPrice >= alert.targetPrice;
|
|
shouldTrigger = crossedFromAbove || crossedFromBelow;
|
|
break;
|
|
}
|
|
|
|
if (shouldTrigger) {
|
|
await triggerAlert(alert, currentPrice);
|
|
}
|
|
|
|
// Check expiration
|
|
if (alert.expiresAt && new Date() > alert.expiresAt) {
|
|
await expireAlert(alert.id);
|
|
}
|
|
}
|
|
```
|
|
|
|
**Trigger Alert Logic:**
|
|
```typescript
|
|
async function triggerAlert(alert, price) {
|
|
// Update alert status
|
|
await updateAlert(alert.id, {
|
|
status: 'triggered',
|
|
triggeredAt: new Date(),
|
|
triggeredPrice: price
|
|
});
|
|
|
|
// Send notifications
|
|
if (alert.notificationMethods.push) {
|
|
await sendPushNotification(alert.userId, {
|
|
title: `Price Alert: ${alert.symbol}`,
|
|
body: `${alert.symbol} ${alert.condition} ${alert.targetPrice}. Current: ${price}`,
|
|
data: { alertId: alert.id, symbol: alert.symbol }
|
|
});
|
|
}
|
|
|
|
if (alert.notificationMethods.email) {
|
|
await sendEmail(alert.userId, {
|
|
subject: `Price Alert: ${alert.symbol}`,
|
|
body: renderAlertEmail(alert, price)
|
|
});
|
|
}
|
|
|
|
if (alert.notificationMethods.sms) {
|
|
await sendSMS(alert.userId, `${alert.symbol} reached ${price}`);
|
|
}
|
|
}
|
|
```
|
|
|
|
**Condiciones disponibles:**
|
|
- **above**: Se dispara cuando `currentPrice >= targetPrice`
|
|
- **below**: Se dispara cuando `currentPrice <= targetPrice`
|
|
- **crosses**: Se dispara cuando el precio cruza el nivel en cualquier dirección
|
|
|
|
**Expiration options:**
|
|
- Never
|
|
- 1 hour
|
|
- 24 hours
|
|
- 7 days
|
|
- Custom date/time
|
|
|
|
**Notification Methods:**
|
|
- Push Notification (Web Push API)
|
|
- Email
|
|
- SMS (Premium feature)
|
|
|
|
---
|
|
|
|
## Definition of Ready (DoR)
|
|
|
|
- [x] Historia claramente escrita
|
|
- [x] Criterios de aceptación definidos
|
|
- [x] Story points estimados
|
|
- [x] Dependencias identificadas
|
|
- [x] Sin bloqueadores
|
|
- [ ] Diseño/mockup disponible
|
|
- [ ] API spec disponible
|
|
|
|
## Definition of Done (DoD)
|
|
|
|
- [ ] Código implementado según criterios
|
|
- [ ] Tests unitarios escritos y pasando
|
|
- [ ] Tests de integración pasando
|
|
- [ ] Code review aprobado
|
|
- [ ] Documentación actualizada
|
|
- [ ] QA aprobado
|
|
- [ ] Desplegado en ambiente de pruebas
|
|
|
|
---
|
|
|
|
## Historial de Cambios
|
|
|
|
| Fecha | Cambio | Autor |
|
|
|-------|--------|-------|
|
|
| 2025-12-05 | Creación | Requirements-Analyst |
|
|
|
|
---
|
|
|
|
**Creada por:** Requirements-Analyst
|
|
**Fecha:** 2025-12-05
|
|
**Última actualización:** 2025-12-05
|