---
id: "US-TRD-018"
title: "Comparar Multiples Simbolos en el Chart"
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-018: Comparar Múltiples Símbolos en el Chart

## Metadata

| Campo | Valor |
|-------|-------|
| **ID** | US-TRD-018 |
| **Épica** | OQI-003 - Trading y Charts |
| **Módulo** | trading |
| **Prioridad** | P2 |
| **Story Points** | 5 |
| **Sprint** | Sprint 7 |
| **Estado** | Pendiente |
| **Asignado a** | Por asignar |

---

## Historia de Usuario

**Como** trader avanzado,
**quiero** comparar múltiples símbolos en el mismo chart,
**para** analizar correlaciones, divergencias y movimientos relativos entre diferentes activos.

## Descripción Detallada

El usuario debe poder superponer múltiples símbolos en el mismo chart para comparación visual. Los precios se normalizan a un índice base (100) para permitir comparación de activos con diferentes escalas de precio. Cada símbolo tiene un color distintivo y se puede mostrar/ocultar individualmente.

## Mockups/Wireframes

```
┌─────────────────────────────────────────────────────────────────┐
│  COMPARE MODE                                         [Exit]    │
├─────────────────────────────────────────────────────────────────┤
│  [+ Add Symbol to Compare]                                      │
│                                                                  │
│  Active Comparisons:                                            │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │ ● BTCUSDT   $97,234.50  +2.34%  [────] [x]             │  │
│  │ ● ETHUSDT   $3,845.20   -0.45%  [────] [x]             │  │
│  │ ● SOLUSDT   $142.73     +1.56%  [────] [x]             │  │
│  └──────────────────────────────────────────────────────────┘  │
│                                                                  │
│  View Mode: [Normalized ▼]                                      │
│  Options: Normalized (Index), Absolute Price, % Change          │
│                                                                  │
│  Base Date: [2025-12-01] (Index = 100 at this date)            │
│                                                                  │
├─────────────────────────────────────────────────────────────────┤
│  NORMALIZED VIEW (Index Base = 100)                            │
│                                                                  │
│  120 ┤                                    ──── BTC (Blue)       │
│  115 ┤                           ────────                       │
│  110 ┤                  ────────                                │
│  105 ┤         ════════                    ════ ETH (Orange)    │
│  100 ┤────────                                                  │
│   95 ┤    ····························     ···· SOL (Green)     │
│   90 ┤                                                          │
│                                                                  │
│  Legend:                                                        │
│  ──── BTCUSDT (+15.2% from base)                               │
│  ════ ETHUSDT (+8.7% from base)                                │
│  ···· SOLUSDT (-3.4% from base)                                │
│                                                                  │
│  Correlation Matrix:                                            │
│  ┌─────────────────────────────────────┐                       │
│  │        BTC    ETH    SOL            │                       │
│  │ BTC    1.00   0.87   0.65           │                       │
│  │ ETH    0.87   1.00   0.72           │                       │
│  │ SOL    0.65   0.72   1.00           │                       │
│  └─────────────────────────────────────┘                       │
└─────────────────────────────────────────────────────────────────┘

ABSOLUTE PRICE VIEW:
┌─────────────────────────────────────────┐
│  Dual Y-Axis Mode                       │
├─────────────────────────────────────────┤
│  $100K ┤                    ──── BTC    │
│  $95K  ┤            ────────            │
│  $90K  ┤  ──────────                    │
│        │                                 │
│  $4K   ┤            ════════ ETH         │
│  $3.8K ┤  ════════                       │
│  $3.6K ┤                                 │
│        │                                 │
│  $150  ┤  ····························  │
│  $140  ┤            ····· SOL            │
│  $130  ┤  ·····                          │
└─────────────────────────────────────────┘
```

---

## Criterios de Aceptación

**Escenario 1: Activar modo comparación**
```gherkin
DADO que el usuario está viendo chart de BTCUSDT
CUANDO hace click en botón "Compare"
ENTONCES el chart entra en modo comparación
Y BTCUSDT se convierte en el símbolo base
Y se muestra botón "+ Add Symbol to Compare"
Y se muestra la lista de símbolos activos
```

**Escenario 2: Agregar símbolo a comparación**
```gherkin
DADO que el usuario está en modo comparación con BTCUSDT
CUANDO hace click en "+ Add Symbol to Compare"
Y busca "ETH"
Y selecciona "ETHUSDT"
ENTONCES ETHUSDT se agrega al chart
Y se muestra con línea de color diferente (naranja)
Y aparece en la lista de símbolos activos
Y ambos símbolos están visibles simultáneamente
```

**Escenario 3: Ver modo normalizado (índice base 100)**
```gherkin
DADO que hay 3 símbolos en comparación
Y el modo es "Normalized"
Y la fecha base es 2025-12-01
ENTONCES todos los símbolos comienzan en índice 100 en la fecha base
Y los valores subsecuentes muestran cambio relativo al base
Ejemplo:
  - BTC: $90K → $100K = Índice 100 → 111.1 (+11.1%)
  - ETH: $3.6K → $3.9K = Índice 100 → 108.3 (+8.3%)
Y se pueden comparar visualmente los rendimientos
```

