//╔════════════════════════════════════════════════════════════════════════╗ //║ ██████╗██╗ ██╗ █████╗ ██████╗ ██████╗ █████╗ █████╗ █████╗ ███╗ ██╗ ║ //║ ██╔═══╝╚██╗██╔╝██╔══██╗██╔══██╗██╔═══╝██╔══██╗██╔═══╝██╔══██╗████╗ ██║ ║ //║ █████╗ ╚███╔╝ ██║ ██║██████╔╝█████╗ ███████║██║ ██║ ██║██╔██╗██║ ║ //║ ██╔══╝ ██╔██╗ ██║ ██║██╔══██╗██╔══╝ ██╔══██║██║ ██║ ██║██║╚████║ ║ //║ ██████╗██╔╝ ██╗╚█████╔╝██████╔╝██████╗██║ ██║╚█████╗╚█████╔╝██║ ╚███║ ║ //║ ╚═════╝╚═╝ ╚═╝ ╚════╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝ ╚════╝ ╚════╝ ╚═╝ ╚══╝ ║ //╚═══════ Algorithms that observe ════ Signals that speak ════════════════╝ // // Archivo : Impulse_MACD.mq4 // 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 // Version : 1.2 (correccion critica: semilla EMA2 = EMA1[seedIdx]) // // Descripcion de la logica: // El indicador combina una DEMA/ZLEMA rapida (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 senal es una SMA del ImpulseMACD. El color del histograma refleja // la posicion del precio (HLC3) respecto a la ZLEMA y al canal SMMA. // // Estructura de calculo: // 1. HLC3 = (High + Low + Close) / 3 // 2. hi = SMMA(High, N) --> techo del canal // 3. lo = SMMA(Low, N) --> suelo del canal // 4. EMA1 = EMA(HLC3, N) // 5. EMA2 = EMA(EMA1, N) <-- semilla = EMA1[seedIdx], NO sma de EMA1 // 6. ZLEMA = 2*EMA1 - EMA2 --> identico a DEMA de Mulloy // 7. BufMD[] = filtro de canal sobre ZLEMA (buffer dedicado) // 8. BufSignal[]= SMA(BufMD, signalPeriod) // 9. BufHisto[] = BufMD - BufSignal // 10. Color = segun posicion HLC3 vs ZLEMA y canal SMMA // // NOTA SOBRE INDEXACION EN MQL4: // Close[0] = barra actual (mas reciente, puede estar en formacion) // Close[Bars-1] = barra mas antigua del historial cargado // Todos los bucles van de indice ALTO (pasado) a BAJO (presente). // Las funciones recursivas usan [i+1] como "valor de la barra anterior". // // HISTORIAL DE CORRECCIONES: // v1.1: Se agrego BufMD[] como buffer separado para evitar que la SMA // leyera EMPTY_VALUE al usar BufHisto como almacen temporal. // v1.2: BUG CRITICO CORREGIDO. La semilla de EMA2 usaba SMA de // BufEMA1[seedIdx..seedIdx+N-1], pero esas posiciones estan en 0.0 // porque SetIndexBuffer inicializa todo a 0.0 y InitEMA solo escribe // BufEMA1[0..seedIdx]. La semilla incorrecta era ~0.035 en vez de ~1.18, // produciendo ZLEMA inicial de ~2.33 y md de ~1.15 (la "linea a 1.25"). // SOLUCION: la semilla de EMA2 = BufEMA1[seedIdx] (valor puntual). // Esto replica la inicializacion de Pine Script donde en la primera barra // valida EMA2 = EMA1, y luego divergen por la recursion. // #property copyright "Exobeacon Labs" #property link "https://www.exobeacon.com/" #property version "1.2" #property description "Impulse MACD [LazyBear] v1.2" #property description "MACD with SMMA channel filter - eliminates whipsaw in ranging markets" #property description "ImpulseMACD = distance from ZLEMA (DEMA) to SMMA(H/L) channel boundary" #property description "Signal = SMA(ImpulseMACD). Color encodes price position vs channel." #property description "Based on LazyBear original Pine Script (TradingView, 2015)" #property description " " #property description "———————————————————————" #property description "mql5.com/en/users/ulisescalb" #property description "github.com/Exobeacon-Labs" #property strict //--- Indicador en subventana separada #property indicator_separate_window //--- 12 buffers: 6 visibles + 6 de calculo interno #property indicator_buffers 12 // ============================================================ // CONFIGURACION DE PLOTS // ============================================================ //--- [0] Alcista fuerte: HLC3 > ZLEMA y HLC3 > SMMA_High #property indicator_type1 DRAW_HISTOGRAM #property indicator_color1 Lime #property indicator_width1 2 #property indicator_label1 "ImpulseMACD Bullish Strong" //--- [1] Alcista moderado: HLC3 > ZLEMA pero HLC3 <= SMMA_High #property indicator_type2 DRAW_HISTOGRAM #property indicator_color2 Green #property indicator_width2 2 #property indicator_label2 "ImpulseMACD Bullish" //--- [2] Bajista moderado: HLC3 <= ZLEMA pero HLC3 >= SMMA_Low #property indicator_type3 DRAW_HISTOGRAM #property indicator_color3 DarkOrange #property indicator_width3 2 #property indicator_label3 "ImpulseMACD Bearish" //--- [3] Bajista fuerte: HLC3 <= ZLEMA y HLC3 < SMMA_Low #property indicator_type4 DRAW_HISTOGRAM #property indicator_color4 Red #property indicator_width4 2 #property indicator_label4 "ImpulseMACD Bearish Strong" //--- [4] Linea de senal: SMA del ImpulseMACD #property indicator_type5 DRAW_LINE #property indicator_color5 Maroon #property indicator_width5 2 #property indicator_label5 "Signal" //--- [5] Histograma de diferencia: ImpulseMACD - Signal #property indicator_type6 DRAW_HISTOGRAM #property indicator_color6 DodgerBlue #property indicator_width6 2 #property indicator_label6 "ImpulseHisto" // ============================================================ // INPUTS // ============================================================ //--- Configuracion del indicador extern int InpLengthMA = 34; // MA Period (SMMA channel + ZLEMA) extern int InpLengthSignal = 9; // Signal Period (SMA of ImpulseMACD) //--- Sistema de alertas extern bool InpEnableAlerts = false; // Enable alerts extern bool InpAlertPopup = true; // Alert: popup extern bool InpAlertSound = false; // Alert: sound extern bool InpAlertPush = false; // Alert: push notification extern bool InpAlertEmail = false; // Alert: email extern string InpSoundFile = "alert.wav"; // Sound file name // ============================================================ // BUFFERS DE PLOT (indices 0-5, visibles) // ============================================================ double BufLime[]; // [0] Histograma alcista fuerte double BufGreen[]; // [1] Histograma alcista moderado double BufOrange[]; // [2] Histograma bajista moderado double BufRed[]; // [3] Histograma bajista fuerte double BufSignal[]; // [4] Linea de senal double BufHisto[]; // [5] Diferencia (md - sb) // ============================================================ // BUFFERS DE CALCULO INTERNO (indices 6-11, ocultos) // ============================================================ double BufHLC3[]; // [6] Precio tipico HLC3 double BufSmmaHigh[]; // [7] SMMA de High (techo del canal) double BufSmmaLow[]; // [8] SMMA de Low (suelo del canal) double BufEMA1[]; // [9] Primera EMA de HLC3 double BufEMA2[]; // [10] EMA de EMA1 (semilla corregida en v1.2) double BufMD[]; // [11] ImpulseMACD raw (buffer dedicado desde v1.1) // ============================================================ // VARIABLES GLOBALES // ============================================================ string g_shortName = ""; datetime g_lastAlertTime = 0; bool g_initialized = false; // ============================================================ // OnInit // ============================================================ int OnInit() { if(InpLengthMA < 2) { PrintFormat("ImpulseMACD: MA Period (%d) must be >= 2.", InpLengthMA); return(INIT_PARAMETERS_INCORRECT); } if(InpLengthSignal < 1) { PrintFormat("ImpulseMACD: Signal Period (%d) must be >= 1.", InpLengthSignal); return(INIT_PARAMETERS_INCORRECT); } //--- Asignar buffers de plot (visibles) SetIndexBuffer(0, BufLime); SetIndexBuffer(1, BufGreen); SetIndexBuffer(2, BufOrange); SetIndexBuffer(3, BufRed); SetIndexBuffer(4, BufSignal); SetIndexBuffer(5, BufHisto); //--- Asignar buffers de calculo (ocultos) SetIndexBuffer(6, BufHLC3); SetIndexBuffer(7, BufSmmaHigh); SetIndexBuffer(8, BufSmmaLow); SetIndexBuffer(9, BufEMA1); SetIndexBuffer(10, BufEMA2); SetIndexBuffer(11, BufMD); //--- CRITICO: ocultar los buffers de calculo interno. // En MQL4, SetIndexBuffer() asigna estilo DRAW_LINE negro por defecto. // A diferencia de MQL5 donde INDICATOR_CALCULATIONS los oculta // automaticamente, en MQL4 hay que llamar explicitamente a // SetIndexStyle(N, DRAW_NONE) para que no se dibujen en el grafico. // Sin esto, los buffers 6-11 aparecen como lineas negras cerca de // su valor numerico (~1.18 para EMA/SMMA en EURUSD), distorsionando // la escala de la ventana del indicador y aplastando el histograma. SetIndexStyle(6, DRAW_NONE); SetIndexStyle(7, DRAW_NONE); SetIndexStyle(8, DRAW_NONE); SetIndexStyle(9, DRAW_NONE); SetIndexStyle(10, DRAW_NONE); SetIndexStyle(11, DRAW_NONE); //--- Valor vacio: barras con este valor no se dibujan SetIndexEmptyValue(0, EMPTY_VALUE); SetIndexEmptyValue(1, EMPTY_VALUE); SetIndexEmptyValue(2, EMPTY_VALUE); SetIndexEmptyValue(3, EMPTY_VALUE); SetIndexEmptyValue(4, EMPTY_VALUE); SetIndexEmptyValue(5, EMPTY_VALUE); //--- Inicio de dibujo: ocultar barras de calentamiento SetIndexDrawBegin(0, InpLengthMA - 1); SetIndexDrawBegin(1, InpLengthMA - 1); SetIndexDrawBegin(2, InpLengthMA - 1); SetIndexDrawBegin(3, InpLengthMA - 1); SetIndexDrawBegin(4, InpLengthMA + InpLengthSignal - 2); SetIndexDrawBegin(5, InpLengthMA + InpLengthSignal - 2); //--- Etiquetas del Data Window SetIndexLabel(0, "ImpulseMACD (Lime)"); SetIndexLabel(1, "ImpulseMACD (Green)"); SetIndexLabel(2, "ImpulseMACD (Orange)"); SetIndexLabel(3, "ImpulseMACD (Red)"); SetIndexLabel(4, "Signal"); SetIndexLabel(5, "ImpulseHisto"); SetIndexLabel(6, NULL); SetIndexLabel(7, NULL); SetIndexLabel(8, NULL); SetIndexLabel(9, NULL); SetIndexLabel(10, NULL); SetIndexLabel(11, NULL); //--- Nombre corto y precision g_shortName = "IMACD_LB(" + IntegerToString(InpLengthMA) + "," + IntegerToString(InpLengthSignal) + ")"; IndicatorShortName(g_shortName); IndicatorDigits(Digits + 2); //--- Nivel de referencia en cero SetLevelValue(0, 0.0); SetLevelStyle(STYLE_SOLID, 1, clrGray); g_initialized = false; return(INIT_SUCCEEDED); } // ============================================================ // OnDeinit // ============================================================ void OnDeinit(const int reason) { Comment(""); } // ============================================================ // start() // ============================================================ int start() { if(Bars < InpLengthMA + InpLengthSignal) return(0); int counted = IndicatorCounted(); if(counted < 0) return(-1); //--- Cuantas barras recalcular en este tick int limit; if(!g_initialized || counted == 0) limit = Bars - 1; else limit = MathMax(Bars - counted, 1); // ============================================================ // PASO 1: HLC3 // ============================================================ int hlcStart = (!g_initialized) ? (Bars - 1) : limit; for(int i = hlcStart; i >= 0; i--) BufHLC3[i] = (High[i] + Low[i] + Close[i]) / 3.0; // ============================================================ // PASO 2-5: SMMA de High/Low y EMA doble de HLC3 // ============================================================ if(!g_initialized) { InitSMMA(High, BufSmmaHigh, InpLengthMA); InitSMMA(Low, BufSmmaLow, InpLengthMA); InitEMA1(BufHLC3, BufEMA1, InpLengthMA); InitEMA2(BufEMA1, BufEMA2, InpLengthMA); // <-- usa semilla corregida g_initialized = true; } else { UpdateSMMA(High, BufSmmaHigh, InpLengthMA, limit); UpdateSMMA(Low, BufSmmaLow, InpLengthMA, limit); UpdateEMA (BufHLC3, BufEMA1, InpLengthMA, limit); UpdateEMA (BufEMA1, BufEMA2, InpLengthMA, limit); } // ============================================================ // PASO 6: ImpulseMACD raw → BufMD[] (buffer dedicado) // ============================================================ int seedIdx = Bars - InpLengthMA; //--- Zona de calentamiento: sin datos validos for(int i = Bars - 1; i >= seedIdx; i--) BufMD[i] = 0.0; //--- Calcular md para todas las barras validas for(int i = seedIdx - 1; i >= 0; i--) { double mi = 2.0 * BufEMA1[i] - BufEMA2[i]; double hi_val = BufSmmaHigh[i]; double lo_val = BufSmmaLow[i]; if(mi > hi_val) BufMD[i] = mi - hi_val; else if(mi < lo_val) BufMD[i] = mi - lo_val; else BufMD[i] = 0.0; } // ============================================================ // PASO 7-9: Signal, Histo y colores // ============================================================ int firstSig = seedIdx - (InpLengthSignal - 1); if(firstSig < 0) firstSig = 0; //--- Limpiar zona de calentamiento for(int i = Bars - 1; i >= firstSig; i--) { BufLime[i] = EMPTY_VALUE; BufGreen[i] = EMPTY_VALUE; BufOrange[i] = EMPTY_VALUE; BufRed[i] = EMPTY_VALUE; BufSignal[i] = EMPTY_VALUE; BufHisto[i] = EMPTY_VALUE; } //--- Calculo principal en un unico pase for(int i = firstSig - 1; i >= 0; i--) { //--- Signal = SMA(BufMD, N) // BufMD[i+j]: j=0 es la barra actual, j=N-1 es la mas antigua del rango. // En MQL4, indice mayor = mas antigua, por eso la ventana es [i .. i+N-1]. double sumMD = 0.0; for(int j = 0; j < InpLengthSignal; j++) sumMD += BufMD[i + j]; double sb = sumMD / (double)InpLengthSignal; BufSignal[i] = sb; double md = BufMD[i]; BufHisto[i] = md - sb; //--- Logica de color: replica exacta del Pine Script // mdc = src>mi ? src>hi ? lime : green : src mi) { if(src_val > hi_val) BufLime[i] = md; // Lime: alcista fuerte else BufGreen[i] = md; // Green: alcista moderado } else { if(src_val < lo_val) BufRed[i] = md; // Red: bajista fuerte else BufOrange[i] = md; // Orange: bajista moderado } } // ============================================================ // PASO 10: Alertas // ============================================================ if(InpEnableAlerts && Bars > 2 && firstSig > 1) { int lastClosed = 1; // indice 1 = ultima vela cerrada en MQL4 if(Time[lastClosed] != g_lastAlertTime && BufSignal[lastClosed] != EMPTY_VALUE) { double md_cur = BufMD[lastClosed]; double md_prev = BufMD[lastClosed + 1]; double sb_cur = BufSignal[lastClosed]; double sb_prev = BufSignal[lastClosed + 1]; string alertMsg = ""; if(md_prev < sb_prev && md_cur >= sb_cur) alertMsg = StringFormat("%s | %s %s | Bullish crossover: ImpulseMACD crossed above Signal", g_shortName, Symbol(), GetPeriodString()); else if(md_prev > sb_prev && md_cur <= sb_cur) alertMsg = StringFormat("%s | %s %s | Bearish crossunder: ImpulseMACD crossed below Signal", g_shortName, Symbol(), GetPeriodString()); else if(md_prev <= 0.0 && md_cur > 0.0) alertMsg = StringFormat("%s | %s %s | Bullish impulse: ImpulseMACD crossed above zero", g_shortName, Symbol(), GetPeriodString()); else if(md_prev >= 0.0 && md_cur < 0.0) alertMsg = StringFormat("%s | %s %s | Bearish impulse: ImpulseMACD crossed below zero", g_shortName, Symbol(), GetPeriodString()); 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(0); } // ============================================================ // FUNCION: InitSMMA // Inicializa la SMMA completa desde la barra mas antigua. // // En MQL4, indice mayor = barra mas antigua: // src[Bars-1] = mas antigua, src[0] = mas reciente // // La semilla SMA usa las (period) barras mas antiguas disponibles: // src[seedIdx], src[seedIdx+1], ..., src[Bars-1] // donde seedIdx = Bars - period // La recursion avanza hacia el presente (indices decrecientes). // ============================================================ void InitSMMA(const double &src[], double &dst[], int period) { int seedIdx = Bars - period; if(seedIdx <= 0) return; double sum = 0.0; for(int j = 0; j < period; j++) sum += src[seedIdx + j]; dst[seedIdx] = sum / (double)period; for(int i = seedIdx - 1; i >= 0; i--) dst[i] = (dst[i + 1] * (double)(period - 1) + src[i]) / (double)period; } // ============================================================ // FUNCION: UpdateSMMA // Actualizacion incremental de SMMA. // ============================================================ void UpdateSMMA(const double &src[], double &dst[], int period, int limit) { for(int i = limit; i >= 0; i--) dst[i] = (dst[i + 1] * (double)(period - 1) + src[i]) / (double)period; } // ============================================================ // FUNCION: InitEMA1 // Inicializa la primera EMA (EMA de HLC3) desde la barra mas antigua. // Semilla = SMA de las (period) barras mas antiguas disponibles. // Esta es la inicializacion estandar, correcta para la primera EMA. // ============================================================ void InitEMA1(const double &src[], double &dst[], int period) { int seedIdx = Bars - period; if(seedIdx <= 0) return; const double k = 2.0 / (double)(period + 1); //--- Semilla: SMA de las (period) barras mas antiguas de src double sum = 0.0; for(int j = 0; j < period; j++) sum += src[seedIdx + j]; dst[seedIdx] = sum / (double)period; for(int i = seedIdx - 1; i >= 0; i--) dst[i] = src[i] * k + dst[i + 1] * (1.0 - k); } // ============================================================ // FUNCION: InitEMA2 [CORRECCION CRITICA v1.2] // Inicializa la segunda EMA (EMA de EMA1) desde la barra mas antigua. // // PROBLEMA de v1.0/v1.1: // SetIndexBuffer() inicializa BufEMA1[] a 0.0 en todas sus posiciones. // InitEMA1() solo escribe BufEMA1[0..seedIdx]. Los indices [seedIdx+1..Bars-1] // permanecen en 0.0. // Si se usa la misma logica de semilla que InitEMA1: // semilla_EMA2 = mean(BufEMA1[seedIdx..seedIdx+period-1]) // entonces: BufEMA1[seedIdx] ≈ 1.18 pero BufEMA1[seedIdx+1..Bars-1] = 0.0 // Resultado: semilla_EMA2 = (1.18 + 0*33) / 34 ≈ 0.035 (INCORRECTO) // ZLEMA inicial = 2*1.18 - 0.035 ≈ 2.33 → escala gigantesca en el grafico. // // SOLUCION: // Usar BufEMA1[seedIdx] (valor puntual) como semilla de EMA2. // Esto replica el comportamiento de Pine Script, donde en la primera // barra valida EMA2 = EMA1 (sin historial previo de EMA1), y luego // la recursion los hace divergir gradualmente. // Con esta semilla: ZLEMA inicial ≈ 2*1.18 - 1.18 = 1.18 (correcto). // ============================================================ void InitEMA2(const double &ema1[], double &dst[], int period) { int seedIdx = Bars - period; if(seedIdx <= 0) return; const double k = 2.0 / (double)(period + 1); //--- CLAVE: semilla = valor puntual de EMA1 en seedIdx // NO usar SMA de ema1[seedIdx+j] porque esas posiciones son 0.0 dst[seedIdx] = ema1[seedIdx]; for(int i = seedIdx - 1; i >= 0; i--) dst[i] = ema1[i] * k + dst[i + 1] * (1.0 - k); } // ============================================================ // FUNCION: UpdateEMA // Actualizacion incremental de EMA (valida para EMA1 y EMA2). // En modo incremental, dst[limit+1] ya tiene un valor correcto, // por lo que no hay problema de posiciones no inicializadas. // ============================================================ void UpdateEMA(const double &src[], double &dst[], int period, int limit) { const double k = 2.0 / (double)(period + 1); for(int i = limit; i >= 0; i--) dst[i] = src[i] * k + dst[i + 1] * (1.0 - k); } // ============================================================ // FUNCION: GetPeriodString // ============================================================ string GetPeriodString() { switch(Period()) { case PERIOD_M1: return("M1"); case PERIOD_M5: return("M5"); case PERIOD_M15: return("M15"); case PERIOD_M30: return("M30"); case PERIOD_H1: return("H1"); case PERIOD_H4: return("H4"); case PERIOD_D1: return("D1"); case PERIOD_W1: return("W1"); case PERIOD_MN1: return("MN1"); default: return("TF" + IntegerToString(Period())); } } // ============================================================ // REFERENCIA: CODIGO 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/ // // 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 MQL4 (v1.2) // ───────────────────────────────────────────────── // // 1. BUG CRITICO (resuelto en v1.2) — Semilla incorrecta de EMA2: // MQL4 inicializa todos los elementos de un SetIndexBuffer a 0.0. // InitEMA1() escribe correctamente BufEMA1[0..seedIdx]. // Pero BufEMA1[seedIdx+1..Bars-1] permanece en 0.0 (zona de calentamiento). // Si la semilla de EMA2 usa esos indices (como hacia la formula SMA // de las N barras mas antiguas de EMA1), obtiene: // semilla = (EMA1_valido + 0 + 0 + ... + 0) / N ≈ 0.035 // Entonces: ZLEMA = 2*EMA1 - 0.035 ≈ 2.33 para EURUSD // Esto produce md = 2.33 - 1.18 ≈ 1.15, que es la "linea a 1.25" del grafico. // SOLUCION: semilla_EMA2 = EMA1[seedIdx] (valor puntual, no SMA). // Con esto: ZLEMA_inicial ≈ 2*1.18 - 1.18 = 1.18 (escala correcta). // // 2. Dos funciones separadas para las EMAs: // InitEMA1() usa semilla SMA(src, N) — correcto para la primera EMA. // InitEMA2() usa semilla = ema1[seedIdx] — correcto para la doble EMA. // UpdateEMA() es comun para ambas en modo incremental (no hay problema // porque dst[limit+1] siempre tiene un valor valido en ese momento). // // 3. Histograma de cuatro colores: // MQL4 no tiene DRAW_COLOR_HISTOGRAM. Se usan 4 buffers DRAW_HISTOGRAM // independientes; solo uno tiene el valor de md por barra, los demas EMPTY_VALUE. // // 4. Buffer dedicado BufMD[]: // Separado de BufHisto[] para evitar que la SMA lea EMPTY_VALUE. // // 5. Coloracion de velas (barcolor): no implementada (MQL4 no tiene API directa). // // ============================================================