628 lignes
25 Kio
MQL4
628 lignes
25 Kio
MQL4
|
|
//╔════════════════════════════════════════════════════════════════════════╗
|
||
|
|
//║ ██████╗██╗ ██╗ █████╗ ██████╗ ██████╗ █████╗ █████╗ █████╗ ███╗ ██╗ ║
|
||
|
|
//║ ██╔═══╝╚██╗██╔╝██╔══██╗██╔══██╗██╔═══╝██╔══██╗██╔═══╝██╔══██╗████╗ ██║ ║
|
||
|
|
//║ █████╗ ╚███╔╝ ██║ ██║██████╔╝█████╗ ███████║██║ ██║ ██║██╔██╗██║ ║
|
||
|
|
//║ ██╔══╝ ██╔██╗ ██║ ██║██╔══██╗██╔══╝ ██╔══██║██║ ██║ ██║██║╚████║ ║
|
||
|
|
//║ ██████╗██╔╝ ██╗╚█████╔╝██████╔╝██████╗██║ ██║╚█████╗╚█████╔╝██║ ╚███║ ║
|
||
|
|
//║ ╚═════╝╚═╝ ╚═╝ ╚════╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝ ╚════╝ ╚════╝ ╚═╝ ╚══╝ ║
|
||
|
|
//╚═══════ 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).
|
||
|
|
//
|
||
|
|
// ============================================================
|