workspace/projects/trading-platform/apps/trading-agents/INTEGRATION.md
rckrdmrd ea1879f4ad feat: Initial workspace structure with multi-level Git configuration
- Configure workspace Git repository with comprehensive .gitignore
- Add Odoo as submodule for ERP reference code
- Include documentation: SETUP.md, GIT-STRUCTURE.md
- Add gitignore templates for projects (backend, frontend, database)
- Structure supports independent repos per project/subproject level

Workspace includes:
- core/ - Reusable patterns, modules, orchestration system
- projects/ - Active projects (erp-suite, gamilit, trading-platform, etc.)
- knowledge-base/ - Reference code and patterns (includes Odoo submodule)
- devtools/ - Development tools and templates
- customers/ - Client implementations template

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 10:44:23 -06:00

16 KiB

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

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

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

# 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

# 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

# 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

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

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

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

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

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

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