Documentation alignment validation completed: - ET-ML-004-api.md: Updated to v2.0.0 with 15 real endpoints documented - ML_INVENTORY.yml: Updated to v2.1.0, added 11 models (ML-008 to ML-018) - TRACEABILITY.yml: Updated to v1.7.0, fixed US-ML-004 mapping - Added VALIDACION-ALINEACION-ML-2026-01-07.md validation report Discrepancies resolved: 10/11 (91%) - All critical and high priority discrepancies fixed - M2 (incompatible ML clients) requires code changes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
26 KiB
| id | title | type | status | priority | epic | project | version | created_date | updated_date |
|---|---|---|---|---|---|---|---|---|---|
| ET-ML-004 | FastAPI Endpoints | Technical Specification | Done | Alta | OQI-006 | trading-platform | 2.0.0 | 2025-12-05 | 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:
- ENDPOINTS IMPLEMENTADOS (v2.0.0) - Endpoints reales en producción
- 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:
{
status: string; // "healthy"
version: string; // "0.1.0"
models_loaded: boolean;
timestamp: string; // ISO 8601
}
GET /models
Lista modelos disponibles y su estado.
Response:
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<string, number>;
}>
GET /symbols
Lista símbolos de trading disponibles.
Response:
["XAUUSD", "EURUSD", "GBPUSD", "USDJPY", "BTCUSD", "ETHUSD"]
2. Predictions
POST /predict/range
Predice rangos de precio (ΔHigh/ΔLow) para un símbolo.
Request:
{
symbol: string; // "XAUUSD"
timeframe?: string; // "5m" | "15m" | "30m" | "1h" | "4h" | "1d" (default: "15m")
horizon?: string; // Prediction horizon (default: "15m")
features?: Record<string, number>; // Pre-computed features (optional)
}
Response:
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:
{
symbol: string;
timeframe?: string; // default: "15m"
horizon?: string;
}
Query Parameters:
rr_config: string - "rr_2_1" | "rr_3_1" (default: "rr_2_1")
Response:
{
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:
{
symbol: string;
timeframe?: string;
horizon?: string;
features?: Record<string, number>;
}
Query Parameters:
rr_config: string - Risk/Reward config (default: "rr_2_1")
Response:
{
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<string, any>;
}
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:
{
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:
{
phase: "accumulation" | "manipulation" | "distribution" | "unknown";
confidence: number;
start_time: string;
end_time?: string;
characteristics: Record<string, number>;
signals: string[];
strength: number;
trading_bias: Record<string, any>;
}
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:
{
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:
{
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<string, number>;
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:
{
symbols: string[]; // ["XAUUSD", "EURUSD", ...]
timeframe?: string; // default: "1h"
min_score?: number; // 0-100 (default: 50)
}
Response:
{
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:
{
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:
{
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:
{
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:
X-API-Key: your-api-key-here
Endpoints
1. Predictions
POST /predictions
Genera predicción de rango de precio.
Request:
{
symbol: string; // "BTCUSDT" | "ETHUSDT"
horizon: number; // 6 | 18 | 36 | 72 (candles)
}
Response:
{
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:
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:
{
"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:
{
symbol: string;
horizon: number;
include_range?: boolean; // Include price range prediction
include_tpsl?: boolean; // Include TP/SL prediction
}
Response:
{
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:
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:
{
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:
{
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:
{
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:
{
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:
{
status: "healthy" | "degraded" | "unhealthy";
timestamp: string;
}
GET /health/detailed
Health check detallado.
Response:
{
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)
# 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
# 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
# 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))
# 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
# 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:
{
"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
- Sin versionado de URL: La API no usa
/api/v1/en las rutas - Autenticación: API Key via header aún no implementado (CORS abierto en dev)
- Rate Limiting: Pendiente de implementar
- Símbolos soportados: XAUUSD, EURUSD, GBPUSD, USDJPY, BTCUSD, ETHUSD (no BTCUSDT/ETHUSDT)
Referencias
- ET-ML-001: Arquitectura
- FastAPI Documentation
- OpenAPI 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