---
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
