# ET-TRD-011: Market Bias Indicator **VersiΓ³n:** 1.0.0 **Fecha:** 2026-01-25 **Epic:** OQI-003 - Trading y Charts **Componente:** Frontend Component + Backend Calculation **Estado:** πŸ”΄ No Implementado (especificaciΓ³n nueva) **Prioridad:** P3 --- ## Metadata | Campo | Valor | |-------|-------| | **ID** | ET-TRD-011 | | **Tipo** | EspecificaciΓ³n TΓ©cnica | | **Epic** | OQI-003 | | **US Relacionada** | US-TRD-011 (Ver Sesgo de Mercado Multi-Timeframe) | | **Componente Frontend** | `MarketBiasIndicator.tsx` (a crear) | | **Componente Backend** | `/api/market-bias/:symbol` (a crear) | | **Complejidad** | Media (anΓ‘lisis multi-timeframe + cΓ‘lculos tΓ©cnicos) | | **Esfuerzo Estimado** | 2 horas | --- ## 1. DescripciΓ³n General **Market Bias Indicator** es un indicador de sesgo de mercado que muestra la direcciΓ³n predominante del precio a travΓ©s de mΓΊltiples timeframes, utilizando una combinaciΓ³n de: - **Moving Averages (MA):** Tendencia basada en EMAs (20, 50, 200) - **RSI:** Momentum (sobrecompra/sobreventa) - **MACD:** Divergencia y seΓ±ales de cruce - **Price Action:** Higher Highs/Lower Lows - **Volume Trend:** ConfirmaciΓ³n de volumen ### Objetivo Proporcionar una visiΓ³n rΓ‘pida y clara del sesgo del mercado en mΓΊltiples timeframes (1m, 5m, 15m, 1h, 4h, 1d) para ayudar a los traders a: βœ… Identificar la tendencia dominante βœ… Alinear trades con el sesgo del mercado βœ… Detectar divergencias entre timeframes βœ… Confirmar setups de entrada con bias --- ## 2. Arquitectura del Indicador ### 2.1 Diagrama de Flujo ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Market Bias Indicator Architecture β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ β”‚ β”‚ Market Data (BTCUSD, Multiple Timeframes) β”‚ β”‚ β”‚ β”‚ β”‚ v β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Fetch Candles for 6 Timeframes β”‚ β”‚ β”‚ β”‚ β€’ 1m (last 100 candles) β”‚ β”‚ β”‚ β”‚ β€’ 5m (last 100 candles) β”‚ β”‚ β”‚ β”‚ β€’ 15m (last 100 candles) β”‚ β”‚ β”‚ β”‚ β€’ 1h (last 100 candles) β”‚ β”‚ β”‚ β”‚ β€’ 4h (last 100 candles) β”‚ β”‚ β”‚ β”‚ β€’ 1d (last 100 candles) β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ v v v v v v β”‚ β”‚ 1m 5m 15m 1h 4h 1d β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ v v v v v v β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Calculate Technical Indicators (per TF) β”‚ β”‚ β”‚ β”‚ β€’ EMA 20, 50, 200 β”‚ β”‚ β”‚ β”‚ β€’ RSI (14 period) β”‚ β”‚ β”‚ β”‚ β€’ MACD (12, 26, 9) β”‚ β”‚ β”‚ β”‚ β€’ Price trend (HH/HL vs LH/LL) β”‚ β”‚ β”‚ β”‚ β€’ Volume trend (avg volume vs current) β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ v β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Calculate Bias Score (per TF) β”‚ β”‚ β”‚ β”‚ β€’ MA Bias: +1 if price > all MAs, -1 if < β”‚ β”‚ β”‚ β”‚ β€’ RSI Bias: +1 if > 50, -1 if < 50 β”‚ β”‚ β”‚ β”‚ β€’ MACD Bias: +1 if histogram > 0, -1 if < 0 β”‚ β”‚ β”‚ β”‚ β€’ Trend Bias: +1 if HH/HL, -1 if LH/LL β”‚ β”‚ β”‚ β”‚ Total Score: Sum / 4 = [-1.0, +1.0] β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ v β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Aggregate Multi-Timeframe Bias β”‚ β”‚ β”‚ β”‚ β€’ 1m: +0.75 (BULLISH) β”‚ β”‚ β”‚ β”‚ β€’ 5m: +0.50 (BULLISH) β”‚ β”‚ β”‚ β”‚ β€’ 15m: +0.25 (NEUTRAL) β”‚ β”‚ β”‚ β”‚ β€’ 1h: -0.25 (NEUTRAL) β”‚ β”‚ β”‚ β”‚ β€’ 4h: -0.50 (BEARISH) β”‚ β”‚ β”‚ β”‚ β€’ 1d: -0.75 (BEARISH) β”‚ β”‚ β”‚ β”‚ Overall: +0.00 (NEUTRAL/CONFLICTED) β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ v β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Frontend Display β”‚ β”‚ β”‚ β”‚ β€’ Visual grid (6 timeframes) β”‚ β”‚ β”‚ β”‚ β€’ Color coding (green/red/yellow) β”‚ β”‚ β”‚ β”‚ β€’ Alignment indicator β”‚ β”‚ β”‚ β”‚ β€’ Recommended action β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` --- ## 3. CΓ‘lculo de Bias Score ### 3.1 Componentes del Score (per Timeframe) #### Component 1: Moving Average Bias ```typescript function calculateMABias(close: number, ema20: number, ema50: number, ema200: number): number { let score = 0; // Price above all MAs = strong bullish if (close > ema20 && close > ema50 && close > ema200) { score = 1.0; } // Price below all MAs = strong bearish else if (close < ema20 && close < ema50 && close < ema200) { score = -1.0; } // Price between MAs = mixed/neutral else if (close > ema20 && close > ema50) { score = 0.5; // Above short-term MAs } else if (close < ema20 && close < ema50) { score = -0.5; // Below short-term MAs } else { score = 0.0; // Choppy/neutral } return score; } ``` **Interpretation:** - +1.0 β†’ Price above ALL MAs (strong uptrend) - +0.5 β†’ Price above short-term MAs (moderate uptrend) - 0.0 β†’ Price mixed between MAs (neutral/range) - -0.5 β†’ Price below short-term MAs (moderate downtrend) - -1.0 β†’ Price below ALL MAs (strong downtrend) #### Component 2: RSI Bias ```typescript function calculateRSIBias(rsi: number): number { if (rsi > 70) return 1.0; // Overbought = strong bullish momentum if (rsi > 60) return 0.75; // Bullish momentum if (rsi > 50) return 0.5; // Slight bullish bias if (rsi === 50) return 0.0; // Neutral if (rsi > 40) return -0.5; // Slight bearish bias if (rsi > 30) return -0.75; // Bearish momentum return -1.0; // Oversold = strong bearish momentum } ``` #### Component 3: MACD Bias ```typescript function calculateMACDBias(macdLine: number, signalLine: number, histogram: number): number { let score = 0; // MACD above signal and histogram positive = bullish if (macdLine > signalLine && histogram > 0) { score = 1.0; } // MACD below signal and histogram negative = bearish else if (macdLine < signalLine && histogram < 0) { score = -1.0; } // MACD above signal but histogram negative = weakening bullish else if (macdLine > signalLine && histogram < 0) { score = 0.25; } // MACD below signal but histogram positive = weakening bearish else if (macdLine < signalLine && histogram > 0) { score = -0.25; } return score; } ``` #### Component 4: Price Action Bias (Higher Highs / Lower Lows) ```typescript function calculatePriceActionBias(candles: Candle[]): number { const last10 = candles.slice(-10); const highs = last10.map(c => c.high); const lows = last10.map(c => c.low); // Count higher highs (HH) let HH = 0; for (let i = 1; i < highs.length; i++) { if (highs[i] > highs[i - 1]) HH++; } // Count lower lows (LL) let LL = 0; for (let i = 1; i < lows.length; i++) { if (lows[i] < lows[i - 1]) LL++; } // HH dominate = uptrend if (HH > LL + 2) return 1.0; if (HH > LL) return 0.5; // LL dominate = downtrend if (LL > HH + 2) return -1.0; if (LL > HH) return -0.5; // Equal = neutral/range return 0.0; } ``` ### 3.2 Final Bias Score (per Timeframe) ```typescript function calculateBiasScore( candles: Candle[], ema20: number, ema50: number, ema200: number, rsi: number, macd: MACD ): BiasResult { const close = candles[candles.length - 1].close; const maBias = calculateMABias(close, ema20, ema50, ema200); const rsiBias = calculateRSIBias(rsi); const macdBias = calculateMACDBias(macd.macd, macd.signal, macd.histogram); const paBias = calculatePriceActionBias(candles); // Weighted average (all equal weight for simplicity) const totalScore = (maBias + rsiBias + macdBias + paBias) / 4; return { score: totalScore, // -1.0 to +1.0 direction: totalScore > 0.3 ? 'BULLISH' : totalScore < -0.3 ? 'BEARISH' : 'NEUTRAL', strength: Math.abs(totalScore) * 100, // 0-100% components: { ma: maBias, rsi: rsiBias, macd: macdBias, priceAction: paBias } }; } ``` --- ## 4. API Endpoint ### 4.1 Request ```http GET /api/market-bias/:symbol Query params: ?timeframes=1m,5m,15m,1h,4h,1d ``` ### 4.2 Response ```json { "symbol": "BTCUSD", "timestamp": "2026-01-25T10:30:15Z", "biases": [ { "timeframe": "1m", "score": 0.75, "direction": "BULLISH", "strength": 75, "components": { "ma": 1.0, "rsi": 0.75, "macd": 1.0, "priceAction": 0.25 }, "currentPrice": 43250.50, "indicators": { "ema20": 43100.00, "ema50": 42950.00, "ema200": 42500.00, "rsi": 68.5, "macd": { "macd": 125.3, "signal": 98.7, "histogram": 26.6 } } }, { "timeframe": "5m", "score": 0.50, "direction": "BULLISH", "strength": 50, "components": { /* ... */ } }, { "timeframe": "15m", "score": 0.25, "direction": "NEUTRAL", "strength": 25, "components": { /* ... */ } }, { "timeframe": "1h", "score": -0.25, "direction": "NEUTRAL", "strength": 25, "components": { /* ... */ } }, { "timeframe": "4h", "score": -0.50, "direction": "BEARISH", "strength": 50, "components": { /* ... */ } }, { "timeframe": "1d", "score": -0.75, "direction": "BEARISH", "strength": 75, "components": { /* ... */ } } ], "overall": { "score": 0.00, "direction": "CONFLICTED", "alignment": "LOW", // ALIGNED / MODERATE / LOW / CONFLICTED "recommendation": "WAIT - Higher timeframes bearish, lower bullish. Wait for alignment." } } ``` --- ## 5. Frontend Component ### 5.1 Component Code ```tsx // apps/frontend/src/modules/trading/components/MarketBiasIndicator.tsx import React, { useEffect, useState } from 'react'; import { TrendingUp, TrendingDown, Minus, AlertTriangle, CheckCircle2 } from 'lucide-react'; import { apiClient } from '@/lib/apiClient'; interface BiasData { timeframe: string; score: number; direction: 'BULLISH' | 'BEARISH' | 'NEUTRAL'; strength: number; components: { ma: number; rsi: number; macd: number; priceAction: number; }; } interface MarketBiasResponse { symbol: string; biases: BiasData[]; overall: { score: number; direction: string; alignment: 'ALIGNED' | 'MODERATE' | 'LOW' | 'CONFLICTED'; recommendation: string; }; } interface MarketBiasIndicatorProps { symbol: string; timeframes?: string[]; onBiasChange?: (overall: any) => void; refreshInterval?: number; // Refresh every N seconds } export const MarketBiasIndicator: React.FC = ({ symbol, timeframes = ['1m', '5m', '15m', '1h', '4h', '1d'], onBiasChange, refreshInterval = 60 // Default 60 seconds }) => { const [biasData, setBiasData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { fetchBias(); // Auto-refresh const interval = setInterval(() => { fetchBias(); }, refreshInterval * 1000); return () => clearInterval(interval); }, [symbol, refreshInterval]); const fetchBias = async () => { setLoading(true); setError(null); try { const response = await apiClient.get(`/api/market-bias/${symbol}`, { params: { timeframes: timeframes.join(',') } }); setBiasData(response.data); onBiasChange?.(response.data.overall); } catch (err) { console.error('Error fetching market bias:', err); setError('Failed to load market bias'); } finally { setLoading(false); } }; const getBiasColor = (direction: string) => { switch (direction) { case 'BULLISH': return 'text-green-400 bg-green-500/10 border-green-500/30'; case 'BEARISH': return 'text-red-400 bg-red-500/10 border-red-500/30'; case 'NEUTRAL': return 'text-yellow-400 bg-yellow-500/10 border-yellow-500/30'; default: return 'text-gray-400 bg-gray-500/10 border-gray-500/30'; } }; const getBiasIcon = (direction: string) => { switch (direction) { case 'BULLISH': return ; case 'BEARISH': return ; default: return ; } }; const getAlignmentIcon = (alignment: string) => { if (alignment === 'ALIGNED') return ; if (alignment === 'CONFLICTED') return ; return ; }; if (loading && !biasData) { return (
Loading market bias...
); } if (error) { return (
{error}
); } if (!biasData) return null; return (
{/* Header */}

Market Bias

{symbol} - Multi-Timeframe Analysis

{getAlignmentIcon(biasData.overall.alignment)}
{/* Overall Bias */}
{getBiasIcon(biasData.overall.direction)} {biasData.overall.direction}
{biasData.overall.alignment} Alignment

{biasData.overall.recommendation}

{/* Timeframe Grid */}
{biasData.biases.map((bias) => (
{bias.timeframe} {getBiasIcon(bias.direction)}
{/* Strength Bar */}
{bias.strength.toFixed(0)}% strength
))}
{/* Component Breakdown (Expandable) */}
View Component Breakdown
{biasData.biases.map((bias) => (
{bias.timeframe}
MA: 0 ? 'text-green-400' : 'text-red-400'}> {' '}{bias.components.ma.toFixed(2)}
RSI: 0 ? 'text-green-400' : 'text-red-400'}> {' '}{bias.components.rsi.toFixed(2)}
MACD: 0 ? 'text-green-400' : 'text-red-400'}> {' '}{bias.components.macd.toFixed(2)}
PA: 0 ? 'text-green-400' : 'text-red-400'}> {' '}{bias.components.priceAction.toFixed(2)}
))}
); }; export default MarketBiasIndicator; ``` --- ## 6. Uso e IntegraciΓ³n ### 6.1 Standalone ```tsx import MarketBiasIndicator from '@/modules/trading/components/MarketBiasIndicator'; function TradingDashboard() { return (
{ console.log('Overall bias changed:', overall.direction); }} />
); } ``` ### 6.2 Integration with Trading Alerts ```tsx function TradingPage() { const handleBiasChange = (overall: any) => { if (overall.alignment === 'ALIGNED') { showNotification(`${overall.direction} bias aligned across timeframes!`); } }; return ( ); } ``` --- ## 7. Roadmap de Implementación ### Fase 1: Backend API (1h) 1. Crear endpoint `/api/market-bias/:symbol` 2. Implementar cÑlculo de indicadores (EMA, RSI, MACD) 3. Implementar scoring logic 4. Test con múltiples símbolos ### Fase 2: Frontend Component (1h) 1. Crear `MarketBiasIndicator.tsx` 2. Grid de timeframes con color coding 3. Overall bias display 4. Component breakdown (expandable) 5. Auto-refresh logic --- ## 8. Referencias - **Componente:** `apps/frontend/src/modules/trading/components/MarketBiasIndicator.tsx` (a crear) - **Backend:** `apps/backend/src/services/marketBias.service.ts` (a crear) - **US:** `US-TRD-011-market-bias-indicator.md` --- **Última actualización:** 2026-01-25 **Responsable:** Frontend Lead + ML Engineer