--- id: "ET-ML-004" title: "FastAPI Endpoints" type: "Technical Specification" status: "Done" priority: "Alta" epic: "OQI-006" project: "trading-platform" version: "2.0.0" created_date: "2025-12-05" updated_date: "2026-01-07" --- # ET-ML-004: FastAPI Endpoints ## Metadata | Campo | Valor | |-------|-------| | **ID** | ET-ML-004 | | **Épica** | OQI-006 - Señales ML | | **Tipo** | Especificación Técnica | | **Versión** | 2.0.0 | | **Estado** | Implementado | | **Última actualización** | 2026-01-07 | --- ## Propósito Especificar los endpoints de la API REST del ML Engine, incluyendo schemas de request/response, validación, autenticación y documentación OpenAPI. --- ## ⚠️ IMPORTANTE: Realidad vs Planificación Este documento contiene DOS secciones de endpoints: 1. **ENDPOINTS IMPLEMENTADOS** (v2.0.0) - Endpoints reales en producción 2. **ENDPOINTS PLANIFICADOS** (v1.0.0) - Diseño original (para referencia) --- ## Base URL ``` Production: https://ml.orbiquant.com/ Development: http://localhost:3083/ ``` > **NOTA:** La API NO usa versionado en rutas (`/api/v1`). El versionado se maneja via headers. --- # ENDPOINTS IMPLEMENTADOS (v2.0.0) Esta sección documenta los endpoints **realmente implementados** en `apps/ml-engine/src/api/main.py`. ## 1. Health & System ### GET /health Health check básico. **Response:** ```typescript { status: string; // "healthy" version: string; // "0.1.0" models_loaded: boolean; timestamp: string; // ISO 8601 } ``` ### GET /models Lista modelos disponibles y su estado. **Response:** ```typescript Array<{ model_type: string; // "range_predictor" | "tpsl_classifier" version: string; status: string; // "deployed" horizons: string[]; // ["15m", "1h"] supported_symbols: string[]; last_trained?: string; metrics?: Record; }> ``` ### GET /symbols Lista símbolos de trading disponibles. **Response:** ```typescript ["XAUUSD", "EURUSD", "GBPUSD", "USDJPY", "BTCUSD", "ETHUSD"] ``` --- ## 2. Predictions ### POST /predict/range Predice rangos de precio (ΔHigh/ΔLow) para un símbolo. **Request:** ```typescript { symbol: string; // "XAUUSD" timeframe?: string; // "5m" | "15m" | "30m" | "1h" | "4h" | "1d" (default: "15m") horizon?: string; // Prediction horizon (default: "15m") features?: Record; // Pre-computed features (optional) } ``` **Response:** ```typescript Array<{ horizon: string; delta_high: number; delta_low: number; delta_high_bin?: number; delta_low_bin?: number; confidence_high: number; confidence_low: number; }> ``` ### POST /predict/tpsl Predice probabilidad de alcanzar TP antes que SL. **Request:** ```typescript { symbol: string; timeframe?: string; // default: "15m" horizon?: string; } ``` **Query Parameters:** - `rr_config`: string - "rr_2_1" | "rr_3_1" (default: "rr_2_1") **Response:** ```typescript { prob_tp_first: number; // 0.0 - 1.0 rr_config: string; confidence: number; calibrated: boolean; } ``` --- ## 3. Signals ### POST /generate/signal Genera señal de trading completa combinando range prediction, TP/SL y AMD. **Request:** ```typescript { symbol: string; timeframe?: string; horizon?: string; features?: Record; } ``` **Query Parameters:** - `rr_config`: string - Risk/Reward config (default: "rr_2_1") **Response:** ```typescript { signal_id: string; symbol: string; direction: "long" | "short"; entry_price: number; stop_loss: number; take_profit: number; risk_reward_ratio: number; prob_tp_first: number; confidence_score: number; amd_phase: "accumulation" | "manipulation" | "distribution" | "unknown"; volatility_regime: "low" | "medium" | "high" | "extreme"; range_prediction: RangePredictionResponse; timestamp: string; valid_until: string; metadata?: Record; } ``` ### GET /api/signals/active Obtiene señales activas para múltiples símbolos en paralelo. **Query Parameters:** - `symbols`: string - Comma-separated (default: all) - `timeframe`: string - "15m" | "1h" etc. (default: "15m") - `rr_config`: string - (default: "rr_2_1") **Response:** ```typescript { signals: SignalResponse[]; generated_at: string; symbols_processed: string[]; errors: string[]; } ``` --- ## 4. AMD (Accumulation-Manipulation-Distribution) ### POST /api/amd/{symbol} Detecta fase AMD actual para un símbolo usando Smart Money Concepts. **Path Parameters:** - `symbol`: string - Trading symbol **Query Parameters:** - `timeframe`: string - (default: "15m") - `lookback_periods`: number - 50-500 (default: 100) **Response:** ```typescript { phase: "accumulation" | "manipulation" | "distribution" | "unknown"; confidence: number; start_time: string; end_time?: string; characteristics: Record; signals: string[]; strength: number; trading_bias: Record; } ``` --- ## 5. ICT/SMC (Inner Circle Trader / Smart Money Concepts) ### POST /api/ict/{symbol} Análisis ICT/SMC completo detectando Order Blocks, FVG, Liquidity Sweeps, etc. **Path Parameters:** - `symbol`: string - Trading symbol **Query Parameters:** - `timeframe`: string - (default: "1h") - `lookback_periods`: number - 100-500 (default: 200) **Response:** ```typescript { timestamp: string; symbol: string; timeframe: string; market_bias: string; bias_confidence: number; current_trend: string; order_blocks: OrderBlock[]; fair_value_gaps: FVG[]; liquidity_sweeps: LiquiditySweep[]; structure_breaks: StructureBreak[]; premium_zone: { low: number; high: number }; discount_zone: { low: number; high: number }; equilibrium: number; entry_zone?: { low: number; high: number }; stop_loss?: number; take_profits: { tp1?: number; tp2?: number; tp3?: number }; risk_reward?: number; signals: string[]; score: number; } ``` --- ## 6. Ensemble (Multi-Strategy) ### POST /api/ensemble/{symbol} Obtiene señal combinada del ensemble de estrategias. Combina: - AMD Detector (25% weight) - ICT/SMC Detector (35% weight) - Range Predictor (20% weight) - TP/SL Classifier (20% weight) **Path Parameters:** - `symbol`: string **Query Parameters:** - `timeframe`: string - (default: "1h") **Response:** ```typescript { timestamp: string; symbol: string; timeframe: string; action: string; confidence: number; strength: string; scores: { bullish: number; bearish: number; net: number }; levels: { entry?: number; stop_loss?: number; take_profit_1?: number; ... }; position: { risk_percent: number; size_multiplier: number }; model_signals: ModelSignal[]; confluence_count: number; market_phase: string; market_bias: string; key_levels: Record; signals: string[]; setup_score: number; } ``` ### GET /api/ensemble/quick/{symbol} Señal rápida simplificada para consumo inmediato. **Path Parameters:** - `symbol`: string **Query Parameters:** - `timeframe`: string - (default: "1h") **Response:** Simplified signal object --- ## 7. Scanner (Multi-Symbol) ### POST /api/scan Escanea múltiples símbolos buscando oportunidades de trading. **Request:** ```typescript { symbols: string[]; // ["XAUUSD", "EURUSD", ...] timeframe?: string; // default: "1h" min_score?: number; // 0-100 (default: 50) } ``` **Response:** ```typescript { timestamp: string; signals: QuickSignal[]; best_setups: QuickSignal[]; // Top 5 by score market_overview: { total_analyzed: number; bullish: number; bearish: number; neutral: number; sentiment: "bullish" | "bearish" | "neutral"; }; } ``` --- ## 8. Training & Backtesting ### POST /api/backtest Ejecuta backtest en datos históricos (mock implementation). **Request:** ```typescript { symbol: string; start_date: string; end_date: string; initial_capital?: number; // default: 10000 risk_per_trade?: number; // 0.001-0.1 (default: 0.02) rr_config?: string; filter_by_amd?: boolean; // default: true min_confidence?: number; // 0-1 (default: 0.55) } ``` ### POST /api/train/full Entrena modelos ML con walk-forward validation (mock implementation). **Request:** ```typescript { symbol: string; start_date: string; end_date: string; models_to_train?: string[]; // default: ["range_predictor", "tpsl_classifier"] use_walk_forward?: boolean; // default: true n_splits?: number; // 2-10 (default: 5) } ``` --- ## 9. WebSocket ### WS /ws/signals WebSocket para señales en tiempo real. **Connection:** `ws://localhost:3083/ws/signals` **Message Format:** ```typescript { type: "signal"; data: { symbol: string; direction: string; timestamp: string; } } ``` --- # ENDPOINTS PLANIFICADOS (v1.0.0 - Referencia) Los siguientes endpoints fueron el diseño original pero **NO están implementados** o tienen rutas diferentes: --- ## Autenticación Todos los endpoints requieren API Key en el header: ```http X-API-Key: your-api-key-here ``` --- ## Endpoints ### 1. Predictions #### POST /predictions Genera predicción de rango de precio. **Request:** ```typescript { symbol: string; // "BTCUSDT" | "ETHUSDT" horizon: number; // 6 | 18 | 36 | 72 (candles) } ``` **Response:** ```typescript { success: boolean; data: { symbol: string; horizon: number; horizon_label: string; // "scalping" | "intraday" | "swing" | "position" timestamp: string; // ISO 8601 current_price: number; predicted_high: number; predicted_low: number; delta_high_percent: number; delta_low_percent: number; range_percent: number; confidence: { mae: number; // Historical MAE for this horizon model_version: string; }; expires_at: string; // ISO 8601 - When this prediction expires }; metadata: { request_id: string; latency_ms: number; cached: boolean; }; } ``` **Example:** ```bash curl -X POST https://ml.trading.com/api/v1/predictions \ -H "X-API-Key: your-key" \ -H "Content-Type: application/json" \ -d '{"symbol": "BTCUSDT", "horizon": 18}' ``` **Response Example:** ```json { "success": true, "data": { "symbol": "BTCUSDT", "horizon": 18, "horizon_label": "intraday", "timestamp": "2025-12-05T10:30:00Z", "current_price": 43250.50, "predicted_high": 43520.75, "predicted_low": 43012.30, "delta_high_percent": 0.625, "delta_low_percent": -0.551, "range_percent": 1.176, "confidence": { "mae": 0.32, "model_version": "v1.2.0" }, "expires_at": "2025-12-05T12:00:00Z" }, "metadata": { "request_id": "req_abc123", "latency_ms": 45, "cached": false } } ``` --- ### 2. Signals #### POST /signals Genera señal de trading. **Request:** ```typescript { symbol: string; horizon: number; include_range?: boolean; // Include price range prediction include_tpsl?: boolean; // Include TP/SL prediction } ``` **Response:** ```typescript { success: boolean; data: { symbol: string; horizon: number; timestamp: string; signal: { type: "buy" | "sell" | "hold"; confidence: number; // 0.0 - 1.0 strength: "weak" | "moderate" | "strong"; probabilities: { hold: number; buy: number; sell: number; }; }; price_range?: { // If include_range = true current: number; predicted_high: number; predicted_low: number; }; tpsl?: { // If include_tpsl = true prediction: "take_profit" | "stop_loss"; probability_tp: number; probability_sl: number; suggested_tp_percent: number; suggested_sl_percent: number; }; recommendation: { action: "BUY" | "SELL" | "HOLD"; entry_zone?: { min: number; max: number; }; take_profit?: number; stop_loss?: number; risk_reward?: string; // "1:2.5" quality: "low" | "medium" | "high"; reason: string; }; }; } ``` **Example:** ```bash curl -X POST https://ml.trading.com/api/v1/signals \ -H "X-API-Key: your-key" \ -H "Content-Type: application/json" \ -d '{"symbol": "BTCUSDT", "horizon": 18, "include_range": true, "include_tpsl": true}' ``` --- #### GET /signals/history Obtiene historial de señales. **Query Parameters:** ``` symbol: string (required) horizon: number (optional) type: string (optional) - "buy" | "sell" | "hold" from: string (optional) - ISO 8601 date to: string (optional) - ISO 8601 date limit: number (optional) - default 50, max 100 offset: number (optional) - default 0 ``` **Response:** ```typescript { success: boolean; data: { signals: Array<{ id: string; symbol: string; horizon: number; type: string; confidence: number; current_price: number; created_at: string; outcome?: { result: "profit" | "loss" | "pending"; pnl_percent?: number; closed_at?: string; }; }>; pagination: { total: number; limit: number; offset: number; has_more: boolean; }; }; } ``` --- ### 3. Indicators #### GET /indicators Obtiene indicadores técnicos actuales. **Query Parameters:** ``` symbol: string (required) indicators: string (optional) - comma-separated list ``` **Response:** ```typescript { success: boolean; data: { symbol: string; timestamp: string; price: number; indicators: { rsi_14: number; macd: { line: number; signal: number; histogram: number; }; bollinger: { upper: number; middle: number; lower: number; position: number; // 0-100 }; moving_averages: { sma_20: number; sma_50: number; ema_12: number; ema_26: number; }; momentum: { roc_10: number; stochastic_k: number; stochastic_d: number; williams_r: number; }; volume: { current: number; avg_20: number; ratio: number; mfi_14: number; }; volatility: { atr_14: number; atr_percent: number; std_20: number; }; }; summary: { trend: "bullish" | "bearish" | "neutral"; momentum: "overbought" | "oversold" | "neutral"; volatility: "high" | "normal" | "low"; }; }; } ``` --- ### 4. Models #### GET /models/status Estado de los modelos cargados. **Response:** ```typescript { success: boolean; data: { models: Array<{ name: string; type: "regressor" | "classifier"; version: string; loaded: boolean; last_trained: string; metrics: { accuracy?: number; mae?: number; f1_score?: number; }; symbols: string[]; horizons: number[]; }>; system: { total_models: number; loaded_models: number; memory_usage_mb: number; }; }; } ``` #### GET /models/{model_name}/metrics Métricas detalladas de un modelo. **Response:** ```typescript { success: boolean; data: { model_name: string; version: string; training: { samples: number; features: number; trained_at: string; training_time_seconds: number; }; performance: { // For regressors mae?: number; mse?: number; rmse?: number; mape?: number; // For classifiers accuracy?: number; precision?: number; recall?: number; f1_score?: number; auc?: number; }; feature_importance: Array<{ feature: string; importance: number; }>; recent_predictions: { total: number; correct: number; accuracy_24h: number; }; }; } ``` --- ### 5. Health #### GET /health Health check básico. **Response:** ```typescript { status: "healthy" | "degraded" | "unhealthy"; timestamp: string; } ``` #### GET /health/detailed Health check detallado. **Response:** ```typescript { status: "healthy" | "degraded" | "unhealthy"; timestamp: string; components: { api: { status: "up" | "down"; response_time_ms: number; }; models: { status: "up" | "down"; loaded_count: number; total_count: number; }; redis: { status: "up" | "down"; latency_ms: number; }; binance: { status: "up" | "down"; last_price_update: string; }; database: { status: "up" | "down"; connection_pool: { active: number; idle: number; max: number; }; }; }; metrics: { uptime_seconds: number; requests_per_minute: number; avg_latency_ms: number; error_rate_percent: number; }; } ``` --- ## Schemas (Pydantic) ```python # app/schemas/prediction.py from pydantic import BaseModel, Field, validator from typing import Optional, Literal from datetime import datetime class PredictionRequest(BaseModel): symbol: str = Field(..., description="Trading pair symbol") horizon: int = Field(..., description="Prediction horizon in candles") @validator('symbol') def validate_symbol(cls, v): valid_symbols = ['BTCUSDT', 'ETHUSDT'] if v not in valid_symbols: raise ValueError(f'Symbol must be one of: {valid_symbols}') return v @validator('horizon') def validate_horizon(cls, v): valid_horizons = [6, 18, 36, 72] if v not in valid_horizons: raise ValueError(f'Horizon must be one of: {valid_horizons}') return v class Config: schema_extra = { "example": { "symbol": "BTCUSDT", "horizon": 18 } } class PredictionConfidence(BaseModel): mae: float model_version: str class PredictionData(BaseModel): symbol: str horizon: int horizon_label: str timestamp: datetime current_price: float predicted_high: float predicted_low: float delta_high_percent: float delta_low_percent: float range_percent: float confidence: PredictionConfidence expires_at: datetime class ResponseMetadata(BaseModel): request_id: str latency_ms: int cached: bool class PredictionResponse(BaseModel): success: bool data: PredictionData metadata: ResponseMetadata ``` ```python # app/schemas/signal.py from pydantic import BaseModel, Field from typing import Optional, Literal from datetime import datetime class SignalRequest(BaseModel): symbol: str horizon: int include_range: bool = False include_tpsl: bool = False class SignalProbabilities(BaseModel): hold: float buy: float sell: float class Signal(BaseModel): type: Literal["buy", "sell", "hold"] confidence: float = Field(..., ge=0, le=1) strength: Literal["weak", "moderate", "strong"] probabilities: SignalProbabilities class PriceRange(BaseModel): current: float predicted_high: float predicted_low: float class TPSL(BaseModel): prediction: Literal["take_profit", "stop_loss"] probability_tp: float probability_sl: float suggested_tp_percent: float suggested_sl_percent: float class EntryZone(BaseModel): min: float max: float class Recommendation(BaseModel): action: Literal["BUY", "SELL", "HOLD"] entry_zone: Optional[EntryZone] take_profit: Optional[float] stop_loss: Optional[float] risk_reward: Optional[str] quality: Literal["low", "medium", "high"] reason: str class SignalData(BaseModel): symbol: str horizon: int timestamp: datetime signal: Signal price_range: Optional[PriceRange] tpsl: Optional[TPSL] recommendation: Recommendation class SignalResponse(BaseModel): success: bool data: SignalData ``` --- ## Router Implementation ```python # app/api/routers/predictions.py from fastapi import APIRouter, Depends, HTTPException from app.schemas.prediction import PredictionRequest, PredictionResponse from app.services.predictor import PredictorService from app.core.security import validate_api_key from app.core.rate_limit import limiter import uuid import time router = APIRouter() @router.post("", response_model=PredictionResponse) @limiter.limit("100/minute") async def create_prediction( request: PredictionRequest, api_key: str = Depends(validate_api_key), predictor: PredictorService = Depends() ): """ Generate price range prediction for a trading pair. - **symbol**: Trading pair (BTCUSDT, ETHUSDT) - **horizon**: Prediction horizon in 5-minute candles (6, 18, 36, 72) """ start_time = time.time() request_id = str(uuid.uuid4())[:8] try: prediction, cached = await predictor.predict( symbol=request.symbol, horizon=request.horizon ) latency_ms = int((time.time() - start_time) * 1000) return PredictionResponse( success=True, data=prediction, metadata={ "request_id": f"req_{request_id}", "latency_ms": latency_ms, "cached": cached } ) except Exception as e: raise HTTPException(status_code=500, detail=str(e)) ``` ```python # app/api/routers/signals.py from fastapi import APIRouter, Depends, Query from typing import Optional, List from app.schemas.signal import SignalRequest, SignalResponse, SignalHistoryResponse from app.services.signal_generator import SignalGeneratorService from app.core.security import validate_api_key router = APIRouter() @router.post("", response_model=SignalResponse) async def generate_signal( request: SignalRequest, api_key: str = Depends(validate_api_key), signal_gen: SignalGeneratorService = Depends() ): """Generate trading signal with optional range and TP/SL predictions.""" return await signal_gen.generate( symbol=request.symbol, horizon=request.horizon, include_range=request.include_range, include_tpsl=request.include_tpsl ) @router.get("/history") async def get_signal_history( symbol: str, horizon: Optional[int] = None, type: Optional[str] = Query(None, regex="^(buy|sell|hold)$"), limit: int = Query(50, le=100), offset: int = 0, api_key: str = Depends(validate_api_key), signal_gen: SignalGeneratorService = Depends() ): """Get historical signals with optional filters.""" return await signal_gen.get_history( symbol=symbol, horizon=horizon, signal_type=type, limit=limit, offset=offset ) ``` --- ## Error Responses ```python # app/core/exceptions.py from fastapi import HTTPException class APIError(HTTPException): """Base API error""" pass class ValidationError(APIError): def __init__(self, detail: str): super().__init__(status_code=400, detail=detail) class AuthenticationError(APIError): def __init__(self): super().__init__(status_code=401, detail="Invalid API key") class RateLimitError(APIError): def __init__(self): super().__init__(status_code=429, detail="Rate limit exceeded") class ModelError(APIError): def __init__(self, detail: str): super().__init__(status_code=500, detail=f"Model error: {detail}") ``` **Error Response Format:** ```json { "success": false, "error": { "code": "VALIDATION_ERROR", "message": "Symbol XYZUSDT is not supported", "details": { "field": "symbol", "valid_values": ["BTCUSDT", "ETHUSDT"] } }, "metadata": { "request_id": "req_abc123", "timestamp": "2025-12-05T10:30:00Z" } } ``` --- ## Rate Limits | Endpoint | Limit | Window | |----------|-------|--------| | POST /predictions | 100 | 1 minute | | POST /signals | 100 | 1 minute | | GET /signals/history | 60 | 1 minute | | GET /indicators | 120 | 1 minute | | GET /models/* | 30 | 1 minute | | GET /health | Unlimited | - | --- # RESUMEN DE DISCREPANCIAS ## Endpoints Documentados vs Implementados | Endpoint Original | Estado | Endpoint Real | |-------------------|--------|---------------| | `POST /api/v1/predictions` | ⚠️ Diferente ruta | `POST /predict/range` | | `POST /api/v1/signals` | ⚠️ Diferente ruta | `POST /generate/signal` | | `GET /api/v1/signals/history` | ❌ No implementado | - | | `GET /api/v1/indicators` | ❌ No implementado | - | | `GET /api/v1/models/status` | ⚠️ Diferente ruta | `GET /models` | | `GET /api/v1/models/{name}/metrics` | ❌ No implementado | - | | `GET /api/v1/health` | ⚠️ Sin versioning | `GET /health` | | `GET /api/v1/health/detailed` | ❌ No implementado | - | ## Endpoints Nuevos (No Documentados Originalmente) | Endpoint | Descripción | |----------|-------------| | `POST /predict/tpsl` | Predicción TP/SL | | `GET /symbols` | Lista de símbolos | | `GET /api/signals/active` | Señales activas multi-símbolo | | `POST /api/amd/{symbol}` | Detección AMD | | `POST /api/ict/{symbol}` | Análisis ICT/SMC | | `POST /api/ensemble/{symbol}` | Señal ensemble | | `GET /api/ensemble/quick/{symbol}` | Señal rápida | | `POST /api/scan` | Scanner multi-símbolo | | `POST /api/backtest` | Backtesting | | `POST /api/train/full` | Entrenamiento | | `WS /ws/signals` | WebSocket tiempo real | ## Cambios Arquitectónicos 1. **Sin versionado de URL**: La API no usa `/api/v1/` en las rutas 2. **Autenticación**: API Key via header aún no implementado (CORS abierto en dev) 3. **Rate Limiting**: Pendiente de implementar 4. **Símbolos soportados**: XAUUSD, EURUSD, GBPUSD, USDJPY, BTCUSD, ETHUSD (no BTCUSDT/ETHUSDT) --- ## Referencias - [ET-ML-001: Arquitectura](./ET-ML-001-arquitectura.md) - [FastAPI Documentation](https://fastapi.tiangolo.com/) - [OpenAPI Specification](https://swagger.io/specification/) - **Código fuente**: `apps/ml-engine/src/api/main.py` --- ## Changelog | Fecha | Versión | Cambio | |-------|---------|--------| | 2026-01-07 | 2.0.0 | Actualización completa con endpoints reales implementados | | 2026-01-07 | 2.0.0 | Separación en secciones IMPLEMENTADOS vs PLANIFICADOS | | 2026-01-07 | 2.0.0 | Documentación de 11 nuevos endpoints | | 2025-12-05 | 1.0.0 | Creación inicial con diseño planificado | --- **Autor:** Requirements-Analyst / ML-Architect **Última actualización:** 2026-01-07