---
id: "RF-TRD-006"
title: "Gestion de Posiciones"
type: "Requirement"
status: "Done"
priority: "Alta"
module: "trading"
epic: "OQI-003"
version: "1.0"
created_date: "2025-12-05"
updated_date: "2026-01-04"
---

# RF-TRD-006: Gestión de Posiciones

**Versión:** 1.0.0
**Fecha:** 2025-12-05
**Épica:** OQI-003 - Trading y Charts
**Prioridad:** P0
**Story Points:** 8

---

## Descripción

El sistema debe proporcionar una gestión completa de posiciones abiertas, permitiendo a los usuarios monitorear su exposición al mercado, PnL en tiempo real, y ejecutar acciones sobre sus posiciones de manera eficiente y segura.

---

## Requisitos Funcionales

### RF-TRD-006.1: Apertura de Posiciones

El sistema debe:
- Crear posición automáticamente al ejecutar orden buy/sell
- Soportar posiciones long (compra) y short (venta, futuro)
- Permitir una posición por símbolo en paper trading MVP
- Calcular precio promedio de entrada
- Registrar timestamp de apertura
- Aplicar stop loss y take profit si fueron configurados

### RF-TRD-006.2: Información de Posición

El sistema debe mostrar para cada posición:

| Campo | Descripción | Cálculo |
|-------|-------------|---------|
| Symbol | Par de trading | - |
| Side | Long/Short | - |
| Entry Price | Precio promedio de entrada | Weighted average |
| Current Price | Precio actual del mercado | Real-time |
| Quantity | Cantidad del activo | - |
| Position Value | Valor actual de la posición | quantity × currentPrice |
| Unrealized PnL | Ganancia/pérdida no realizada | (currentPrice - entryPrice) × quantity |
| Unrealized PnL % | Porcentaje de ganancia/pérdida | ((currentPrice - entryPrice) / entryPrice) × 100 |
| Margin Used | Margen ocupado | positionValue / leverage |
| Liquidation Price | Precio de liquidación | Calculado según leverage |
| Duration | Tiempo abierta | now - openedAt |

### RF-TRD-006.3: Actualización en Tiempo Real

El sistema debe:
- Actualizar precios actuales cada 1 segundo vía WebSocket
- Recalcular PnL automáticamente con cada update
- Mostrar animación visual en cambios de precio
- Destacar posiciones en riesgo (pérdida > 50%)
- Actualizar métricas agregadas (total PnL, total margin)

### RF-TRD-006.4: Panel de Posiciones

El sistema debe proporcionar tabla con:
- Lista de todas las posiciones abiertas
- Resumen agregado en header
- Filtros por símbolo, side, PnL
- Ordenamiento por cualquier columna
- Indicadores visuales de estado
- Acciones rápidas por posición

**Resumen agregado:**
```
Total Positions: 5
Total Margin Used: $15,234.56
Total Unrealized PnL: +$1,234.56 (+8.1%)
```

### RF-TRD-006.5: Cerrar Posiciones

El sistema debe permitir:

**Cierre total:**
- Cerrar 100% de la posición
- Ejecutar orden market al precio actual
- Calcular PnL realizado
- Actualizar balance
- Registrar trade en historial

**Cierre parcial:**
- Cerrar porcentaje específico (25%, 50%, 75%)
- Mantener resto de posición abierta
- Recalcular precio promedio
- Registrar trade parcial

**Confirmación:**
- Modal con previsualización del cierre
- Mostrar PnL estimado
- Mostrar balance después del cierre
- Requiere confirmación del usuario

### RF-TRD-006.6: Stop Loss y Take Profit

El sistema debe:

**Configuración:**
- Añadir SL/TP a posición existente
- Modificar SL/TP existentes
- Eliminar SL/TP
- Validar precios lógicos según side

**Monitoreo automático:**
- Verificar precios cada 1 segundo
- Ejecutar cierre automático al alcanzar nivel
- Notificar al usuario
- Registrar motivo de cierre

**Trailing Stop Loss:**
- Actualizar SL automáticamente según ganancia
- Configurar trailing distance (% o USD)
- Activar solo cuando posición es ganadora

### RF-TRD-006.7: Alertas y Notificaciones

El sistema debe notificar cuando:
- Posición alcanza +X% de ganancia
- Posición alcanza -X% de pérdida
- Stop Loss está cerca de activarse (5%)
- Take Profit está cerca de activarse (5%)
- Posición está en riesgo de liquidación
- Posición ha estado abierta > 24h

### RF-TRD-006.8: Gestión de Riesgo

El sistema debe:

**Validaciones:**
- Impedir apertura si margin used > 80% del balance
- Advertir si posición representa > 50% del portfolio
- Bloquear si existe ya posición del mismo símbolo
- Validar que stop loss sea lógico

