# US-TRD-008: Cerrar Posición

## Metadata

| Campo | Valor |
|-------|-------|
| **ID** | US-TRD-008 |
| **Épica** | OQI-003 - Trading y Charts |
| **Módulo** | trading |
| **Prioridad** | P0 |
| **Story Points** | 3 |
| **Sprint** | Sprint 4 |
| **Estado** | Pendiente |
| **Asignado a** | Por asignar |

---

## Historia de Usuario

**Como** trader practicante,
**quiero** cerrar mis posiciones abiertas total o parcialmente,
**para** realizar mis ganancias o limitar mis pérdidas en el momento que decida.

## Descripción Detallada

El usuario debe poder cerrar posiciones long o short en cualquier momento, ya sea completamente o una porción específica. Al cerrar, se ejecuta una orden market en dirección opuesta y se calcula el P&L (profit and loss) realizado.

## Mockups/Wireframes

```
┌─────────────────────────────────────────────────────────────────┐
│  OPEN POSITIONS                                                 │
├─────────────────────────────────────────────────────────────────┤
│  Symbol    Side  Size    Entry      Current    PnL      Actions │
│  ──────────────────────────────────────────────────────────────  │
│  BTCUSDT   LONG  0.1 BTC $95,000   $97,234.50  +$223.45  [Close]│
│                                                  (+2.35%)  [ ▼ ] │
│  ──────────────────────────────────────────────────────────────  │
│                                                                   │
│  ┌────────────────────────────────────┐                         │
│  │ CLOSE POSITION                     │                         │
│  ├────────────────────────────────────┤                         │
│  │ Symbol: BTCUSDT                    │                         │
│  │ Position: 0.1 BTC LONG             │                         │
│  │                                     │                         │
│  │ Amount to close:                   │                         │
│  │ ┌──────────────────────────────┐  │                         │
│  │ │ 0.1 BTC                      │  │                         │
│  │ └──────────────────────────────┘  │                         │
│  │ [25%] [50%] [75%] [100%]           │                         │
│  │                                     │                         │
│  │ Entry Price: $95,000.00            │                         │
│  │ Current Price: $97,234.50          │                         │
│  │ Est. P&L: +$223.45 (+2.35%)        │                         │
│  │                                     │                         │
│  │  [Cancel]     [Close Position]     │                         │
│  └────────────────────────────────────┘                         │
└─────────────────────────────────────────────────────────────────┘
```

---

## Criterios de Aceptación

**Escenario 1: Cerrar posición long completa**
```gherkin
DADO que el usuario tiene posición long de 0.1 BTC en BTCUSDT
Y el precio de entrada fue $95,000
Y el precio actual es $97,234.50
CUANDO hace click en "Close" en la posición
Y selecciona cerrar 100% (0.1 BTC)
Y confirma
ENTONCES se ejecuta orden market SELL de 0.1 BTC
Y la posición se cierra completamente
Y se calcula P&L: +$223.45 (+2.35%)
Y el balance se incrementa en $9,723.45 ($9,500 + $223.45)
Y la posición desaparece de "Open Positions"
Y se muestra notificación "Position closed. P&L: +$223.45"
```

**Escenario 2: Cerrar posición short con ganancia**
```gherkin
DADO que el usuario tiene posición short de 0.05 BTC
Y el precio de entrada fue $97,000
Y el precio actual es $96,000
CUANDO cierra la posición completa
ENTONCES se ejecuta orden market BUY de 0.05 BTC
Y se calcula P&L: +$50.00 (+1.03%)
Y la posición se cierra
```

**Escenario 3: Cerrar posición parcial (50%)**
```gherkin
DADO que el usuario tiene posición long de 0.1 BTC
CUANDO hace click en "Close"
Y selecciona 50% (0.05 BTC)
Y confirma
ENTONCES se cierra solo 0.05 BTC
Y la posición restante es 0.05 BTC
Y el P&L parcial se registra
Y la posición sigue apareciendo con nuevo tamaño
```

**Escenario 4: Cerrar posición con pérdida**
```gherkin
DADO que el usuario tiene posición long a $97,000
Y el precio actual es $95,000
CUANDO cierra la posición
ENTONCES se calcula P&L: -$200.00 (-2.06%)
Y el balance se reduce en $200
Y se registra la pérdida en el historial
```

**Escenario 5: Confirmación antes de cerrar**
```gherkin
DADO que el usuario hace click en "Close"
CUANDO se abre el diálogo de confirmación
ENTONCES muestra:
  - Cantidad a cerrar
  - Precio de entrada vs actual
  - P&L estimado
  - Botón de confirmar
Y debe confirmar antes de ejecutar
```

## Criterios Adicionales