**Escenario 4: Ver modo precio absoluto**
```gherkin
DADO que el usuario selecciona "Absolute Price"
ENTONCES cada símbolo muestra su precio real
Y se usan ejes Y múltiples (uno por símbolo)
Y los ejes Y están a la derecha con colores matching
Y el chart permite ver movimientos absolutos
```

**Escenario 5: Ver modo % de cambio**
```gherkin
DADO que el usuario selecciona "% Change"
Y la fecha base es 2025-12-01
ENTONCES todos los símbolos muestran % de cambio desde el base
Y el eje Y muestra -10%, 0%, +10%, +20%, etc.
Y las líneas cruzan en 0% en la fecha base
```

**Escenario 6: Ocultar/mostrar símbolo**
```gherkin
DADO que hay 3 símbolos en comparación
CUANDO el usuario hace click en el color de ETHUSDT
ENTONCES la línea de ETHUSDT se oculta
Y sigue en la lista pero con opacidad reducida
Y puede volver a hacer click para mostrarla
```

**Escenario 7: Eliminar símbolo de comparación**
```gherkin
DADO que SOLUSDT está en comparación
CUANDO el usuario hace click en [x] junto a SOLUSDT
ENTONCES SOLUSDT se elimina del chart
Y desaparece de la lista
Y su línea desaparece del gráfico
```

**Escenario 8: Ver matriz de correlación**
```gherkin
DADO que hay múltiples símbolos en comparación
CUANDO se calcula la correlación
ENTONCES se muestra matriz con coeficientes de correlación
Y valores van de -1.0 (correlación negativa) a +1.0 (positiva)
Y diagonal siempre es 1.00 (símbolo consigo mismo)
Ejemplo: BTC vs ETH = 0.87 (alta correlación positiva)
```

**Escenario 9: Cambiar fecha base**
```gherkin
DADO que el usuario está en modo normalizado
CUANDO cambia la fecha base a 2025-11-15
ENTONCES todos los índices se recalculan
Y el índice 100 ahora está en 2025-11-15
Y los cambios relativos se ajustan
```

**Escenario 10: Límite de símbolos**
```gherkin
DADO que el usuario tiene 5 símbolos en comparación (límite)
CUANDO intenta agregar un sexto
ENTONCES se muestra mensaje "Maximum 5 symbols for comparison"
Y debe eliminar uno para agregar otro
```

## Criterios Adicionales

- [ ] Colores distintivos automáticos para cada símbolo
- [ ] Tooltip sincronizado mostrando todos los valores
- [ ] Exportar comparación como imagen
- [ ] Guardar configuración de comparación
- [ ] Templates de comparación (ej: "Major Crypto", "DeFi Tokens")

---

## Tareas Técnicas

**Database:**
- [ ] DB-TRD-026: Crear tabla trading.comparison_presets
  - Campos: id, user_id, name, symbols, view_mode, base_date

**Backend:**
- [ ] BE-TRD-096: Crear endpoint GET /trading/candles/multi
- [ ] BE-TRD-097: Implementar normalización de precios
- [ ] BE-TRD-098: Implementar cálculo de correlación
- [ ] BE-TRD-099: Optimizar queries para múltiples símbolos
- [ ] BE-TRD-100: Crear endpoint para guardar comparación

**Frontend:**
- [ ] FE-TRD-102: Crear componente CompareMode.tsx
- [ ] FE-TRD-103: Crear componente SymbolSelector.tsx
- [ ] FE-TRD-104: Crear componente SymbolsList.tsx
- [ ] FE-TRD-105: Crear componente CorrelationMatrix.tsx
- [ ] FE-TRD-106: Implementar normalización en frontend
- [ ] FE-TRD-107: Implementar múltiples series en Lightweight Charts
- [ ] FE-TRD-108: Implementar tooltip sincronizado
- [ ] FE-TRD-109: Implementar hook useCompare

**Tests:**
- [ ] TEST-TRD-049: Test unitario normalización
- [ ] TEST-TRD-050: Test unitario correlación
- [ ] TEST-TRD-051: Test integración múltiples símbolos
- [ ] TEST-TRD-052: Test E2E modo comparación completo

---

## Dependencias

**Depende de:**
- [ ] US-TRD-001: Ver chart - Estado: Pendiente

**Bloquea:**
- Ninguna

---

## Notas Técnicas

**Endpoints involucrados:**
| Método | Endpoint | Descripción |
|--------|----------|-------------|
| GET | /trading/candles/multi | Obtener velas de múltiples símbolos |
| POST | /trading/compare/correlation | Calcular correlación |
| POST | /trading/compare/presets | Guardar preset de comparación |

