trading-platform/docs/02-definicion-modulos/OQI-003-trading-charts/historias-usuario/US-TRD-016-modo-oscuro-chart.md
rckrdmrd a7cca885f0 feat: Major platform documentation and architecture updates
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>
2026-01-07 05:33:35 -06:00

13 KiB

id title type status priority epic story_points created_date updated_date
US-TRD-016 Cambiar Tema del Chart User Story Done Baja OQI-003 2 2025-12-05 2026-01-04

US-TRD-016: Cambiar Tema del Chart (Claro/Oscuro)

Metadata

Campo Valor
ID US-TRD-016
Épica OQI-003 - Trading y Charts
Módulo trading
Prioridad P2
Story Points 2
Sprint Sprint 6
Estado Pendiente
Asignado a Por asignar

Historia de Usuario

Como usuario de la plataforma, quiero cambiar entre tema claro y oscuro para el chart de trading, para adaptar la visualización a mis preferencias y reducir fatiga visual en sesiones largas.

Descripción Detallada

El usuario debe poder alternar entre un tema claro y un tema oscuro para el chart de trading. El tema oscuro es especialmente útil para trading nocturno o sesiones prolongadas, reduciendo la fatiga visual. La preferencia debe persistir entre sesiones.

Mockups/Wireframes

TEMA CLARO:
┌─────────────────────────────────────────────────────────────────┐
│  BTCUSDT   $97,234.50   +2.34% ▲                     [☀ Light] │
├─────────────────────────────────────────────────────────────────┤
│  Background: #FFFFFF                                            │
│  Grid: #E0E0E0                                                  │
│  Text: #000000                                                  │
│  Candles Up: #26A69A (Green)                                    │
│  Candles Down: #EF5350 (Red)                                    │
│  Volume: #757575 (Gray)                                         │
│  Crosshair: #000000                                             │
│                                                                  │
│    ████ (Green candles on white background)                     │
│    ████  ████                                                   │
│          ████  ════ (SMA - Blue line on white)                  │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

TEMA OSCURO:
┌─────────────────────────────────────────────────────────────────┐
│  BTCUSDT   $97,234.50   +2.34% ▲                     [🌙 Dark] │
├─────────────────────────────────────────────────────────────────┤
│  Background: #1E222D                                            │
│  Grid: #363C4E                                                  │
│  Text: #D1D4DC                                                  │
│  Candles Up: #26A69A (Green)                                    │
│  Candles Down: #EF5350 (Red)                                    │
│  Volume: #757575 (Gray)                                         │
│  Crosshair: #FFFFFF                                             │
│                                                                  │
│    ████ (Green candles on dark background)                      │
│    ████  ████                                                   │
│          ████  ════ (SMA - Blue line on dark)                   │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

CONTROL DE TEMA:
┌─────────────────────────┐
│ [☀ Light] [🌙 Dark]    │
│   └─Active (Blue)       │
└─────────────────────────┘

Criterios de Aceptación

Escenario 1: Cambiar a tema oscuro

