# ET-LLM-002: Agente de Análisis de Mercado

**Épica:** OQI-007 - LLM Strategy Agent
**Versión:** 1.0
**Fecha:** 2025-12-05
**Estado:** Planificado
**Prioridad:** P0 - Crítico

---

## Resumen

Esta especificación define la arquitectura y prompts del agente especializado en análisis de mercado, incluyendo análisis técnico, fundamental y de sentimiento.

---

## Arquitectura del Agente

```
┌─────────────────────────────────────────────────────────────────────────┐
│                         MARKET ANALYSIS AGENT                            │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  ┌──────────────────────────────────────────────────────────────────┐   │
│  │                     System Prompt                                 │   │
│  │  - Rol: Analista de mercados experto                             │   │
│  │  - Capacidades: Técnico, fundamental, sentiment                   │   │
│  │  - Restricciones: Disclaimers, no asesoría financiera            │   │
│  └──────────────────────────────────────────────────────────────────┘   │
│                                                                          │
│  ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐   │
│  │   Tool:      │ │   Tool:      │ │   Tool:      │ │   Tool:      │   │
│  │  get_price   │ │ get_ohlcv    │ │get_indicators│ │  get_news    │   │
│  └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘   │
│                                                                          │
│  ┌──────────────┐ ┌──────────────┐ ┌──────────────┐                     │
│  │   Tool:      │ │   Tool:      │ │   Tool:      │                     │
│  │get_ml_signal │ │get_fundament │ │get_sentiment │                     │
│  └──────────────┘ └──────────────┘ └──────────────┘                     │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘
```

---

## System Prompt

```markdown
# Market Analysis Agent - OrbiQuant

## Tu Rol
Eres un analista de mercados experto especializado en trading. Tu objetivo es proporcionar análisis objetivos, detallados y accionables basados en datos reales del mercado.

## Capacidades
1. **Análisis Técnico:** Interpretas patrones de precio, indicadores técnicos y formaciones de velas
2. **Análisis Fundamental:** Evalúas métricas financieras, earnings y comparativas sectoriales (solo stocks)
3. **Análisis de Sentimiento:** Analizas noticias, menciones sociales y fear & greed index
4. **Integración ML:** Explicas predicciones del modelo de machine learning

## Herramientas Disponibles
- `get_price(symbol)`: Precio actual y cambio 24h
- `get_ohlcv(symbol, timeframe, limit)`: Datos de velas históricas
- `get_indicators(symbol, indicators[], timeframe)`: Indicadores técnicos calculados
- `get_news(symbol, limit)`: Noticias recientes con sentiment
- `get_ml_signals(symbol)`: Predicciones del ML Engine (solo Pro/Premium)
- `get_fundamentals(symbol)`: Datos fundamentales (solo stocks)
- `get_sentiment(symbol)`: Sentiment agregado de múltiples fuentes

## Formato de Respuesta para Análisis Completo

```markdown
## Análisis de [SYMBOL] - [Nombre Completo]

### Resumen Ejecutivo
[2-3 oraciones con conclusión principal y sesgo]

### Análisis Técnico
- **Tendencia:** [Alcista/Bajista/Lateral] ([justificación])
- **Soportes:** $X, $Y, $Z
- **Resistencias:** $X, $Y, $Z
- **Indicadores:**
  - RSI (14): [valor] - [interpretación]
  - MACD: [estado] - [interpretación]
  - BB: [posición] - [interpretación]
- **Patrones:** [patrones identificados]

### Análisis Fundamental (si aplica)
- **P/E:** [valor] vs sector [valor]
- **Revenue Growth:** [%]
- **Earnings:** [próximo/último]

### Sentimiento
- **Noticias:** [positivo/negativo/neutral]
- **Principales headlines:** [lista]

### Señales ML (si disponible)
- **Predicción:** [dirección]
- **Confianza:** [%]
- **Horizonte:** [timeframe]

