//╔════════════════════════════════════════════════════════════════════════╗ //║ ██████╗██╗ ██╗ █████╗ ██████╗ ██████╗ █████╗ █████╗ █████╗ ███╗ ██╗ ║ //║ ██╔═══╝╚██╗██╔╝██╔══██╗██╔══██╗██╔═══╝██╔══██╗██╔═══╝██╔══██╗████╗ ██║ ║ //║ █████╗ ╚███╔╝ ██║ ██║██████╔╝█████╗ ███████║██║ ██║ ██║██╔██╗██║ ║ //║ ██╔══╝ ██╔██╗ ██║ ██║██╔══██╗██╔══╝ ██╔══██║██║ ██║ ██║██║╚████║ ║ //║ ██████╗██╔╝ ██╗╚█████╔╝██████╔╝██████╗██║ ██║╚█████╗╚█████╔╝██║ ╚███║ ║ //║ ╚═════╝╚═╝ ╚═╝ ╚════╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝ ╚════╝ ╚════╝ ╚═╝ ╚══╝ ║ //╚═══════ Algorithms that observe ════ Signals that speak ════════════════╝ // // Archivo : Impulse_MACD.mq5 // Origen : Port fiel del indicador "Impulse MACD [LazyBear]" de TradingView // URL ref. : https://www.tradingview.com/script/qt6xLfLi-Impulse-MACD-LazyBear/ // Autor : LazyBear (Pine Script original, 2015) // Port : Exobeacon Labs // // Descripción de la lógica: // El indicador combina una DEMA/ZLEMA rápida (sobre HLC3) con un canal // de doble SMMA (sobre High y Low). El ImpulseMACD vale la distancia // entre la ZLEMA y el borde del canal solo cuando la ZLEMA lo rompe. // Si la ZLEMA permanece dentro del canal, el valor es 0 (zona neutra). // La señal es una SMA del ImpulseMACD. El color del histograma refleja // la posición del precio (HLC3) respecto a la ZLEMA y al canal SMMA. // // Estructura de cálculo: // 1. HLC3 = (High + Low + Close) / 3 // 2. hi = SMMA(High, lengthMA) → techo del canal // 3. lo = SMMA(Low, lengthMA) → suelo del canal // 4. mi = ZLEMA(HLC3, lengthMA) → DEMA: 2*EMA1 - EMA2 // 5. md = (mi>hi)?(mi-hi) : (mi= 2. Initialization failed.", InpLengthMA); return(INIT_PARAMETERS_INCORRECT); } if(InpLengthSignal < 1) { PrintFormat("ImpulseMACD: Signal Period (%d) must be >= 1. Initialization failed.", InpLengthSignal); return(INIT_PARAMETERS_INCORRECT); } // ---- Asignación de buffers de plot ---- // Buffer 0: datos del histograma ImpulseMACD SetIndexBuffer(0, BufMACDData, INDICATOR_DATA); // Buffer 1: índices de color para el histograma SetIndexBuffer(1, BufMACDColor, INDICATOR_COLOR_INDEX); // Buffer 2: línea de señal SetIndexBuffer(2, BufSignal, INDICATOR_DATA); // Buffer 3: histograma de diferencia SetIndexBuffer(3, BufHisto, INDICATOR_DATA); // ---- Asignación de buffers de cálculo interno ---- SetIndexBuffer(4, BufHLC3, INDICATOR_CALCULATIONS); SetIndexBuffer(5, BufSmmaHigh, INDICATOR_CALCULATIONS); SetIndexBuffer(6, BufSmmaLow, INDICATOR_CALCULATIONS); SetIndexBuffer(7, BufEMA1, INDICATOR_CALCULATIONS); SetIndexBuffer(8, BufEMA2, INDICATOR_CALCULATIONS); SetIndexBuffer(9, BufZLEMA, INDICATOR_CALCULATIONS); // ---- Valores vacíos: usar EMPTY_VALUE para evitar basura visual ---- PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, EMPTY_VALUE); // ---- Inicio de dibujo (evita basura en las primeras barras) ---- // El ImpulseMACD requiere al menos (lengthMA) barras para el primer SMMA/EMA válido // La señal requiere además (lengthSignal - 1) barras adicionales PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, InpLengthMA); PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, InpLengthMA + InpLengthSignal - 1); PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, InpLengthMA + InpLengthSignal - 1); // ---- Nombre corto del indicador en la ventana ---- g_shortName = StringFormat("IMACD_LB(%d,%d)", InpLengthMA, InpLengthSignal); IndicatorSetString(INDICATOR_SHORTNAME, g_shortName); // ---- Dígitos de precisión ---- IndicatorSetInteger(INDICATOR_DIGITS, _Digits + 2); // ---- Nivel de referencia en cero ---- IndicatorSetInteger(INDICATOR_LEVELS, 1); IndicatorSetDouble(INDICATOR_LEVELVALUE, 0, 0.0); IndicatorSetInteger(INDICATOR_LEVELCOLOR, 0, clrGray); IndicatorSetInteger(INDICATOR_LEVELSTYLE, 0, STYLE_SOLID); IndicatorSetInteger(INDICATOR_LEVELWIDTH, 0, 1); return(INIT_SUCCEEDED); } // ============================================================ // OnDeinit // ============================================================ void OnDeinit(const int reason) { //--- Limpiar el comentario en el gráfico si se hubiera usado Comment(""); } // ============================================================ // OnCalculate // ============================================================ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { //--- Verificar mínimo de barras necesarias para un cálculo válido // Se necesita: lengthMA (para SMMA/EMA) + lengthSignal (para SMA de señal) if(rates_total < InpLengthMA + InpLengthSignal) return(0); // ============================================================ // PASO 1: Calcular HLC3 = (High + Low + Close) / 3 // ============================================================ // Si es recálculo completo, procesar desde la barra 0 // Si es actualización incremental, solo recalcular la última barra int startHLC3 = (prev_calculated > 0) ? (prev_calculated - 1) : 0; for(int i = startHLC3; i < rates_total; i++) BufHLC3[i] = (high[i] + low[i] + close[i]) / 3.0; // ============================================================ // PASO 2: SMMA de High → techo del canal (BufSmmaHigh) // PASO 3: SMMA de Low → suelo del canal (BufSmmaLow) // ============================================================ // SMMA: primer valor = SMA(N), luego SMMA[i] = (SMMA[i-1]*(N-1) + src[i]) / N CalcSMMA(high, BufSmmaHigh, InpLengthMA, rates_total, prev_calculated); CalcSMMA(low, BufSmmaLow, InpLengthMA, rates_total, prev_calculated); // ============================================================ // PASO 4: ZLEMA de HLC3 = 2*EMA1 - EMA2 (equivale a DEMA de Mulloy) // ============================================================ // EMA1 = EMA(HLC3, N) CalcEMA(BufHLC3, BufEMA1, InpLengthMA, rates_total, prev_calculated); // EMA2 = EMA(EMA1, N) — doble suavizado CalcEMA(BufEMA1, BufEMA2, InpLengthMA, rates_total, prev_calculated); // ZLEMA[i] = 2*EMA1[i] - EMA2[i] // Las primeras (lengthMA - 1) barras de EMA1 y EMA2 no son válidas int startZL = (prev_calculated > 0) ? (prev_calculated - 1) : (InpLengthMA - 1); for(int i = startZL; i < rates_total; i++) BufZLEMA[i] = 2.0 * BufEMA1[i] - BufEMA2[i]; // ============================================================ // PASO 5: ImpulseMACD (md) — filtro de canal // md = (mi > hi) ? (mi - hi) : (mi < lo) ? (mi - lo) : 0 // Anula la señal cuando la ZLEMA permanece dentro del canal SMMA // ============================================================ int startMD = (prev_calculated > 0) ? (prev_calculated - 1) : (InpLengthMA - 1); for(int i = startMD; i < rates_total; i++) { double mi = BufZLEMA[i]; double hi_val = BufSmmaHigh[i]; double lo_val = BufSmmaLow[i]; if(mi > hi_val) BufMACDData[i] = mi - hi_val; // Impulso alcista: ZLEMA sobre techo del canal else if(mi < lo_val) BufMACDData[i] = mi - lo_val; // Impulso bajista: ZLEMA bajo suelo del canal else BufMACDData[i] = 0.0; // Zona neutra: sin señal } // ============================================================ // PASO 6: Señal = SMA(ImpulseMACD, lengthSignal) // ============================================================ // El primer valor válido de md está en el índice (lengthMA - 1) // La señal necesita además (lengthSignal - 1) barras de md int firstValidMD = InpLengthMA - 1; int firstValidSig = firstValidMD + InpLengthSignal - 1; int startSig = (prev_calculated > 0) ? MathMax(firstValidSig, prev_calculated - 1) : firstValidSig; for(int i = startSig; i < rates_total; i++) { //--- SMA simple sobre la ventana de (lengthSignal) valores de md double sum = 0.0; for(int j = 0; j < InpLengthSignal; j++) sum += BufMACDData[i - j]; BufSignal[i] = sum / (double)InpLengthSignal; } // ============================================================ // PASO 7: Histograma de diferencia y lógica de colores // ============================================================ // El histograma sh = md - sb // El color depende de la posición de HLC3 respecto a mi y el canal: // src > mi && src > hi → índice 0 (Lime) alcista fuerte // src > mi && src <= hi → índice 1 (Green) alcista moderado // src <= mi && src >= lo→ índice 2 (Orange) bajista moderado // src <= mi && src < lo → índice 3 (Red) bajista fuerte int startFinal = (prev_calculated > 0) ? MathMax(firstValidSig, prev_calculated - 1) : firstValidSig; for(int i = startFinal; i < rates_total; i++) { //--- Histograma de diferencia BufHisto[i] = BufMACDData[i] - BufSignal[i]; //--- Variables locales para legibilidad double src_val = BufHLC3[i]; double mi = BufZLEMA[i]; double hi_val = BufSmmaHigh[i]; double lo_val = BufSmmaLow[i]; //--- Lógica de color (réplica exacta del Pine Script original): // mdc = src>mi ? src>hi ? lime : green : src mi) { if(src_val > hi_val) BufMACDColor[i] = 0; // Lime — alcista fuerte else BufMACDColor[i] = 1; // Green — alcista moderado } else { if(src_val < lo_val) BufMACDColor[i] = 3; // Red — bajista fuerte else BufMACDColor[i] = 2; // Orange — bajista moderado } } // ============================================================ // PASO 8: Sistema de alertas // ============================================================ if(InpEnableAlerts && rates_total >= 2) { //--- Solo alertar en la última vela CERRADA (índice rates_total - 2) // para evitar repintado en la vela actual int lastClosed = rates_total - 2; double md_cur = BufMACDData[lastClosed]; double md_prev = (lastClosed > 0) ? BufMACDData[lastClosed - 1] : 0.0; double sb_cur = BufSignal[lastClosed]; double sb_prev = (lastClosed > 0) ? BufSignal[lastClosed - 1] : 0.0; //--- Evitar alertas duplicadas en la misma vela if(time[lastClosed] != g_lastAlertTime) { string alertMsg = ""; //--- Cruce alcista: ImpulseMACD cruza sobre la línea de señal // crossover(md, sb): md_prev < sb_prev && md_cur >= sb_cur if(md_prev < sb_prev && md_cur >= sb_cur) { alertMsg = StringFormat("%s | %s %s | Bullish crossover: ImpulseMACD crossed above Signal", g_shortName, _Symbol, EnumToString((ENUM_TIMEFRAMES)Period())); } //--- Cruce bajista: ImpulseMACD cruza bajo la línea de señal // crossunder(md, sb): md_prev > sb_prev && md_cur <= sb_cur else if(md_prev > sb_prev && md_cur <= sb_cur) { alertMsg = StringFormat("%s | %s %s | Bearish crossunder: ImpulseMACD crossed below Signal", g_shortName, _Symbol, EnumToString((ENUM_TIMEFRAMES)Period())); } //--- Cruce de cero al alza: ImpulseMACD pasa de negativo/cero a positivo else if(md_prev <= 0.0 && md_cur > 0.0) { alertMsg = StringFormat("%s | %s %s | Bullish impulse: ImpulseMACD crossed above zero", g_shortName, _Symbol, EnumToString((ENUM_TIMEFRAMES)Period())); } //--- Cruce de cero a la baja: ImpulseMACD pasa de positivo a negativo/cero else if(md_prev >= 0.0 && md_cur < 0.0) { alertMsg = StringFormat("%s | %s %s | Bearish impulse: ImpulseMACD crossed below zero", g_shortName, _Symbol, EnumToString((ENUM_TIMEFRAMES)Period())); } //--- Disparar alerta si hay mensaje if(alertMsg != "") { g_lastAlertTime = time[lastClosed]; if(InpAlertPopup) Alert(alertMsg); if(InpAlertSound) PlaySound(InpSoundFile); if(InpAlertPush) SendNotification(alertMsg); if(InpAlertEmail) SendMail(g_shortName + " Alert", alertMsg); } } } return(rates_total); } // ============================================================ // FUNCIÓN: CalcSMMA // Calcula la SMMA (Smoothed Moving Average) recursiva. // Equivale a MODE_SMMA en MetaTrader / RMA en Pine Script. // // Fórmula: // - Primera barra válida (idx = period-1): SMMA = SMA(src, period) // - Barras siguientes: SMMA[i] = (SMMA[i-1] * (period-1) + src[i]) / period // // Parámetros: // src → array fuente (High o Low) // dst → array destino // period → período de suavizado // rates_total → número total de barras disponibles // prev_calc → barras calculadas en la llamada anterior // ============================================================ void CalcSMMA(const double &src[], double &dst[], int period, int rates_total, int prev_calc) { if(prev_calc == 0) { //--- Inicialización completa desde cero //--- La primera barra válida (idx = period-1) se inicializa con SMA simple double sum = 0.0; for(int j = 0; j < period; j++) sum += src[j]; dst[period - 1] = sum / (double)period; //--- Recursión SMMA desde la barra siguiente al primer valor válido for(int i = period; i < rates_total; i++) dst[i] = (dst[i - 1] * (double)(period - 1) + src[i]) / (double)period; } else { //--- Actualización incremental: solo recalcular desde la última barra conocida // El valor anterior (prev_calc - 2) ya fue calculado y es válido como semilla for(int i = prev_calc - 1; i < rates_total; i++) dst[i] = (dst[i - 1] * (double)(period - 1) + src[i]) / (double)period; } } // ============================================================ // FUNCIÓN: CalcEMA // Calcula la EMA (Exponential Moving Average) recursiva. // // Fórmula: // k = 2 / (period + 1) // - Primera barra válida (idx = period-1): EMA = SMA(src, period) // - Barras siguientes: EMA[i] = src[i]*k + EMA[i-1]*(1-k) // // Parámetros: // src → array fuente (puede ser BufHLC3 o BufEMA1) // dst → array destino // period → período de suavizado // rates_total → número total de barras disponibles // prev_calc → barras calculadas en la llamada anterior // // Nota: Se usa la misma lógica de inicialización que Pine Script, // que inicializa la primera barra de EMA con SMA(N). // ============================================================ void CalcEMA(const double &src[], double &dst[], int period, int rates_total, int prev_calc) { const double k = 2.0 / (double)(period + 1); if(prev_calc == 0) { //--- Primer valor válido = SMA simple de los primeros N elementos double sum = 0.0; for(int j = 0; j < period; j++) sum += src[j]; dst[period - 1] = sum / (double)period; //--- Recursión EMA estándar for(int i = period; i < rates_total; i++) dst[i] = src[i] * k + dst[i - 1] * (1.0 - k); } else { //--- Actualización incremental for(int i = prev_calc - 1; i < rates_total; i++) dst[i] = src[i] * k + dst[i - 1] * (1.0 - k); } } // ============================================================ // ============================================================ // // REFERENCIA: CÓDIGO PINE SCRIPT ORIGINAL // // Indicador: Impulse MACD [LazyBear] // Autor : LazyBear // Fecha : 29 de abril de 2015 // URL : https://www.tradingview.com/script/qt6xLfLi-Impulse-MACD-LazyBear/ // Versión : Pine Script v2 (sin anotación @version explícita) // Licencia : Código abierto en TradingView // // ============================================================ // // // // // @author LazyBear // // List of my public indicators: http://bit.ly/1LQaPK8 // // List of my app-store indicators: http://blog.tradingview.com/?p=970 // // // study("Impulse MACD [LazyBear]", shorttitle="IMACD_LB", overlay=false) // lengthMA = input(34) // lengthSignal = input(9) // // calc_smma(src, len) => // smma=na(smma[1]) ? sma(src, len) : (smma[1] * (len - 1) + src) / len // smma // // calc_zlema(src, length) => // ema1=ema(src, length) // ema2=ema(ema1, length) // d=ema1-ema2 // ema1+d // // src=hlc3 // hi=calc_smma(high, lengthMA) // lo=calc_smma(low, lengthMA) // mi=calc_zlema(src, lengthMA) // // md=(mi>hi)? (mi-hi) : (mimi?src>hi?lime:green:src