DADO que el usuario está viendo el chart en tema claro
CUANDO hace click en el botón "Dark"
ENTONCES el chart cambia a tema oscuro inmediatamente
Y el fondo cambia a #1E222D
Y el texto cambia a color claro (#D1D4DC)
Y la grid cambia a #363C4E
Y las velas mantienen sus colores (verde/rojo)
Y el botón "Dark" se marca como activo

Escenario 2: Cambiar a tema claro

DADO que el usuario está viendo el chart en tema oscuro
CUANDO hace click en el botón "Light"
ENTONCES el chart cambia a tema claro
Y el fondo cambia a #FFFFFF
Y el texto cambia a color oscuro (#000000)
Y el botón "Light" se marca como activo

Escenario 3: Persistencia de preferencia

DADO que el usuario selecciona tema oscuro
CUANDO cierra sesión y vuelve a iniciar sesión
ENTONCES el chart se carga automáticamente en tema oscuro
Y la preferencia se mantiene

Escenario 4: Sincronización con tema del sistema

DADO que el usuario tiene preferencia "Auto"
CUANDO el sistema operativo está en modo oscuro
ENTONCES el chart usa tema oscuro automáticamente

CUANDO el sistema cambia a modo claro
ENTONCES el chart cambia a tema claro

Escenario 5: Indicadores en ambos temas

DADO que el usuario tiene indicadores SMA y RSI activos
CUANDO cambia entre temas
ENTONCES los indicadores mantienen sus colores distintivos
Y son visibles en ambos temas
Y los colores contrastan correctamente con el fondo

Escenario 6: Keyboard shortcut

DADO que el usuario está en el chart
CUANDO presiona "Ctrl + D" (o "Cmd + D" en Mac)
ENTONCES el tema alterna entre claro y oscuro

Criterios Adicionales

  • Transición suave entre temas (200ms)
  • Aplicar tema también a paneles laterales
  • Opción "Auto" que sigue el tema del sistema
  • Previsualización de temas antes de aplicar
  • Temas personalizados (futuro)

Tareas Técnicas

Database:

  • DB-TRD-025: Añadir campo theme a user_preferences
    • Valores: 'light', 'dark', 'auto'

Backend:

  • BE-TRD-092: Crear endpoint PATCH /users/preferences/theme
  • BE-TRD-093: Implementar UserPreferencesService.updateTheme()

Frontend:

  • FE-TRD-086: Crear componente ThemeToggle.tsx
  • FE-TRD-087: Definir paletas de colores para cada tema
  • FE-TRD-088: Implementar hook useTheme
  • FE-TRD-089: Aplicar tema a Lightweight Charts
  • FE-TRD-090: Implementar detección de tema del sistema
  • FE-TRD-091: Añadir transiciones CSS
  • FE-TRD-092: Implementar keyboard shortcut

Tests:

  • TEST-TRD-043: Test unitario cambio de tema
  • TEST-TRD-044: Test integración persistencia de tema
  • TEST-TRD-045: Test E2E alternancia de temas

Dependencias

Depende de:

  • US-TRD-001: Ver chart - Estado: Pendiente

Bloquea:

  • Ninguna

Notas Técnicas

Endpoints involucrados:

Método Endpoint Descripción
PATCH /users/preferences/theme Actualizar preferencia de tema
GET /users/preferences Obtener preferencias (incluye tema)

Entidades/Tablas:

ALTER TABLE auth.user_preferences
ADD COLUMN theme VARCHAR(10) DEFAULT 'light';
-- Valores: 'light', 'dark', 'auto'

Componentes UI:

  • ThemeToggle: Botones de alternancia de tema
  • ThemeProvider: Context provider de tema

Theme Palettes:

const LIGHT_THEME = {
  chart: {
    background: '#FFFFFF',
    textColor: '#000000',
    gridColor: '#E0E0E0',
    crosshairColor: '#000000',
  },
  candles: {
    upColor: '#26A69A',      // Verde
    downColor: '#EF5350',    // Rojo
    borderUpColor: '#26A69A',
    borderDownColor: '#EF5350',
    wickUpColor: '#26A69A',
    wickDownColor: '#EF5350',
  },
  volume: {
    upColor: 'rgba(38, 166, 154, 0.5)',
    downColor: 'rgba(239, 83, 80, 0.5)',
  },
  indicators: {
    sma: '#2962FF',          // Azul
    ema: '#FF6D00',          // Naranja
    rsi: '#9C27B0',          // Púrpura
    macd: '#00C853',         // Verde oscuro
  },
  ui: {
    background: '#FAFAFA',
    cardBackground: '#FFFFFF',
    border: '#E0E0E0',
    text: '#000000',
    textSecondary: '#757575',
  }
};

const DARK_THEME = {
  chart: {
    background: '#1E222D',
    textColor: '#D1D4DC',
    gridColor: '#363C4E',
    crosshairColor: '#FFFFFF',
  },
  candles: {
    upColor: '#26A69A',
    downColor: '#EF5350',
    borderUpColor: '#26A69A',
    borderDownColor: '#EF5350',
    wickUpColor: '#26A69A',
    wickDownColor: '#EF5350',
  },
  volume: {
    upColor: 'rgba(38, 166, 154, 0.5)',
    downColor: 'rgba(239, 83, 80, 0.5)',
  },
  indicators: {
    sma: '#5E81F4',
    ema: '#FF9800',
    rsi: '#BA68C8',
    macd: '#66BB6A',
  },
  ui: {
    background: '#131722',
    cardBackground: '#1E222D',
    border: '#363C4E',
    text: '#D1D4DC',
    textSecondary: '#898E9C',
  }
};

Request Body:

{
  theme: "dark"  // "light", "dark", "auto"
}

Response:

{
  theme: "dark",
  updatedAt: "2025-12-05T10:00:00Z"
}

Frontend Implementation:

// Theme Context
const ThemeContext = createContext<ThemeContextType>(null);

export function ThemeProvider({ children }) {
  const [theme, setTheme] = useState<'light' | 'dark' | 'auto'>('light');
  const [resolvedTheme, setResolvedTheme] = useState<'light' | 'dark'>('light');

  // Load theme from user preferences
  useEffect(() => {
    loadUserTheme().then(setTheme);
  }, []);

  // Listen to system theme changes
  useEffect(() => {
    if (theme === 'auto') {
      const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
      const updateResolvedTheme = () => {
        setResolvedTheme(mediaQuery.matches ? 'dark' : 'light');
      };

      updateResolvedTheme();
      mediaQuery.addEventListener('change', updateResolvedTheme);
      return () => mediaQuery.removeEventListener('change', updateResolvedTheme);
    } else {
      setResolvedTheme(theme as 'light' | 'dark');
    }
  }, [theme]);

  const toggleTheme = async () => {
    const newTheme = resolvedTheme === 'light' ? 'dark' : 'light';
    setTheme(newTheme);
    await saveUserTheme(newTheme);
  };

  return (
    <ThemeContext.Provider value={{ theme, resolvedTheme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

// Apply theme to chart
function applyChartTheme(chart, theme) {
  const palette = theme === 'dark' ? DARK_THEME : LIGHT_THEME;

  chart.applyOptions({
    layout: {
      background: { color: palette.chart.background },
      textColor: palette.chart.textColor,
    },
    grid: {
      vertLines: { color: palette.chart.gridColor },
      horzLines: { color: palette.chart.gridColor },
    },
    crosshair: {
      vertLine: { color: palette.chart.crosshairColor },
      horzLine: { color: palette.chart.crosshairColor },
    },
  });

  candleSeries.applyOptions({
    upColor: palette.candles.upColor,
    downColor: palette.candles.downColor,
    borderUpColor: palette.candles.borderUpColor,
    borderDownColor: palette.candles.borderDownColor,
    wickUpColor: palette.candles.wickUpColor,
    wickDownColor: palette.candles.wickDownColor,
  });
}

// Keyboard shortcut
useEffect(() => {
  const handleKeyDown = (e: KeyboardEvent) => {
    if ((e.ctrlKey || e.metaKey) && e.key === 'd') {
      e.preventDefault();
      toggleTheme();
    }
  };

  window.addEventListener('keydown', handleKeyDown);
  return () => window.removeEventListener('keydown', handleKeyDown);
}, [toggleTheme]);

CSS Transitions:

.chart-container {
  transition: background-color 200ms ease-in-out;
}

.theme-toggle {
  transition: all 200ms ease-in-out;
}

.theme-toggle.active {
  background-color: #2962FF;
  color: white;
}

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