### Oportunidad
[Descripción de oportunidad si existe]
- Entry: $X
- Stop: $X ([%] riesgo)
- Target: $X (R:R [ratio])

⚠️ *Disclaimer: Este análisis es informativo. No constituye asesoría financiera.*
```

## Reglas
1. SIEMPRE usa herramientas para obtener datos actuales - NUNCA inventes precios
2. SIEMPRE incluye disclaimer en análisis financieros
3. Si el usuario es Free, NO muestres señales ML
4. Para crypto, NO incluyas análisis fundamental
5. Sé objetivo - no exageres oportunidades
6. Indica nivel de confianza en tus análisis
7. Si no tienes datos suficientes, dilo claramente
```

---

## Tools Definitions

### get_price

```typescript
const getPriceTool = {
  type: 'function',
  function: {
    name: 'get_price',
    description: 'Obtiene el precio actual de un símbolo con cambio 24h',
    parameters: {
      type: 'object',
      properties: {
        symbol: {
          type: 'string',
          description: 'Símbolo del activo (ej: AAPL, BTC/USD)',
        },
      },
      required: ['symbol'],
    },
  },
};

// Implementation
async function getPrice(symbol: string): Promise<PriceData> {
  const normalizedSymbol = normalizeSymbol(symbol);
  const data = await marketDataService.getQuote(normalizedSymbol);

  return {
    symbol: normalizedSymbol,
    price: data.price,
    change24h: data.change,
    changePercent: data.changePercent,
    high24h: data.high,
    low24h: data.low,
    volume: data.volume,
    timestamp: data.timestamp,
  };
}
```

### get_ohlcv

```typescript
const getOhlcvTool = {
  type: 'function',
  function: {
    name: 'get_ohlcv',
    description: 'Obtiene datos OHLCV (velas) históricos',
    parameters: {
      type: 'object',
      properties: {
        symbol: {
          type: 'string',
          description: 'Símbolo del activo',
        },
        timeframe: {
          type: 'string',
          enum: ['1m', '5m', '15m', '1h', '4h', '1d', '1w'],
          description: 'Timeframe de las velas',
        },
        limit: {
          type: 'number',
          description: 'Número de velas (max 500)',
          default: 100,
        },
      },
      required: ['symbol', 'timeframe'],
    },
  },
};
```

### get_indicators

```typescript
const getIndicatorsTool = {
  type: 'function',
  function: {
    name: 'get_indicators',
    description: 'Calcula indicadores técnicos para un símbolo',
    parameters: {
      type: 'object',
      properties: {
        symbol: {
          type: 'string',
          description: 'Símbolo del activo',
        },
        indicators: {
          type: 'array',
          items: {
            type: 'string',
            enum: ['RSI', 'MACD', 'BB', 'SMA', 'EMA', 'ATR', 'VWAP', 'OBV'],
          },
          description: 'Lista de indicadores a calcular',
        },
        timeframe: {
          type: 'string',
          enum: ['1h', '4h', '1d'],
          default: '1d',
        },
      },
      required: ['symbol', 'indicators'],
    },
  },
};

// Implementation
async function getIndicators(
  symbol: string,
  indicators: string[],
  timeframe: string,
): Promise<IndicatorsData> {
  const ohlcv = await marketDataService.getOHLCV(symbol, timeframe, 100);
  const results: Record<string, any> = {};

  for (const indicator of indicators) {
    switch (indicator) {
      case 'RSI':
        results.RSI = calculateRSI(ohlcv.close, 14);
        break;
      case 'MACD':
        results.MACD = calculateMACD(ohlcv.close);
        break;
      case 'BB':
        results.BB = calculateBollingerBands(ohlcv.close, 20, 2);
        break;
      case 'SMA':
        results.SMA = {
          sma20: calculateSMA(ohlcv.close, 20),
          sma50: calculateSMA(ohlcv.close, 50),
          sma200: calculateSMA(ohlcv.close, 200),
        };
        break;
      case 'EMA':
        results.EMA = {
          ema12: calculateEMA(ohlcv.close, 12),
          ema26: calculateEMA(ohlcv.close, 26),
        };
        break;
      case 'ATR':
        results.ATR = calculateATR(ohlcv, 14);
        break;
      // ... más indicadores
    }
  }

  return {
    symbol,
    timeframe,
    indicators: results,
    timestamp: new Date().toISOString(),
  };
}
```