**Liquidación:**
- Calcular precio de liquidación
- Liquidar automáticamente al alcanzarlo
- Notificar antes de liquidación (margen < 20%)
- Registrar liquidación en historial

**Métricas de riesgo:**
- Exposure por símbolo
- Exposure total
- Ratio de margen (margin used / balance)
- Win rate actual
- Average holding time

---

## Datos de Entrada

### Modificar Posición

```typescript
interface UpdatePositionDto {
  positionId: string;
  stopLoss?: {
    type: 'price' | 'percent';
    value: number;
    trailing?: {
      enabled: boolean;
      distance: number;  // % o USD
    };
  };
  takeProfit?: {
    type: 'price' | 'percent';
    value: number;
  };
}
```

### Cerrar Posición

```typescript
interface ClosePositionDto {
  positionId: string;
  percentage: number;  // 1-100, default 100
  type: 'market' | 'limit';
  limitPrice?: number;
}
```

---

## Datos de Salida

### Posición

```typescript
interface Position {
  id: string;
  userId: string;
  symbol: string;
  side: 'long' | 'short';

  // Cantidades
  quantity: number;
  entryPrice: number;
  currentPrice: number;

  // PnL
  unrealizedPnl: number;
  unrealizedPnlPercent: number;
  realizedPnl: number;        // De cierres parciales

  // Valores
  positionValue: number;
  marginUsed: number;

  // Stop Loss / Take Profit
  stopLoss: PositionStopLoss | null;
  takeProfit: PositionTakeProfit | null;

  // Liquidación
  liquidationPrice: number | null;
  liquidationRisk: 'low' | 'medium' | 'high';

  // Timestamps
  openedAt: string;
  lastUpdatedAt: string;
  duration: number;  // Segundos

  // Estado
  status: 'open' | 'closing' | 'closed';
  isInProfit: boolean;
}

interface PositionStopLoss {
  price: number;
  type: 'price' | 'percent';
  isTrailing: boolean;
  trailingDistance?: number;
  originalPrice?: number;  // Para trailing
}

interface PositionTakeProfit {
  price: number;
  type: 'price' | 'percent';
}
```

### Resumen de Posiciones

```typescript
interface PositionsSummary {
  totalPositions: number;
  totalMarginUsed: number;
  totalUnrealizedPnl: number;
  totalUnrealizedPnlPercent: number;
  marginRatio: number;  // marginUsed / balance

  byStatus: {
    profitable: number;
    unprofitable: number;
  };

  bySide: {
    long: number;
    short: number;
  };

  topGainer: Position | null;
  topLoser: Position | null;
}
```

---

## Reglas de Negocio

1. **Posiciones simultáneas:**
   - MVP: 1 posición por símbolo
   - Futuro: Múltiples posiciones (avg down/up)

2. **Cierre de posiciones:**
   - Cierre parcial mínimo: 10%
   - Siempre ejecutar a precio market
   - Actualizar entrada promedio en parciales

3. **Stop Loss:**
   - Debe estar por debajo del entry (long) o por encima (short)
   - Mínimo 0.5% del entry price
   - Máximo 50% del entry price

4. **Take Profit:**
   - Debe estar por encima del entry (long) o por debajo (short)
   - Mínimo 0.5% del entry price
   - Sin límite máximo

5. **Liquidación:**
   - Ocurre si pérdida > 80% del margen
   - Notificar cuando margen < 20%
   - Liquidar todo, no parcial

6. **Trailing Stop:**
   - Solo se activa si posición en ganancia
   - Se mueve solo hacia ganancia, nunca retrocede
   - Mínima distancia: 0.5%

---

## Criterios de Aceptación

```gherkin
Escenario: Posición se crea al ejecutar orden
  DADO que el usuario creó orden buy de 0.5 BTC a $50,000
  CUANDO la orden se ejecuta
  ENTONCES se crea posición long
  Y entry price es $50,000
  Y quantity es 0.5 BTC
  Y aparece en panel de posiciones

Escenario: PnL se actualiza en tiempo real
  DADO que el usuario tiene posición long con entry $50,000
  Y quantity es 0.5 BTC
  CUANDO el precio sube a $51,000
  ENTONCES unrealized PnL muestra +$500 (+2%)
  Y el valor se actualiza cada segundo
  Y se muestra en color verde

Escenario: Usuario cierra posición parcialmente
  DADO que el usuario tiene posición de 1 BTC
  CUANDO cierra 50% de la posición
  ENTONCES se ejecuta orden sell de 0.5 BTC
  Y la posición se reduce a 0.5 BTC
  Y se calcula PnL realizado de la mitad cerrada
  Y se registra trade en historial

Escenario: Stop Loss se activa
  DADO que el usuario tiene posición long entry $50,000
  Y stop loss configurado en $48,000
  CUANDO el precio baja a $48,000
  ENTONCES se ejecuta orden market sell automática
  Y la posición se cierra completamente
  Y se registra PnL realizado de -4%
  Y aparece notificación "Stop Loss activado"

Escenario: Trailing Stop se ajusta
  DADO que el usuario tiene trailing stop con 5% de distancia
  Y posición long entry $50,000
  Y stop loss inicial $47,500
  CUANDO el precio sube a $55,000
  ENTONCES stop loss se actualiza a $52,250 (5% debajo)
  Y se muestra en panel de posiciones
  Y se preserva ganancia de +4.5%

Escenario: Advertencia de riesgo de liquidación
  DADO que el usuario tiene posición con 80% de pérdida
  CUANDO se actualiza el precio
  ENTONCES aparece alerta "Riesgo de liquidación"
  Y se destaca la posición en rojo
  Y se sugiere añadir margen o cerrar

Escenario: Usuario modifica Stop Loss
  DADO que el usuario tiene posición con SL en $48,000
  CUANDO modifica SL a $49,000
  ENTONCES el nuevo SL se guarda
  Y se monitorea el nuevo nivel
  Y aparece confirmación
```

