- Add 5 frontend specification documents (ET-*-frontend.md): - ET-AUTH-006: Authentication module frontend spec - ET-ML-008: ML Signals module frontend spec - ET-LLM-007: LLM Agent module frontend spec - ET-PFM-008: Portfolio Manager frontend spec (design) - ET-MKT-003: Marketplace frontend spec (design) - Add 8 new user stories: - US-AUTH-013: Global logout - US-AUTH-014: Device management - US-ML-008: Ensemble signal view - US-ML-009: ICT analysis view - US-ML-010: Multi-symbol scan - US-LLM-011: Execute trade from chat - US-PFM-013: Rebalance alerts - US-PFM-014: PDF report generation - Update task index with completed analysis Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
16 KiB
ET-PFM-008: Especificación Frontend - Módulo Portfolio Manager
Módulo: OQI-008 Portfolio Manager Documento: ET-PFM-008 Estado: A IMPLEMENTAR Versión: 1.0.0 Fecha de Creación: 2026-01-25 Última Actualización: 2026-01-25
📋 Resumen Ejecutivo
Este documento especifica la arquitectura, estructura de componentes y flujos de interfaz de usuario para el módulo Portfolio Manager en la plataforma de trading. Este es un diseño a implementar que no cuenta actualmente con código base.
El módulo proporciona funcionalidades de visualización, análisis y gestión de portafolios de inversión, incluyendo posiciones, métricas de riesgo, pruebas de estrés y generación de reportes.
🎯 Objetivos del Frontend
- Proporcionar visualización clara y en tiempo real del estado del portafolio
- Facilitar análisis de riesgo mediante métricas consolidadas
- Permitir simulaciones de rebalanceo de posiciones
- Generar reportes analíticos exportables
- Integrar datos de múltiples fuentes de mercado
📄 Páginas Propuestas
1. PortfolioPage
Ruta: /portfolio
Descripción: Página principal de gestión del portafolio
Responsabilidades:
- Mostrar resumen ejecutivo del portafolio
- Listar posiciones activas
- Mostrar indicadores clave de desempeño (KPIs)
- Permitir filtrado y búsqueda de posiciones
- Facilitar acceso a rebalanceo
Estructura:
PortfolioPage
├── PortfolioSummaryCard
├── PerformanceChart
├── PositionsTable
│ ├── Filtros
│ └── Paginación
└── Acciones (Rebalancear, Exportar)
Datos Requeridos:
- Valor total del portafolio
- Rentabilidad YTD
- Rentabilidad acumulada
- Composición por activo
- Posiciones individuales con métricas
2. StressTestPage
Ruta: /portfolio/stress-tests
Descripción: Página para ejecución y análisis de pruebas de estrés
Responsabilidades:
- Crear nuevas pruebas de estrés
- Ejecutar escenarios predefinidos
- Visualizar resultados de stress tests
- Comparar múltiples escenarios
- Exportar análisis de sensibilidad
Estructura:
StressTestPage
├── ScenarioSelector
├── SimulationControls
├── RiskMetricsPanel
├── ResultsVisualization
│ ├── ImpactChart
│ └── SensitivityTable
└── ExportOptions
Datos Requeridos:
- Escenarios disponibles
- Parámetros de simulación
- Resultados de ejecución
- Metricas de sensibilidad
3. ReportsPage
Ruta: /portfolio/reports
Descripción: Página de generación y consulta de reportes analíticos
Responsabilidades:
- Listar reportes generados
- Crear nuevos reportes personalizados
- Visualizar reportes en tiempo real
- Descargar reportes en múltiples formatos
- Programar generación automática de reportes
Estructura:
ReportsPage
├── ReportsList
│ └── ReportFilters
├── ReportGenerator
│ ├── ParameterSelector
│ └── FormatSelector
├── ReportViewer
│ ├── PaginationControls
│ └── ExportOptions
└── ScheduleManager
Datos Requeridos:
- Lista de reportes disponibles
- Historico de reportes generados
- Parámetros configurables
- Formato de salida (PDF, Excel, CSV)
🧩 Componentes Propuestos
1. PortfolioSummaryCard
Tipo: Componente Presentacional
Ubicación: src/components/portfolio/PortfolioSummaryCard.vue
Props:
interface PortfolioSummaryProps {
totalValue: number;
ytdReturn: number;
cumulativeReturn: number;
currency: string;
lastUpdated: Date;
isLoading?: boolean;
}
Funcionalidades:
- Mostrar valor total del portafolio
- Indicadores de rentabilidad (YTD, acumulada)
- Tendencia visual (arriba/abajo)
- Moneda de referencia
- Timestamp de actualización
- Estado de carga
Emits:
refresh: Solicitar actualización de datos
2. PositionsTable
Tipo: Componente Presentacional
Ubicación: src/components/portfolio/PositionsTable.vue
Props:
interface PositionsTableProps {
positions: Position[];
loading?: boolean;
sortBy?: string;
sortOrder?: 'asc' | 'desc';
pageSize?: number;
currentPage?: number;
}
interface Position {
id: string;
symbol: string;
name: string;
quantity: number;
entryPrice: number;
currentPrice: number;
currentValue: number;
percentageChange: number;
percentageOfPortfolio: number;
sector?: string;
lastUpdated: Date;
}
Funcionalidades:
- Visualización de tabla de posiciones
- Ordenamiento por columnas
- Paginación configurable
- Filtrado por sector/tipo
- Búsqueda por símbolo o nombre
- Indicadores visuales de desempeño
- Acciones por fila (detalles, vender, rebalancear)
Emits:
sort: Cambio de ordenamientopage-change: Cambio de páginaposition-selected: Selección de posiciónposition-action: Acción sobre posición
3. PerformanceChart
Tipo: Componente Presentacional
Ubicación: src/components/portfolio/PerformanceChart.vue
Props:
interface PerformanceChartProps {
data: PerformanceData[];
timeframe?: 'D' | 'W' | 'M' | 'Y' | 'ALL';
currency?: string;
showComparison?: boolean;
isLoading?: boolean;
}
interface PerformanceData {
date: Date;
value: number;
return: number;
benchmark?: number;
}
Funcionalidades:
- Gráfico de línea del desempeño del portafolio
- Múltiples plazos (Día, Semana, Mes, Año, Todo)
- Comparación con benchmark
- Visualización de retorno absoluto y porcentual
- Interactividad con tooltips
- Exportación como imagen
Emits:
timeframe-change: Cambio de períodorange-select: Selección de rango personalizado
4. RiskMetricsPanel
Tipo: Componente Presentacional
Ubicación: src/components/portfolio/RiskMetricsPanel.vue
Props:
interface RiskMetricsPanelProps {
metrics: RiskMetrics;
isLoading?: boolean;
}
interface RiskMetrics {
volatility: number; // Volatilidad anualizada
sharpeRatio: number; // Ratio de Sharpe
sortinoRatio: number; // Ratio de Sortino
valueAtRisk: number; // VaR 95%
expectedShortfall: number; // CVaR (Expected Shortfall)
beta: number; // Beta respecto a benchmark
correlation: number; // Correlación con benchmark
maxDrawdown: number; // Máxima caída histórica
diversificationRatio: number; // Ratio de diversificación
}
Funcionalidades:
- Visualización de métricas de riesgo clave
- Indicadores de riesgo (bajo/medio/alto)
- Comparación con benchmarks
- Explicación de métricas (tooltips)
- Gráficos de sensibilidad
- Histórico de evolución de métricas
Emits:
metric-selected: Selección de métrica para análisis profundolearn-more: Solicitud de explicación de métrica
5. RebalanceModal
Tipo: Componente Modal
Ubicación: src/components/portfolio/RebalanceModal.vue
Props:
interface RebalanceModalProps {
visible: boolean;
portfolio: Portfolio;
targetAllocation: AllocationTarget[];
currentAllocation: AllocationCurrent[];
constraints?: RebalanceConstraints;
}
interface AllocationTarget {
assetType: string;
targetPercentage: number;
minPercentage?: number;
maxPercentage?: number;
}
interface RebalanceConstraints {
maxTransactionCost?: number;
minTradeSize?: number;
excludeAssets?: string[];
allowNewPositions?: boolean;
}
Funcionalidades:
- Mostrar asignación actual vs. objetivo
- Calcular transacciones necesarias
- Permitir selección de restricciones
- Vista previa de cambios
- Estimación de costos de transacción
- Confirmación antes de ejecutar
- Historial de rebalanceos
Emits:
close: Cierre del modalconfirm: Confirmación de rebalanceocancel: Cancelación de operaciónpreview: Solicitud de vista previa
🗂️ Estructura de Store
portfolioStore
Ubicación: src/stores/portfolioStore.ts
Estado:
interface PortfolioState {
portfolio: Portfolio | null;
positions: Position[];
selectedPosition: Position | null;
performanceData: PerformanceData[];
riskMetrics: RiskMetrics | null;
stressTestResults: StressTestResult[] | null;
reports: Report[];
loading: {
portfolio: boolean;
positions: boolean;
riskMetrics: boolean;
stressTest: boolean;
reports: boolean;
};
filters: {
sector?: string;
minValue?: number;
maxValue?: number;
searchTerm?: string;
};
error: string | null;
}
interface Portfolio {
id: string;
name: string;
totalValue: number;
ytdReturn: number;
cumulativeReturn: number;
currency: string;
positions: Position[];
lastUpdated: Date;
}
Acciones Principales:
// Portafolio
fetchPortfolio(portfolioId: string): Promise<Portfolio>
updatePortfolio(portfolioId: string, data: Partial<Portfolio>): Promise<Portfolio>
selectPosition(position: Position): void
clearSelection(): void
// Posiciones
fetchPositions(portfolioId: string): Promise<Position[]>
updatePosition(positionId: string, data: Partial<Position>): Promise<Position>
deletePosition(positionId: string): Promise<void>
addPosition(position: Position): Promise<Position>
// Desempeño
fetchPerformanceData(portfolioId: string, timeframe: string): Promise<PerformanceData[]>
// Métricas de Riesgo
fetchRiskMetrics(portfolioId: string): Promise<RiskMetrics>
// Pruebas de Estrés
executeStressTest(portfolioId: string, scenario: StressScenario): Promise<StressTestResult>
fetchStressTestResults(portfolioId: string): Promise<StressTestResult[]>
// Rebalanceo
calculateRebalance(portfolioId: string, targets: AllocationTarget[]): Promise<RebalancePlan>
executeRebalance(portfolioId: string, plan: RebalancePlan): Promise<RebalanceResult>
// Reportes
fetchReports(portfolioId: string): Promise<Report[]>
generateReport(portfolioId: string, config: ReportConfig): Promise<Report>
exportReport(reportId: string, format: 'pdf' | 'excel' | 'csv'): Promise<Blob>
// Filtros
setFilter(filterName: string, value: any): void
clearFilters(): void
Getters:
getPortfolioValue(): number
getTotalReturn(): number
getPositionCount(): number
getFilteredPositions(): Position[]
getTopPerformer(): Position | null
getTopLoser(): Position | null
getRiskLevel(): 'low' | 'medium' | 'high'
🔄 Flujos de Datos
Flujo 1: Carga Inicial de Portafolio
PortfolioPage Montada
↓
[Dispatch] fetchPortfolio()
↓
API Backend (GET /api/portfolio/{id})
↓
[Commit] setPortfolio()
↓
PortfolioSummaryCard actualizado
PositionsTable actualizado
Flujo 2: Búsqueda y Filtrado
Usuario escribe en SearchInput
↓
[Dispatch] setFilter('searchTerm', value)
↓
Computed getFilteredPositions()
↓
PositionsTable re-renderiza
Flujo 3: Ejecución de Stress Test
Usuario selecciona escenario
↓
[Dispatch] executeStressTest(scenario)
↓
API Backend (POST /api/portfolio/stress-test)
↓
[Commit] setStressTestResults()
↓
RiskMetricsPanel actualizado
ResultsVisualization mostrada
Flujo 4: Rebalanceo
Usuario abre RebalanceModal
↓
[Dispatch] calculateRebalance(targets)
↓
API Backend (POST /api/portfolio/calculate-rebalance)
↓
Modal muestra vista previa
↓
Usuario confirma
↓
[Dispatch] executeRebalance(plan)
↓
API Backend (POST /api/portfolio/execute-rebalance)
↓
[Dispatch] fetchPortfolio() para refrescar
🎨 Estilos y Temas
Principios de Diseño
- Diseño Responsivo: Compatible con desktop, tablet y mobile
- Accesibilidad: WCAG 2.1 AA como mínimo
- Consistencia: Uso de design system corporativo
- Rendimiento: Carga y animaciones fluidas
Componentes de UI Requeridos
- Cards para resumen de métricas
- Tablas con ordenamiento y paginación
- Gráficos interactivos (Chart.js o Echarts)
- Modals para acciones confirmables
- Alerts y notificaciones Toast
- Loaders y spinners
- Iconografía consistente
📊 Integraciones Externas
APIs Backend Requeridas
| Endpoint | Método | Descripción |
|---|---|---|
/api/portfolio/{id} |
GET | Obtener datos del portafolio |
/api/portfolio/{id} |
PUT | Actualizar portafolio |
/api/portfolio/{id}/positions |
GET | Listar posiciones |
/api/portfolio/{id}/positions |
POST | Crear posición |
/api/portfolio/position/{id} |
PUT | Actualizar posición |
/api/portfolio/position/{id} |
DELETE | Eliminar posición |
/api/portfolio/{id}/performance |
GET | Obtener datos de desempeño |
/api/portfolio/{id}/risk-metrics |
GET | Calcular métricas de riesgo |
/api/portfolio/stress-test |
POST | Ejecutar prueba de estrés |
/api/portfolio/{id}/rebalance/calculate |
POST | Calcular plan de rebalanceo |
/api/portfolio/{id}/rebalance/execute |
POST | Ejecutar rebalanceo |
/api/portfolio/{id}/reports |
GET | Listar reportes |
/api/portfolio/report/generate |
POST | Generar reporte |
/api/portfolio/report/{id}/export |
GET | Exportar reporte |
Servicios Externos
- Market Data Provider: Para precios en tiempo real
- Benchmark Data: Índices de referencia (S&P 500, etc.)
- Reporting Engine: Generación de reportes complejos
⚙️ Configuración y Constantes
Variables de Entorno
VITE_PORTFOLIO_API_BASE_URL=http://localhost:3000/api
VITE_CHART_LIBRARY=echarts|chart.js
VITE_REPORT_FORMAT_DEFAULT=pdf
VITE_AUTO_REFRESH_INTERVAL=30000
VITE_MAX_POSITIONS_DISPLAY=50
Constantes de Aplicación
const CURRENCY_SYMBOLS: Record<string, string> = {
USD: '$',
EUR: '€',
COP: '$',
};
const TIMEFRAMES = ['D', 'W', 'M', 'Y', 'ALL'] as const;
const RISK_LEVELS = {
low: { min: 0, max: 0.08 },
medium: { min: 0.08, max: 0.15 },
high: { min: 0.15, max: Infinity },
};
const STRESS_SCENARIOS = [
{ id: 'market-crash', label: 'Caída de Mercado (-10%)', intensity: -0.1 },
{ id: 'rate-shock', label: 'Choque de Tasas (+2%)', intensity: 0.02 },
{ id: 'geopolitical', label: 'Evento Geopolítico', intensity: -0.05 },
];
📱 Responsive Design
Breakpoints
- Mobile: < 640px
- Tablet: 640px - 1024px
- Desktop: > 1024px
Ajustes por Pantalla
| Elemento | Mobile | Tablet | Desktop |
|---|---|---|---|
| PortfolioSummaryCard | Apilado | 2 columnas | 4 columnas |
| PositionsTable | Horizontal scroll | Scroll limitado | Completo |
| PerformanceChart | Altura reducida | Altura media | Altura completa |
| RiskMetricsPanel | 1 columna | 2 columnas | 3 columnas |
🔒 Seguridad
- Autenticación: Bearer token en headers
- Validación Frontend: Validación de input antes de envío
- HTTPS Obligatorio: En producción
- CORS: Configuración restrictiva
- Rate Limiting: Control de llamadas a API
📝 Notas de Implementación
Estado Actual
Este documento describe un diseño que aún no ha sido implementado. No existe código base en el repositorio para estos componentes.
Próximos Pasos
- Crear estructura de directorios de componentes
- Implementar store de Pinia con acciones básicas
- Desarrollar componentes en orden de dependencia
- Integrar con APIs backend
- Implementar pruebas unitarias
- Realizar pruebas de integración
- Optimizar rendimiento
Consideraciones Técnicas
- Framework: Vue 3 (Composition API recomendada)
- State Management: Pinia
- Gráficos: Echarts o Chart.js según disponibilidad
- Validación: Vee-validate
- Estilos: TailwindCSS o SCSS
- Pruebas: Vitest + Vue Test Utils
📚 Referencias Relacionadas
- OQI-008 Portfolio Manager - Especificación General
- ET-PFM-001-backend.md - Especificación Backend
- ET-PFM-002-database.md - Especificación Base de Datos
- Arquitectura Trading Platform
✅ Control de Cambios
| Versión | Fecha | Autor | Cambios |
|---|---|---|---|
| 1.0.0 | 2026-01-25 | Sistema | Especificación inicial |
Estado: A IMPLEMENTAR | Prioridad: Alta | Dependencias: Backend Portfolio Manager (OQI-008)