### get_news

```typescript
const getNewsTool = {
  type: 'function',
  function: {
    name: 'get_news',
    description: 'Obtiene noticias recientes con análisis de sentimiento',
    parameters: {
      type: 'object',
      properties: {
        symbol: {
          type: 'string',
          description: 'Símbolo del activo',
        },
        limit: {
          type: 'number',
          description: 'Número de noticias (max 10)',
          default: 5,
        },
      },
      required: ['symbol'],
    },
  },
};

// Implementation
async function getNews(symbol: string, limit: number): Promise<NewsData> {
  const news = await newsService.getBySymbol(symbol, limit);

  return {
    symbol,
    news: news.map(article => ({
      title: article.title,
      source: article.source,
      publishedAt: article.publishedAt,
      sentiment: article.sentiment, // -1 to 1
      sentimentLabel: getSentimentLabel(article.sentiment),
      summary: article.summary,
      url: article.url,
    })),
    overallSentiment: calculateOverallSentiment(news),
    timestamp: new Date().toISOString(),
  };
}
```

### get_ml_signals

```typescript
const getMlSignalsTool = {
  type: 'function',
  function: {
    name: 'get_ml_signals',
    description: 'Obtiene predicciones del ML Engine (solo Pro/Premium)',
    parameters: {
      type: 'object',
      properties: {
        symbol: {
          type: 'string',
          description: 'Símbolo del activo',
        },
      },
      required: ['symbol'],
    },
  },
};

// Implementation (with plan check)
async function getMlSignals(
  symbol: string,
  userPlan: string,
): Promise<MLSignalData | PermissionError> {
  if (userPlan === 'free') {
    return {
      error: 'PLAN_REQUIRED',
      message: 'Las señales ML requieren plan Pro o Premium',
      upgradeUrl: '/pricing',
    };
  }

  const signal = await mlService.getPrediction(symbol);

  return {
    symbol,
    prediction: signal.direction, // 'bullish' | 'bearish' | 'neutral'
    confidence: signal.confidence, // 0-1
    horizon: signal.horizon, // '1h', '4h', '1d'
    features: signal.topFeatures, // Principales features usados
    historicalAccuracy: signal.modelAccuracy,
    timestamp: signal.timestamp,
  };
}
```

### get_fundamentals

```typescript
const getFundamentalsTool = {
  type: 'function',
  function: {
    name: 'get_fundamentals',
    description: 'Obtiene datos fundamentales de una acción (no disponible para crypto)',
    parameters: {
      type: 'object',
      properties: {
        symbol: {
          type: 'string',
          description: 'Símbolo de la acción',
        },
      },
      required: ['symbol'],
    },
  },
};

// Implementation
async function getFundamentals(symbol: string): Promise<FundamentalsData> {
  if (isCrypto(symbol)) {
    return {
      error: 'NOT_AVAILABLE',
      message: 'Datos fundamentales no disponibles para crypto',
    };
  }

  const data = await fundamentalsService.get(symbol);

  return {
    symbol,
    companyName: data.name,
    sector: data.sector,
    industry: data.industry,
    marketCap: data.marketCap,
    peRatio: data.peRatio,
    pbRatio: data.pbRatio,
    eps: data.eps,
    epsGrowth: data.epsGrowth,
    revenue: data.revenue,
    revenueGrowth: data.revenueGrowth,
    profitMargin: data.profitMargin,
    dividendYield: data.dividendYield,
    nextEarnings: data.nextEarningsDate,
    analystRating: data.analystRating, // buy/hold/sell
    priceTarget: data.priceTarget,
    timestamp: new Date().toISOString(),
  };
}
```

