# Integración con OrbiQuant Trading Platform

Guía de integración del servicio Trading Agents con otros módulos de la plataforma.

## 1. Integración con Backend (NestJS)

### 1.1 Investment Service Integration

El backend debe comunicarse con el servicio de Trading Agents para:

1. **Asignar agente a cuenta de inversión**
2. **Notificar depósitos/retiros**
3. **Obtener métricas de rendimiento**
4. **Gestionar estado del agente**

### 1.2 Ejemplo de Cliente TypeScript

```typescript
// backend/src/services/trading-agents.client.ts

import axios, { AxiosInstance } from 'axios';

export class TradingAgentsClient {
  private client: AxiosInstance;

  constructor() {
    this.client = axios.create({
      baseURL: process.env.TRADING_AGENTS_URL || 'http://trading-agents:8003',
      timeout: 30000,
      headers: {
        'Content-Type': 'application/json',
      },
    });
  }

  /**
   * Iniciar agente para una cuenta de inversión
   */
  async startAgent(accountId: string, agentType: string, initialEquity: number) {
    const response = await this.client.post(`/agents/${agentType}/start`, {
      agent_name: agentType,
      initial_equity: initialEquity,
    });

    return response.data;
  }

  /**
   * Obtener estado del agente
   */
  async getAgentStatus(agentType: string) {
    const response = await this.client.get(`/agents/${agentType}/status`);
    return response.data;
  }

  /**
   * Obtener métricas del agente
   */
  async getAgentMetrics(agentType: string) {
    const response = await this.client.get(`/agents/${agentType}/metrics`);
    return response.data;
  }

  /**
   * Pausar agente
   */
  async pauseAgent(agentType: string) {
    const response = await this.client.post(`/agents/${agentType}/pause`);
    return response.data;
  }

  /**
   * Reanudar agente
   */
  async resumeAgent(agentType: string) {
    const response = await this.client.post(`/agents/${agentType}/resume`);
    return response.data;
  }

  /**
   * Detener agente
   */
  async stopAgent(agentType: string) {
    const response = await this.client.post(`/agents/${agentType}/stop`);
    return response.data;
  }

  /**
   * Obtener posiciones abiertas
   */
  async getPositions(agentType: string) {
    const response = await this.client.get(`/agents/${agentType}/positions`);
    return response.data;
  }
}
```

### 1.3 Investment Account Service

```typescript
// backend/src/modules/investment/services/account.service.ts

import { Injectable } from '@nestjs/common';
import { TradingAgentsClient } from '../../../services/trading-agents.client';

@Injectable()
export class AccountService {
  constructor(
    private tradingAgentsClient: TradingAgentsClient,
  ) {}

  async openAccount(userId: string, productId: string, initialDeposit: number) {
    // 1. Crear cuenta en DB
    const account = await this.createAccountRecord(userId, productId, initialDeposit);

    // 2. Determinar tipo de agente según producto
    const agentType = this.getAgentTypeByProduct(productId);

    // 3. Iniciar agente
    await this.tradingAgentsClient.startAgent(
      account.id,
      agentType,
      initialDeposit
    );

    return account;
  }

  async getAccountPerformance(accountId: string) {
    const account = await this.findAccountById(accountId);
    const agentType = this.getAgentTypeByProduct(account.product_id);

    // Obtener métricas del agente
    const metrics = await this.tradingAgentsClient.getAgentMetrics(agentType);

    return {
      account_id: accountId,
      current_balance: account.current_balance,
      total_return: metrics.total_profit - metrics.total_loss,
      win_rate: metrics.win_rate,
      total_trades: metrics.total_trades,
      current_drawdown: metrics.current_drawdown,
    };
  }

  private getAgentTypeByProduct(productId: string): string {
    // Mapear producto a agente
    // Atlas: productos conservadores
    // Orion: productos moderados
    // Nova: productos agresivos
    // Implementar lógica según tu DB
    return 'atlas'; // placeholder
  }
}
```

## 2. Integración con ML Engine

### 2.1 Flujo de Señales

```
ML Engine → Trading Agents → Execute Trade
```

El ML Engine debe exponer endpoints para:

1. **POST /api/v1/signals/batch** - Señales en batch
2. **GET /api/v1/signals/latest/{symbol}** - Última señal por símbolo
3. **GET /api/v1/signals/account/{account_id}** - Señales para cuenta

### 2.2 Ejemplo de Integración

```python
# ml-engine/api/signals.py

from fastapi import APIRouter, Depends
from typing import List

router = APIRouter(prefix="/signals", tags=["signals"])

@router.post("/batch")
async def get_signals_batch(symbols: List[str]):
    """
    Obtener señales para múltiples símbolos
    Usado por MLSignalConsumer del Trading Agents
    """
    signals = {}

    for symbol in symbols:
        # Generar señal usando tu modelo ML
        signal = generate_signal(symbol)
        signals[symbol] = signal

    return {"signals": signals}

@router.get("/latest/{symbol}")
async def get_latest_signal(symbol: str):
    """
    Obtener última señal para un símbolo
    """
    signal = generate_signal(symbol)

    return {
        "signal": {
            "symbol": symbol,
            "action": signal.action,  # buy, sell, hold
            "confidence": signal.confidence,  # 0-1
            "price": signal.price,
            "timestamp": signal.timestamp
        }
    }

def generate_signal(symbol: str):
    # Tu lógica ML aquí
    pass
```

