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>
16 KiB
| id | title | type | status | priority | epic | story_points | created_date | updated_date |
|---|---|---|---|---|---|---|---|---|
| US-TRD-018 | Comparar Multiples Simbolos en el Chart | User Story | Done | Media | OQI-003 | 5 | 2025-12-05 | 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
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
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)
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
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
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
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
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
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
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
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 principalSymbolSelector: Selector de símbolosSymbolsList: Lista de símbolos activosCorrelationMatrix: Matriz de correlacionesViewModeSelector: Selector de modo de vista
Query Parameters (Multi Candles):
{
symbols: ["BTCUSDT", "ETHUSDT", "SOLUSDT"],
interval: "1h",
from: "2025-11-01",
to: "2025-12-05"
}
Response (Multi Candles):
{
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:
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:
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):
// 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:
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:
const SYMBOL_COLORS = [
'#2962FF', // Blue
'#FF6D00', // Orange
'#26A69A', // Teal
'#9C27B0', // Purple
'#F44336' // Red
];
Definition of Ready (DoR)
- Historia claramente escrita
- Criterios de aceptación definidos
- Story points estimados
- Dependencias identificadas
- 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