- [ ] Slippage aplicado al cerrar (±0.1%)
- [ ] Keyboard shortcut: Esc para cancelar
- [ ] Color verde para P&L positivo, rojo para negativo
- [ ] Mostrar ROI (Return on Investment) en %
- [ ] Registro en trade history

---

## Tareas Técnicas

**Database:**
- [ ] DB-TRD-012: Crear tabla paper_trade_history
- [ ] DB-TRD-013: Añadir campo closed_at a paper_positions

**Backend:**
- [ ] BE-TRD-038: Crear endpoint POST /trading/paper/positions/:id/close
- [ ] BE-TRD-039: Implementar PositionService.closePosition()
- [ ] BE-TRD-040: Implementar cálculo de P&L
- [ ] BE-TRD-041: Implementar cierre parcial
- [ ] BE-TRD-042: Actualizar BalanceService con P&L
- [ ] BE-TRD-043: Crear registro en trade_history

**Frontend:**
- [ ] FE-TRD-038: Crear componente OpenPositionsPanel.tsx
- [ ] FE-TRD-039: Crear componente ClosePositionDialog.tsx
- [ ] FE-TRD-040: Crear componente PositionRow.tsx con P&L
- [ ] FE-TRD-041: Implementar hook useClosePosition
- [ ] FE-TRD-042: Actualizar positionStore con cierres
- [ ] FE-TRD-043: Implementar animación al cerrar

**Tests:**
- [ ] TEST-TRD-019: Test unitario cálculo P&L
- [ ] TEST-TRD-020: Test integración cerrar posición
- [ ] TEST-TRD-021: Test E2E flujo completo cierre

---

## Dependencias

**Depende de:**
- [ ] US-TRD-006: Crear orden market - Estado: Pendiente (necesita posiciones abiertas)

**Bloquea:**
- [ ] US-TRD-010: Ver historial de trades

---

## Notas Técnicas

**Endpoints involucrados:**
| Método | Endpoint | Descripción |
|--------|----------|-------------|
| POST | /trading/paper/positions/:id/close | Cerrar posición |
| GET | /trading/paper/positions | Listar posiciones abiertas |
| GET | /trading/paper/positions/:id | Obtener detalles de posición |

**Entidades/Tablas:**
```sql
CREATE TABLE trading.paper_trade_history (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  user_id UUID NOT NULL REFERENCES auth.users(id),
  symbol VARCHAR(20) NOT NULL,
  side VARCHAR(10) NOT NULL,
  quantity DECIMAL(20, 8) NOT NULL,
  entry_price DECIMAL(20, 8) NOT NULL,
  exit_price DECIMAL(20, 8) NOT NULL,
  pnl DECIMAL(20, 8) NOT NULL,
  pnl_percentage DECIMAL(10, 4) NOT NULL,
  opened_at TIMESTAMP NOT NULL,
  closed_at TIMESTAMP DEFAULT NOW(),
  duration_seconds INTEGER
);

ALTER TABLE trading.paper_positions ADD COLUMN closed_at TIMESTAMP;
ALTER TABLE trading.paper_positions ADD COLUMN status VARCHAR(20) DEFAULT 'open';
```

**Componentes UI:**
- `OpenPositionsPanel`: Panel de posiciones abiertas
- `ClosePositionDialog`: Modal de confirmación
- `PositionRow`: Fila con datos de posición
- `PnLBadge`: Badge con P&L coloreado

**Request Body:**
```typescript
{
  quantity: 0.05,  // Cantidad a cerrar (o null para cerrar todo)
  percentage: 50   // O porcentaje (25, 50, 75, 100)
}
```

**Response:**
```typescript
{
  trade: {
    id: "uuid",
    symbol: "BTCUSDT",
    side: "long",
    quantity: 0.05,
    entryPrice: 95000.00,
    exitPrice: 97234.50,
    pnl: 111.73,
    pnlPercentage: 2.35,
    openedAt: "2025-12-05T09:00:00Z",
    closedAt: "2025-12-05T10:30:00Z",
    durationSeconds: 5400
  },
  position: {
    id: "uuid",
    remainingQuantity: 0.05,  // Si cierre parcial
    status: "open"  // o "closed"
  },
  balance: {
    balance: 9611.73,
    equity: 9723.46,
    marginUsed: 4762.50
  }
}
```

**Cálculo de P&L:**
```typescript
// Para posición LONG
const pnl = (exitPrice - entryPrice) * quantity;
const pnlPercentage = ((exitPrice - entryPrice) / entryPrice) * 100;

// Para posición SHORT
const pnl = (entryPrice - exitPrice) * quantity;
const pnlPercentage = ((entryPrice - exitPrice) / entryPrice) * 100;

// Aplicar slippage (simulación)
const slippage = 0.001; // 0.1%
const actualExitPrice = side === 'long'
  ? currentPrice * (1 - slippage)
  : currentPrice * (1 + slippage);
```

---

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