---

## Interfaz de Usuario

```
┌────────────────────────────────────────────────────────────────┐
│                    OPEN POSITIONS                              │
├────────────────────────────────────────────────────────────────┤
│  Total: 3  │  Margin: $15,234  │  PnL: +$1,234 (+8.1%)      │
├────────────────────────────────────────────────────────────────┤
│                                                                │
│  Symbol  │ Side │ Entry  │ Current │  Qty   │    PnL   │     │
│──────────┼──────┼────────┼─────────┼────────┼──────────┼─────│
│ BTCUSDT  │ LONG │ 50,000 │ 51,500  │ 0.5    │ +$750    │ ... │
│          │      │        │         │        │ (+3.0%)  │     │
│          │      │ SL: $48,000  TP: $55,000           │ Close│
│──────────┼──────┼────────┼─────────┼────────┼──────────┼─────│
│ ETHUSDT  │ LONG │  3,200 │  3,350  │ 2.0    │ +$300    │ ... │
│          │      │        │         │        │ (+4.7%)  │     │
│          │      │ SL: None     TP: $3,500            │ Close│
│──────────┼──────┼────────┼─────────┼────────┼──────────┼─────│
│ BNBUSDT  │ LONG │    450 │    425  │ 10     │ -$250    │ ... │
│          │      │        │         │        │ (-5.6%)  │     │
│          │      │ SL: $400     TP: None              │ Close│
└────────────────────────────────────────────────────────────────┘
```

---

## Estados de Posición

```
┌──────────┐
│   OPEN   │  ← Estado normal
└────┬─────┘
     │
     ├─→ Profitable (PnL > 0)
     ├─→ Unprofitable (PnL < 0)
     ├─→ At Risk (loss > 50%)
     │
     ├─→ [SL triggered] → CLOSING → CLOSED
     ├─→ [TP triggered] → CLOSING → CLOSED
     ├─→ [User closes]  → CLOSING → CLOSED
     └─→ [Liquidated]   → LIQUIDATED
```

---

## Dependencias

- RF-TRD-005: Sistema de Órdenes (crear órdenes de cierre)
- RF-TRD-004: Paper Trading (balance y ejecución)
- RF-TRD-007: Historial (registrar trades cerrados)
- WebSocket para precios en tiempo real

---

## Notas Técnicas

- Actualizar PnL con debounce de 1 segundo
- Usar transacciones para cierres de posición
- Implementar locks para evitar double-close
- Cachear cálculos pesados (liquidation price)
- Guardar snapshots de precio cada minuto
- Implementar circuit breaker para liquidaciones masivas
- Usar Redis para monitoreo eficiente de SL/TP
- Indexar por userId y status para queries rápidas

---

## Cálculos Importantes

### PnL para Long Position
```
Unrealized PnL = (currentPrice - entryPrice) × quantity
Unrealized PnL % = ((currentPrice - entryPrice) / entryPrice) × 100
```

### PnL para Short Position (futuro)
```
Unrealized PnL = (entryPrice - currentPrice) × quantity
Unrealized PnL % = ((entryPrice - currentPrice) / entryPrice) × 100
```

### Liquidation Price (sin apalancamiento)
```
Liquidation Price = entryPrice × 0.2  (pérdida del 80%)
```

### Trailing Stop Loss Update
```
New SL = currentPrice × (1 - trailingDistance)  // Para long
```

---

## Métricas a Trackear

- Posiciones abiertas concurrentes
- Tiempo promedio de holding
- Win rate (profitable / total)
- Profit factor (gross profit / gross loss)
- Activaciones de SL vs TP
- Liquidaciones (debería ser 0 idealmente)
- Máximo drawdown por posición
