Impulse-MACD/Impulse_MACD.mq4

628 lignes
25 Kio
MQL4
Brut Lien permanent Vue normale Historique

2026-02-22 17:46:05 -06:00
//╔════════════════════════════════════════════════════════════════════════╗
//║ ██████╗██╗ ██╗ █████╗ ██████╗ ██████╗ █████╗ █████╗ █████╗ ███╗ ██╗ ║
//║ ██╔═══╝╚██╗██╔╝██╔══██╗██╔══██╗██╔═══╝██╔══██╗██╔═══╝██╔══██╗████╗ ██║ ║
//║ █████╗ ╚███╔╝ ██║ ██║██████╔╝█████╗ ███████║██║ ██║ ██║██╔██╗██║ ║
//║ ██╔══╝ ██╔██╗ ██║ ██║██╔══██╗██╔══╝ ██╔══██║██║ ██║ ██║██║╚████║ ║
//║ ██████╗██╔╝ ██╗╚█████╔╝██████╔╝██████╗██║ ██║╚█████╗╚█████╔╝██║ ╚███║ ║
//║ ╚═════╝╚═╝ ╚═╝ ╚════╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝ ╚════╝ ╚════╝ ╚═╝ ╚══╝ ║
//╚═══════ 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<lo ? red : orange
double src_val = BufHLC3[i];
double mi = 2.0 * BufEMA1[i] - BufEMA2[i];
double hi_val = BufSmmaHigh[i];
double lo_val = BufSmmaLow[i];
BufLime[i] = EMPTY_VALUE;
BufGreen[i] = EMPTY_VALUE;
BufOrange[i] = EMPTY_VALUE;
BufRed[i] = EMPTY_VALUE;
if(src_val > 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) : (mi<lo) ? (mi - lo) : 0
// sb=sma(md, lengthSignal)
// sh=md-sb
// mdc=src>mi?src>hi?lime:green:src<lo?red:orange
//
// plot(0, color=gray, linewidth=1, title="MidLine")
// plot(md, color=mdc, linewidth=2, title="ImpulseMACD", style=histogram)
// plot(sh, color=blue, linewidth=2, title="ImpulseHisto", style=histogram)
// plot(sb, color=maroon, linewidth=2, title="ImpulseMACDCDSignal")
//
// ebc=input(false, title="Enable bar colors")
// barcolor(ebc?mdc:na)
//
// ============================================================
//
// NOTAS DE CONVERSION Pine Script --> 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).
//
// ============================================================