---
id: "US-TRD-015"
title: "Exportar Historial de Trades a CSV"
type: "User Story"
status: "Done"
priority: "Media"
epic: "OQI-003"
story_points: 3
created_date: "2025-12-05"
updated_date: "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**
```gherkin
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**
```gherkin
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**
```gherkin
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**
```gherkin
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**
```gherkin
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**
```gherkin
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 export
- `ColumnSelector`: Checklist de columnas
- `FormatSelector`: Radio buttons de formato
- `DateRangePicker`: Selector de rango

**Query Parameters:**
```typescript
{
  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:**
```csv
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):**
```csv
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):**
```csv
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:**
```typescript
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:**
```typescript
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:**
```typescript
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)

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