588 lines
16 KiB
Markdown
588 lines
16 KiB
Markdown
# 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
|