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