## 3. Integración con Database

### 3.1 Guardar Trades en DB

```python
# trading-agents/src/journal/trade_journal.py

import asyncpg
from datetime import datetime

class TradeJournal:
    """
    Guarda trades en la base de datos investment.account_transactions
    """

    def __init__(self, db_url: str):
        self.db_url = db_url
        self.pool = None

    async def connect(self):
        self.pool = await asyncpg.create_pool(self.db_url)

    async def record_trade(
        self,
        account_id: str,
        symbol: str,
        side: str,
        quantity: float,
        entry_price: float,
        exit_price: float,
        profit_loss: float
    ):
        """Guardar trade en DB"""
        async with self.pool.acquire() as conn:
            # Insertar en account_transactions
            await conn.execute("""
                INSERT INTO investment.account_transactions (
                    account_id,
                    transaction_type,
                    amount,
                    balance_before,
                    balance_after,
                    description,
                    reference_type,
                    reference_id
                ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
            """,
                account_id,
                'profit' if profit_loss > 0 else 'loss',
                abs(profit_loss),
                0,  # balance_before (obtener de agente)
                0,  # balance_after
                f"{side.upper()} {symbol} {quantity} @ {entry_price}",
                'agent_trade',
                None  # ID del trade
            )

    async def update_daily_performance(
        self,
        account_id: str,
        date: str,
        opening_balance: float,
        closing_balance: float,
        trades_count: int,
        winning_trades: int
    ):
        """Actualizar performance diaria"""
        async with self.pool.acquire() as conn:
            await conn.execute("""
                INSERT INTO investment.performance_snapshots (
                    account_id,
                    snapshot_date,
                    period_type,
                    opening_balance,
                    closing_balance,
                    profit_loss,
                    total_trades,
                    winning_trades
                ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
                ON CONFLICT (account_id, snapshot_date, period_type)
                DO UPDATE SET
                    closing_balance = EXCLUDED.closing_balance,
                    profit_loss = EXCLUDED.profit_loss,
                    total_trades = EXCLUDED.total_trades,
                    winning_trades = EXCLUDED.winning_trades
            """,
                account_id,
                date,
                'daily',
                opening_balance,
                closing_balance,
                closing_balance - opening_balance,
                trades_count,
                winning_trades
            )
```

### 3.2 Uso en Agentes

```python
# Modificar agentes para usar TradeJournal

class AtlasAgent(BaseAgent):
    def __init__(self, equity: float, db_url: str, account_id: str):
        super().__init__(...)
        self.account_id = account_id
        self.journal = TradeJournal(db_url)

    async def on_start(self):
        await self.journal.connect()
        # ...

    async def _execute_close(self, symbol: str):
        position = self.get_position(symbol)
        # ... cerrar posición

        # Guardar en DB
        await self.journal.record_trade(
            account_id=self.account_id,
            symbol=position.symbol,
            side=position.side,
            quantity=position.quantity,
            entry_price=position.entry_price,
            exit_price=position.current_price,
            profit_loss=position.pnl
        )
```

## 4. Variables de Entorno

### 4.1 Actualizar docker-compose.yml principal

```yaml
# /trading-platform/docker-compose.yml

services:
  trading-agents:
    build: ./apps/trading-agents
    environment:
      - DATABASE_URL=postgresql+asyncpg://${POSTGRES_USER}:${POSTGRES_PASSWORD}@database:5432/${POSTGRES_DB}
      - ML_ENGINE_URL=http://ml-engine:8000
      - BINANCE_API_KEY=${BINANCE_API_KEY}
      - BINANCE_API_SECRET=${BINANCE_API_SECRET}
      - BINANCE_TESTNET=true
    depends_on:
      - database
      - ml-engine
    networks:
      - orbiquant-network
```

### 4.2 Variables Requeridas

```bash
# .env principal del proyecto

# Trading Agents
BINANCE_API_KEY=your_testnet_key
BINANCE_API_SECRET=your_testnet_secret
BINANCE_TESTNET=true
TRADING_AGENTS_URL=http://trading-agents:8003
```

## 5. Cron Jobs para Sincronización

### 5.1 Daily Performance Sync

```typescript
// backend/src/jobs/sync-trading-performance.job.ts

import { Injectable } from '@nestjs/common';
import { Cron } from '@nestjs/schedule';
import { TradingAgentsClient } from '../services/trading-agents.client';

@Injectable()
export class SyncTradingPerformanceJob {
  constructor(
    private tradingAgentsClient: TradingAgentsClient,
  ) {}

  @Cron('0 0 * * *') // Diariamente a medianoche
  async syncPerformance() {
    // Obtener todas las cuentas activas con agentes
    const accounts = await this.getActiveAccounts();

    for (const account of accounts) {
      const agentType = this.getAgentType(account.product_id);

      // Obtener métricas del agente
      const metrics = await this.tradingAgentsClient.getAgentMetrics(agentType);

      // Actualizar en DB
      await this.updateAccountMetrics(account.id, metrics);
    }
  }
}
```

