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>
15 KiB
| id | title | type | status | priority | epic | story_points | created_date | updated_date |
|---|---|---|---|---|---|---|---|---|
| US-TRD-015 | Exportar Historial de Trades a CSV | User Story | Done | Media | OQI-003 | 3 | 2025-12-05 | 2026-01-04 |
US-TRD-015: Exportar Historial de Trades a CSV
Metadata
| Campo | Valor |
|---|---|
| ID | US-TRD-015 |
| É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 trader practicante, quiero exportar mi historial de trades a formato CSV, para analizarlo en Excel u otras herramientas externas y llevar un registro personal.
Descripción Detallada
El usuario debe poder descargar su historial completo de trades (o filtrado) en formato CSV, incluyendo todos los detalles relevantes: fechas, símbolos, precios, P&L, duración, etc. Esto permite análisis externo y mantener registros personales.
Mockups/Wireframes
┌─────────────────────────────────────────────────────────────────┐
│ TRADE HISTORY │
├─────────────────────────────────────────────────────────────────┤
│ Filters: │
│ Date Range: [Last 30 days ▼] Symbol: [All ▼] Side: [All ▼] │
│ Result: [All ▼] [🔍 Search] [📥 Export CSV] │
│ └─────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ EXPORT TO CSV │ │
│ ├────────────────────────────────────────────────────────────┤ │
│ │ │ │
│ │ Export Options: │ │
│ │ │ │
│ │ Date Range: │ │
│ │ ○ Current filter (Last 30 days - 45 trades) │ │
│ │ ○ Custom range │ │
│ │ From: [2025-11-01] To: [2025-12-05] │ │
│ │ ○ All time (127 trades) │ │
│ │ │ │
│ │ Include Columns: │ │
│ │ [✓] Trade ID │ │
│ │ [✓] Symbol │ │
│ │ [✓] Side (Long/Short) │ │
│ │ [✓] Entry Price │ │
│ │ [✓] Exit Price │ │
│ │ [✓] Quantity │ │
│ │ [✓] P&L (USD) │ │
│ │ [✓] P&L (%) │ │
│ │ [✓] Opened At │ │
│ │ [✓] Closed At │ │
│ │ [✓] Duration │ │
│ │ [✓] Close Reason │ │
│ │ [ ] Entry Order ID │ │
│ │ [ ] Exit Order ID │ │
│ │ │ │
│ │ Format: │ │
│ │ ○ CSV (Comma-separated) │ │
│ │ ○ CSV (Semicolon-separated - Excel Europe) │ │
│ │ ○ TSV (Tab-separated) │ │
│ │ │ │
│ │ File name: │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ trades_2025-11-05_to_2025-12-05.csv │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ Estimated file size: ~15 KB (45 trades) │ │
│ │ │ │
│ │ [Cancel] [Download CSV] │ │
│ └────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Criterios de Aceptación
Escenario 1: Exportar con filtros actuales
DADO que el usuario tiene filtro "Last 30 days" activo
Y hay 45 trades en ese rango
CUANDO hace click en "Export CSV"
Y selecciona "Current filter"
Y hace click en "Download CSV"
ENTONCES se descarga archivo trades_2025-11-05_to_2025-12-05.csv
Y contiene 45 trades + 1 línea de headers
Y las columnas corresponden a las seleccionadas
Escenario 2: Exportar rango personalizado
DADO que el usuario abre el diálogo de export
CUANDO selecciona "Custom range"
Y elige From: 2025-10-01, To: 2025-10-31
Y hay 23 trades en octubre
Y hace click en "Download CSV"
ENTONCES se descarga archivo con 23 trades de octubre
Y el nombre refleja las fechas: trades_2025-10-01_to_2025-10-31.csv
Escenario 3: Exportar todo el historial
DADO que el usuario tiene 127 trades en total
CUANDO selecciona "All time"
Y descarga
ENTONCES se exportan los 127 trades
Y el archivo se llama trades_all_time.csv
Escenario 4: Seleccionar columnas específicas
DADO que el usuario abre opciones de export
CUANDO desmarca "Entry Order ID" y "Exit Order ID"
Y solo deja las columnas principales marcadas
Y descarga
ENTONCES el CSV contiene solo las columnas seleccionadas
Y no incluye las columnas desmarcadas
Escenario 5: Formato separador para Excel Europa
DADO que el usuario usa Excel con configuración europea
CUANDO selecciona formato "CSV (Semicolon-separated)"
Y descarga
ENTONCES el archivo usa punto y coma como separador
Y los decimales usan coma (ej: 123,45 en lugar de 123.45)
Escenario 6: Validar contenido del CSV
DADO que se exporta un archivo CSV
CUANDO se abre el archivo
ENTONCES contiene:
- Primera línea: Headers de columnas
- Líneas siguientes: Datos de trades
- Formato de fechas: YYYY-MM-DD HH:MM:SS
- Formato de precios: Con 2 decimales
- Formato de P&L: Con signo +/- y 2 decimales
- Sin errores de encoding (UTF-8)
Criterios Adicionales
- Máximo 1000 trades por exportación
- Progreso de descarga para archivos grandes
- Opción de incluir resumen estadístico al final
- Exportar también a JSON (futuro)
- Exportar también a Excel XLSX (futuro)
Tareas Técnicas
Database:
- No requiere cambios en DB
Backend:
- BE-TRD-086: Crear endpoint GET /trading/paper/trades/export/csv
- BE-TRD-087: Implementar ExportService.generateCSV()
- BE-TRD-088: Implementar diferentes formatos (comma, semicolon, tab)
- BE-TRD-089: Implementar conversión de decimales según formato
- BE-TRD-090: Optimizar queries para exportaciones grandes
- BE-TRD-091: Implementar streaming para archivos grandes
Frontend:
- FE-TRD-081: Crear componente ExportCSVDialog.tsx
- FE-TRD-082: Crear componente ColumnSelector.tsx
- FE-TRD-083: Crear componente FormatSelector.tsx
- FE-TRD-084: Implementar hook useExportTrades
- FE-TRD-085: Implementar descarga de archivo blob
Tests:
- TEST-TRD-040: Test unitario generación CSV
- TEST-TRD-041: Test integración export endpoint
- TEST-TRD-042: Test E2E descarga archivo
Dependencias
Depende de:
- US-TRD-010: Ver historial - Estado: Pendiente
Bloquea:
- Ninguna
Notas Técnicas
Endpoints involucrados:
| Método | Endpoint | Descripción |
|---|---|---|
| GET | /trading/paper/trades/export/csv | Exportar CSV |
| GET | /trading/paper/trades/export/json | Exportar JSON (futuro) |
Componentes UI:
ExportCSVDialog: Modal de opciones de exportColumnSelector: Checklist de columnasFormatSelector: Radio buttons de formatoDateRangePicker: Selector de rango
Query Parameters:
{
dateFrom: "2025-11-05",
dateTo: "2025-12-05",
symbol: "BTCUSDT", // opcional
side: "long", // opcional
result: "win", // opcional
columns: ["id", "symbol", "side", "entry_price", "exit_price", "quantity", "pnl", "pnl_percentage", "opened_at", "closed_at", "duration", "close_reason"],
format: "csv", // csv, csv-semicolon, tsv
locale: "en-US" // en-US, es-ES, etc.
}
CSV Headers:
Trade ID,Symbol,Side,Entry Price,Exit Price,Quantity,P&L (USD),P&L (%),Opened At,Closed At,Duration,Close Reason
CSV Example (Comma-separated, en-US):
Trade ID,Symbol,Side,Entry Price,Exit Price,Quantity,P&L (USD),P&L (%),Opened At,Closed At,Duration,Close Reason
550e8400-e29b-41d4-a716-446655440000,BTCUSDT,long,95000.00,97234.50,0.10,+223.45,+2.35,2025-12-05 08:00:00,2025-12-05 10:30:00,2h 30m,manual
660e8400-e29b-41d4-a716-446655440001,ETHUSDT,long,3800.00,3750.00,2.50,-125.00,-1.32,2025-12-04 14:15:00,2025-12-04 19:35:00,5h 20m,stop_loss
770e8400-e29b-41d4-a716-446655440002,SOLUSDT,short,145.00,142.73,10.00,+22.70,+1.56,2025-12-03 09:00:00,2025-12-04 12:45:00,1d 3h 45m,take_profit
CSV Example (Semicolon-separated, es-ES):
Trade ID;Symbol;Side;Entry Price;Exit Price;Quantity;P&L (USD);P&L (%);Opened At;Closed At;Duration;Close Reason
550e8400-e29b-41d4-a716-446655440000;BTCUSDT;long;95000,00;97234,50;0,10;+223,45;+2,35;2025-12-05 08:00:00;2025-12-05 10:30:00;2h 30m;manual
Backend Implementation:
async function generateCSV(params) {
const {
dateFrom,
dateTo,
columns,
format,
locale
} = params;
// Obtener trades
const trades = await getTradesForExport(params);
// Configuración según formato
const config = {
delimiter: format === 'csv-semicolon' ? ';' : (format === 'tsv' ? '\t' : ','),
decimalSeparator: locale.startsWith('es') ? ',' : '.',
dateFormat: 'YYYY-MM-DD HH:mm:ss'
};
// Headers
const headers = columns.map(col => COLUMN_LABELS[col]);
let csv = headers.join(config.delimiter) + '\n';
// Data rows
for (const trade of trades) {
const row = columns.map(col => {
const value = formatValue(trade[col], col, config);
return escapeCSV(value, config.delimiter);
});
csv += row.join(config.delimiter) + '\n';
}
return csv;
}
function formatValue(value, column, config) {
if (value === null || value === undefined) return '';
switch (column) {
case 'pnl':
case 'pnl_percentage':
const sign = value >= 0 ? '+' : '';
const formatted = value.toFixed(2);
return sign + (config.decimalSeparator === ',' ? formatted.replace('.', ',') : formatted);
case 'entry_price':
case 'exit_price':
return value.toFixed(2).replace('.', config.decimalSeparator);
case 'opened_at':
case 'closed_at':
return moment(value).format(config.dateFormat);
case 'duration':
return formatDuration(value);
default:
return String(value);
}
}
function escapeCSV(value, delimiter) {
const str = String(value);
if (str.includes(delimiter) || str.includes('"') || str.includes('\n')) {
return '"' + str.replace(/"/g, '""') + '"';
}
return str;
}
Frontend Download:
async function downloadCSV(params) {
// Request CSV from backend
const response = await fetch('/trading/paper/trades/export/csv?' + new URLSearchParams(params));
const blob = await response.blob();
// Create download link
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = generateFilename(params);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
}
function generateFilename(params) {
const { dateFrom, dateTo } = params;
if (dateFrom && dateTo) {
return `trades_${dateFrom}_to_${dateTo}.csv`;
}
return `trades_all_time.csv`;
}
Column Labels:
const COLUMN_LABELS = {
id: 'Trade ID',
symbol: 'Symbol',
side: 'Side',
entry_price: 'Entry Price',
exit_price: 'Exit Price',
quantity: 'Quantity',
pnl: 'P&L (USD)',
pnl_percentage: 'P&L (%)',
opened_at: 'Opened At',
closed_at: 'Closed At',
duration_seconds: 'Duration',
close_reason: 'Close Reason',
entry_order_id: 'Entry Order ID',
exit_order_id: 'Exit Order ID'
};
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