**Componentes UI:**
- `CompareMode`: Modo de comparación principal
- `SymbolSelector`: Selector de símbolos
- `SymbolsList`: Lista de símbolos activos
- `CorrelationMatrix`: Matriz de correlaciones
- `ViewModeSelector`: Selector de modo de vista

**Query Parameters (Multi Candles):**
```typescript
{
  symbols: ["BTCUSDT", "ETHUSDT", "SOLUSDT"],
  interval: "1h",
  from: "2025-11-01",
  to: "2025-12-05"
}
```

**Response (Multi Candles):**
```typescript
{
  data: {
    "BTCUSDT": [
      { time: 1699027200, open: 90000, high: 91000, low: 89500, close: 90500, volume: 1234 },
      // ... más velas
    ],
    "ETHUSDT": [
      { time: 1699027200, open: 3600, high: 3650, low: 3580, close: 3620, volume: 5678 },
      // ... más velas
    ],
    "SOLUSDT": [
      { time: 1699027200, open: 140, high: 145, low: 138, close: 142, volume: 9012 },
      // ... más velas
    ]
  }
}
```

**Normalization Logic:**
```typescript
function normalizeToIndex(candles: Candle[], baseDate: Date) {
  const baseCandle = candles.find(c => c.time === baseDate.getTime() / 1000);
  if (!baseCandle) return candles;

  const basePrice = baseCandle.close;

  return candles.map(candle => ({
    ...candle,
    indexValue: (candle.close / basePrice) * 100,
    percentChange: ((candle.close - basePrice) / basePrice) * 100
  }));
}
```

**Correlation Calculation:**
```typescript
function calculateCorrelation(series1: number[], series2: number[]): number {
  const n = Math.min(series1.length, series2.length);

  // Calculate means
  const mean1 = series1.reduce((a, b) => a + b, 0) / n;
  const mean2 = series2.reduce((a, b) => a + b, 0) / n;

  // Calculate correlation coefficient
  let numerator = 0;
  let sum1Sq = 0;
  let sum2Sq = 0;

  for (let i = 0; i < n; i++) {
    const diff1 = series1[i] - mean1;
    const diff2 = series2[i] - mean2;

    numerator += diff1 * diff2;
    sum1Sq += diff1 * diff1;
    sum2Sq += diff2 * diff2;
  }

  const denominator = Math.sqrt(sum1Sq * sum2Sq);

  return denominator === 0 ? 0 : numerator / denominator;
}

function calculateCorrelationMatrix(symbols: string[], data: CandleData) {
  const matrix: { [key: string]: { [key: string]: number } } = {};

  symbols.forEach(symbol1 => {
    matrix[symbol1] = {};
    symbols.forEach(symbol2 => {
      if (symbol1 === symbol2) {
        matrix[symbol1][symbol2] = 1.0;
      } else {
        const series1 = data[symbol1].map(c => c.close);
        const series2 = data[symbol2].map(c => c.close);
        matrix[symbol1][symbol2] = calculateCorrelation(series1, series2);
      }
    });
  });

  return matrix;
}
```

**Chart Implementation (Lightweight Charts):**
```typescript
// Add multiple series
const colors = ['#2962FF', '#FF6D00', '#26A69A', '#9C27B0', '#F44336'];

symbols.forEach((symbol, index) => {
  const series = chart.addLineSeries({
    color: colors[index],
    lineWidth: 2,
    title: symbol,
    priceScaleId: viewMode === 'absolute' ? `scale-${index}` : 'right',
  });

  const normalizedData = normalizeToIndex(data[symbol], baseDate);
  series.setData(normalizedData.map(candle => ({
    time: candle.time,
    value: viewMode === 'normalized' ? candle.indexValue : candle.close
  })));

  symbolSeries[symbol] = series;
});

// Configure price scales for absolute mode
if (viewMode === 'absolute') {
  symbols.forEach((symbol, index) => {
    chart.priceScale(`scale-${index}`).applyOptions({
      scaleMargins: {
        top: 0.1 + (index * 0.3),
        bottom: 0.7 - (index * 0.3),
      },
      borderColor: colors[index],
    });
  });
}
```

**Synchronized Crosshair:**
```typescript
function setupSyncedCrosshair(chart, symbols, data) {
  chart.subscribeCrosshairMove((param) => {
    if (!param.time) {
      tooltip.style.display = 'none';
      return;
    }

    const tooltipContent = symbols.map(symbol => {
      const price = param.seriesPrices.get(symbolSeries[symbol]);
      return `${symbol}: ${price?.toFixed(2) || 'N/A'}`;
    }).join('\n');

    tooltip.textContent = tooltipContent;
    tooltip.style.display = 'block';
  });
}
```

**Color Palette:**
```typescript
const SYMBOL_COLORS = [
  '#2962FF', // Blue
  '#FF6D00', // Orange
  '#26A69A', // Teal
  '#9C27B0', // Purple
  '#F44336'  // Red
];
```

---

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