## 6. Webhooks del ML Engine

### 6.1 Configurar Webhook en Trading Agents

```python
# trading-agents/src/api/webhooks.py

from fastapi import APIRouter, Request, HTTPException
import hmac
import hashlib

router = APIRouter(prefix="/webhooks", tags=["webhooks"])

WEBHOOK_SECRET = "your_webhook_secret"

@router.post("/ml-signals")
async def receive_ml_signal(request: Request):
    """
    Recibir señal del ML Engine vía webhook
    """
    # Verificar firma
    signature = request.headers.get("X-ML-Signature")
    payload = await request.body()

    expected_signature = hmac.new(
        WEBHOOK_SECRET.encode(),
        payload,
        hashlib.sha256
    ).hexdigest()

    if signature != expected_signature:
        raise HTTPException(status_code=401, detail="Invalid signature")

    # Procesar señal
    data = await request.json()

    # Despachar a agente correspondiente
    agent_name = data.get('agent_name', 'atlas')
    if agent_name in agents:
        await agents[agent_name].on_signal(data['signal'])

    return {"status": "received"}
```

## 7. Monitoreo y Alertas

### 7.1 Health Check Endpoint

El backend debe monitorear el servicio:

```typescript
// backend/src/health/trading-agents.health.ts

import { Injectable } from '@nestjs/common';
import { HealthIndicator, HealthIndicatorResult } from '@nestjs/terminus';
import { TradingAgentsClient } from '../services/trading-agents.client';

@Injectable()
export class TradingAgentsHealthIndicator extends HealthIndicator {
  constructor(
    private tradingAgentsClient: TradingAgentsClient,
  ) {
    super();
  }

  async isHealthy(key: string): Promise<HealthIndicatorResult> {
    try {
      const response = await this.tradingAgentsClient.healthCheck();

      return this.getStatus(key, true, {
        status: 'up',
        agents_running: response.agents_running
      });
    } catch (error) {
      return this.getStatus(key, false, {
        status: 'down',
        message: error.message
      });
    }
  }
}
```

## 8. Testing de Integración

### 8.1 Test End-to-End

```typescript
// backend/test/e2e/trading-agents.e2e-spec.ts

describe('Trading Agents Integration', () => {
  it('should create account and start agent', async () => {
    // 1. Crear cuenta de inversión
    const account = await request(app.getHttpServer())
      .post('/investment/accounts')
      .send({
        user_id: userId,
        product_id: 'atlas-conservative',
        initial_deposit: 1000.0
      })
      .expect(201);

    // 2. Verificar que agente se inició
    const agentStatus = await tradingAgentsClient.getAgentStatus('atlas');
    expect(agentStatus.status).toBe('running');

    // 3. Simular señal ML
    await tradingAgentsClient.sendSignal('atlas', {
      symbol: 'BTCUSDT',
      action: 'buy',
      confidence: 0.85,
      price: 45000.0
    });

    // 4. Verificar posición abierta
    const positions = await tradingAgentsClient.getPositions('atlas');
    expect(positions.total_positions).toBeGreaterThan(0);
  });
});
```

## 9. Diagrama de Flujo Completo

```
┌─────────────┐
│   Usuario   │
└──────┬──────┘
       │ 1. Abre cuenta
       ▼
┌─────────────────┐
│  Backend API    │
│   (NestJS)      │
└──────┬──────────┘
       │ 2. Start agent
       ▼
┌─────────────────┐
│ Trading Agents  │◄──── 3. Poll signals ────┐
│   (Python)      │                           │
└──────┬──────────┘                    ┌──────┴──────┐
       │ 4. Execute trade               │  ML Engine  │
       ▼                                │  (Python)   │
┌─────────────────┐                    └─────────────┘
│    Binance      │
│   Testnet       │
└──────┬──────────┘
       │ 5. Order filled
       ▼
┌─────────────────┐
│   PostgreSQL    │◄──── 6. Save trade
│   Database      │
└─────────────────┘
```

## 10. Checklist de Integración

- [ ] Configurar variables de entorno en docker-compose
- [ ] Implementar TradingAgentsClient en backend
- [ ] Integrar con Investment Account Service
- [ ] Configurar webhooks del ML Engine
- [ ] Implementar TradeJournal para guardar en DB
- [ ] Agregar health checks
- [ ] Configurar cron jobs de sincronización
- [ ] Testing end-to-end
- [ ] Monitoreo y alertas
- [ ] Documentación API

---

Para más información, consultar:
- `README.md` - Documentación principal
- `PAPER_TRADING_GUIDE.md` - Guía de paper trading
- `IMPLEMENTATION_REPORT.md` - Reporte de implementación