---

## Flujo de Análisis Completo

```
┌─────────────────────────────────────────────────────────────┐
│ Usuario: "Dame un análisis completo de NVDA"                │
└──────────────────────────┬──────────────────────────────────┘
                           ↓
┌─────────────────────────────────────────────────────────────┐
│ 1. Agente determina herramientas necesarias:                │
│    - get_price("NVDA")                                      │
│    - get_ohlcv("NVDA", "1d", 100)                           │
│    - get_indicators("NVDA", ["RSI","MACD","BB"], "1d")      │
│    - get_fundamentals("NVDA")                               │
│    - get_news("NVDA", 5)                                    │
│    - get_ml_signals("NVDA") // si usuario Pro/Premium       │
└──────────────────────────┬──────────────────────────────────┘
                           ↓
┌─────────────────────────────────────────────────────────────┐
│ 2. Ejecutar herramientas en paralelo                        │
│    (batch de 6 tool calls simultáneos)                      │
└──────────────────────────┬──────────────────────────────────┘
                           ↓
┌─────────────────────────────────────────────────────────────┐
│ 3. Agente recibe resultados y sintetiza:                    │
│                                                              │
│    get_price → $145.23 (+2.3%)                              │
│    get_indicators → RSI: 58, MACD: bullish, BB: middle      │
│    get_fundamentals → P/E: 65, Rev Growth: 122%             │
│    get_news → 4 positive, 1 neutral                         │
│    get_ml_signals → bullish 68% (4h)                        │
└──────────────────────────┬──────────────────────────────────┘
                           ↓
┌─────────────────────────────────────────────────────────────┐
│ 4. Generar análisis estructurado con formato definido       │
│    (streaming al usuario)                                    │
└─────────────────────────────────────────────────────────────┘
```

---

## Tipos de Respuesta

### Interfaz de Response

```typescript
interface IndicatorsData {
  symbol: string;
  timeframe: string;
  indicators: {
    RSI?: {
      value: number;
      interpretation: 'oversold' | 'neutral' | 'overbought';
    };
    MACD?: {
      value: number;
      signal: number;
      histogram: number;
      interpretation: 'bullish' | 'bearish' | 'neutral';
    };
    BB?: {
      upper: number;
      middle: number;
      lower: number;
      percentB: number;
      interpretation: string;
    };
    SMA?: {
      sma20: number;
      sma50: number;
      sma200: number;
    };
    // ... más indicadores
  };
  timestamp: string;
}

interface FundamentalsData {
  symbol: string;
  companyName: string;
  sector: string;
  industry: string;
  marketCap: number;
  peRatio: number;
  pbRatio: number;
  eps: number;
  epsGrowth: number;
  revenue: number;
  revenueGrowth: number;
  profitMargin: number;
  dividendYield: number | null;
  nextEarnings: string | null;
  analystRating: 'strong_buy' | 'buy' | 'hold' | 'sell' | 'strong_sell';
  priceTarget: number;
  timestamp: string;
}

interface MLSignalData {
  symbol: string;
  prediction: 'bullish' | 'bearish' | 'neutral';
  confidence: number;
  horizon: string;
  features: Array<{
    name: string;
    importance: number;
  }>;
  historicalAccuracy: number;
  timestamp: string;
}
```

---

## Dependencias

### Servicios Requeridos
- MarketDataService (OQI-003)
- MLSignalService (OQI-006)
- NewsService (externo)
- FundamentalsService (externo)

### APIs Externas
- Alpaca Markets (market data)
- Alpha Vantage (fundamentals)
- News API (noticias)
- TradingAgent ML Engine (predicciones)

---

## Referencias

- [RF-LLM-002: Análisis de Mercado](../requerimientos/RF-LLM-002-market-analysis.md)
- [ET-LLM-003: Integración ML](./ET-LLM-003-integracion-ml.md)

---

*Especificación técnica - Sistema NEXUS*
*OrbiQuant IA Trading Platform*
