//╔════════════════════════════════════════════════════════════════════════╗ //║ ██████╗██╗ ██╗ █████╗ ██████╗ ██████╗ █████╗ █████╗ █████╗ ███╗ ██╗ ║ //║ ██╔═══╝╚██╗██╔╝██╔══██╗██╔══██╗██╔═══╝██╔══██╗██╔═══╝██╔══██╗████╗ ██║ ║ //║ █████╗ ╚███╔╝ ██║ ██║██████╔╝█████╗ ███████║██║ ██║ ██║██╔██╗██║ ║ //║ ██╔══╝ ██╔██╗ ██║ ██║██╔══██╗██╔══╝ ██╔══██║██║ ██║ ██║██║╚████║ ║ //║ ██████╗██╔╝ ██╗╚█████╔╝██████╔╝██████╗██║ ██║╚█████╗╚█████╔╝██║ ╚███║ ║ //║ ╚═════╝╚═╝ ╚═╝ ╚════╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝ ╚════╝ ╚════╝ ╚═╝ ╚══╝ ║ //╚═══════ Algorithms that observe ════ Signals that speak ════════════════╝ //+------------------------------------------------------------------+ //| Archivo: HeikinAshiTrendIndicator.mq4 | //| Origen: Port del indicador "Heikin Ashi Trend Indicator" | //| de dman103 (TradingView, Pine Script v4) | //| Referencia: https://www.tradingview.com/script/OULfXjkq- | //| Heikin-Ashi-Trend-Indicator/ | //| Algoritmo: 1) Calcula OHLC Heikin Ashi a partir de velas reales | //| 2) Suaviza los 4 componentes HA con Tilson T3 | //| 3) Compara T3(haClose) vs T3(haOpen) para tendencia | //| 4) Grafica marcadores por encima del precio (bajista) | //| o por debajo (alcista), estilo "Parabolic SAR" | //| 5) Detecta squeeze (convergencia) con colores claros | //| | //| Nota MQL4: DRAW_COLOR_LINE/ARROW no existen en MQL4. | //| - Lineas: 4 buffers DRAW_LINE independientes | //| - Marcadores: 4 buffers DRAW_ARROW independientes | //| Cada buffer = 1 color (bull, bull-sqz, bear, bear-sqz) | //+------------------------------------------------------------------+ #property copyright "Exobeacon Labs" #property link "https://www.exobeacon.com/" #property version "2.1" #property description "Heikin Ashi Trend Indicator (dman103) v2.1" #property description "Smoothed HA + Tilson T3. Multiple visual styles." #property description "SAR-style: above price (bear), below price (bull)." #property description " " #property description "———————————————————————" #property description "mql5.com/en/users/ulisescalb" #property description "github.com/Exobeacon-Labs" #property strict //--- Ventana del grafico principal #property indicator_chart_window //--- Buffers visibles: solo 4 plots //--- Los 28 buffers internos (HA + T3) se declaran con IndicatorBuffers() en OnInit #property indicator_buffers 4 //--- Colores por defecto para los 4 plots visibles #property indicator_color1 clrDeepSkyBlue // Bull fuerte #property indicator_color2 clrLightBlue // Bull squeeze #property indicator_color3 clrDeepPink // Bear fuerte #property indicator_color4 clrPink // Bear squeeze #property indicator_width1 2 #property indicator_width2 2 #property indicator_width3 2 #property indicator_width4 2 //+------------------------------------------------------------------+ //| ENUMERACIONES | //+------------------------------------------------------------------+ //--- Estilos visuales disponibles en MQL4 //--- Solo linea solida para estilos de linea (limitacion MQL4) enum ENUM_HAT_STYLE { HAT_STYLE_CROSS = 0, // Crosses (x) HAT_STYLE_DOT = 1, // Dots HAT_STYLE_LINE = 2, // Solid Line HAT_STYLE_ARROW = 3, // Arrows HAT_STYLE_DIAMOND = 4, // Large dots HAT_STYLE_SQUARE = 5, // Squares HAT_STYLE_CIRCLE = 6, // Empty squares HAT_STYLE_STAR = 7, // Diamonds HAT_STYLE_THUMBS = 8, // Thumbs }; //+------------------------------------------------------------------+ //| PARAMETROS DE ENTRADA | //+------------------------------------------------------------------+ //--- Tilson T3 extern int InpT3Period = 8; // T3 Period extern double InpVolumeFactor = 0.59; // T3 Volume Factor (0.0-1.0) //--- Squeeze extern double InpPercentSqueeze = 0.066; // Squeeze Threshold (%) [0=disabled] //--- Estilo visual extern ENUM_HAT_STYLE InpStyle = HAT_STYLE_DOT; // Drawing Style extern color InpBullColor = clrDeepSkyBlue; // Bull Color (strong) extern color InpBullSqzColor = clrLightBlue; // Bull Color (squeeze) extern color InpBearColor = clrDeepPink; // Bear Color (strong) extern color InpBearSqzColor = clrPink; // Bear Color (squeeze) extern bool InpUseBarColor = false; // Color Chart Bars //--- 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 //+------------------------------------------------------------------+ //| BUFFERS GLOBALES | //+------------------------------------------------------------------+ //--- 4 buffers de salida (uno por color/estado) double BuffBullStrong[]; // Index 0: Bull fuerte double BuffBullSqueeze[]; // Index 1: Bull squeeze double BuffBearStrong[]; // Index 2: Bear fuerte double BuffBearSqueeze[]; // Index 3: Bear squeeze //--- 4 buffers Heikin Ashi double BuffHaOpen[]; // Index 4 double BuffHaClose[]; // Index 5 double BuffHaHigh[]; // Index 6 double BuffHaLow[]; // Index 7 //--- T3 para haClose: 6 EMAs (indices 8-13) double e1c[], e2c[], e3c[], e4c[], e5c[], e6c[]; //--- T3 para haOpen: 6 EMAs (indices 14-19) double e1o[], e2o[], e3o[], e4o[], e5o[], e6o[]; //--- T3 para haHigh: 6 EMAs (indices 20-25) double e1h[], e2h[], e3h[], e4h[], e5h[], e6h[]; //--- T3 para haLow: 6 EMAs (indices 26-31) double e1l[], e2l[], e3l[], e4l[], e5l[], e6l[]; //+------------------------------------------------------------------+ //| VARIABLES GLOBALES | //+------------------------------------------------------------------+ //--- Coeficientes T3 double g_c1 = 0.0; double g_c2 = 0.0; double g_c3 = 0.0; double g_c4 = 0.0; //--- Factor EMA double g_alpha = 0.0; //--- Control de alertas datetime g_lastAlertTime = 0; //--- Flag de estilo linea bool g_isLineStyle = false; //+------------------------------------------------------------------+ //| OnInit | //+------------------------------------------------------------------+ int OnInit() { //--- Reservar 32 buffers en total (4 visibles + 28 internos) IndicatorBuffers(32); //--- Validar parametros if(InpT3Period < 1) { Print("Error: T3 Period must be >= 1. Current: ", InpT3Period); return(INIT_PARAMETERS_INCORRECT); } if(InpVolumeFactor < 0.0 || InpVolumeFactor > 1.0) { Print("Error: Volume Factor must be 0.0-1.0. Current: ", DoubleToString(InpVolumeFactor, 2)); return(INIT_PARAMETERS_INCORRECT); } if(InpPercentSqueeze < 0.0) { Print("Error: Squeeze must be >= 0. Current: ", DoubleToString(InpPercentSqueeze, 2)); return(INIT_PARAMETERS_INCORRECT); } //=== ASIGNAR BUFFERS === //--- 4 buffers de salida visibles SetIndexBuffer(0, BuffBullStrong); SetIndexBuffer(1, BuffBullSqueeze); SetIndexBuffer(2, BuffBearStrong); SetIndexBuffer(3, BuffBearSqueeze); //--- 4 buffers HA (internos) SetIndexBuffer(4, BuffHaOpen); SetIndexBuffer(5, BuffHaClose); SetIndexBuffer(6, BuffHaHigh); SetIndexBuffer(7, BuffHaLow); //--- T3 haClose SetIndexBuffer(8, e1c); SetIndexBuffer(9, e2c); SetIndexBuffer(10, e3c); SetIndexBuffer(11, e4c); SetIndexBuffer(12, e5c); SetIndexBuffer(13, e6c); //--- T3 haOpen SetIndexBuffer(14, e1o); SetIndexBuffer(15, e2o); SetIndexBuffer(16, e3o); SetIndexBuffer(17, e4o); SetIndexBuffer(18, e5o); SetIndexBuffer(19, e6o); //--- T3 haHigh SetIndexBuffer(20, e1h); SetIndexBuffer(21, e2h); SetIndexBuffer(22, e3h); SetIndexBuffer(23, e4h); SetIndexBuffer(24, e5h); SetIndexBuffer(25, e6h); //--- T3 haLow SetIndexBuffer(26, e1l); SetIndexBuffer(27, e2l); SetIndexBuffer(28, e3l); SetIndexBuffer(29, e4l); SetIndexBuffer(30, e5l); SetIndexBuffer(31, e6l); //--- Ocultar todos los buffers internos (indices 4-31) //--- Sin esto, MQL4 los dibuja como lineas negras por defecto for(int b = 4; b < 32; b++) SetIndexStyle(b, DRAW_NONE); //=== CONFIGURAR ESTILO VISUAL === g_isLineStyle = (InpStyle == HAT_STYLE_LINE); if(g_isLineStyle) { //--- Modo linea: 4 buffers DRAW_LINE (bull, bull-sqz, bear, bear-sqz) SetIndexStyle(0, DRAW_LINE, STYLE_SOLID, 2, InpBullColor); SetIndexStyle(1, DRAW_LINE, STYLE_SOLID, 2, InpBullSqzColor); SetIndexStyle(2, DRAW_LINE, STYLE_SOLID, 2, InpBearColor); SetIndexStyle(3, DRAW_LINE, STYLE_SOLID, 2, InpBearSqzColor); } else { //--- Modo marcador: 4 buffers DRAW_ARROW SetIndexStyle(0, DRAW_ARROW, EMPTY, 2, InpBullColor); SetIndexStyle(1, DRAW_ARROW, EMPTY, 2, InpBullSqzColor); SetIndexStyle(2, DRAW_ARROW, EMPTY, 2, InpBearColor); SetIndexStyle(3, DRAW_ARROW, EMPTY, 2, InpBearSqzColor); //--- Seleccionar simbolos Wingdings int bullCode = 251; int bearCode = 251; switch(InpStyle) { case HAT_STYLE_CROSS: bullCode = 251; bearCode = 251; break; case HAT_STYLE_DOT: bullCode = 159; bearCode = 159; break; case HAT_STYLE_ARROW: bullCode = 233; bearCode = 234; break; case HAT_STYLE_DIAMOND: bullCode = 108; bearCode = 108; break; case HAT_STYLE_SQUARE: bullCode = 110; bearCode = 110; break; case HAT_STYLE_CIRCLE: bullCode = 113; bearCode = 113; break; case HAT_STYLE_STAR: bullCode = 117; bearCode = 117; break; case HAT_STYLE_THUMBS: bullCode = 67; bearCode = 68; break; } SetIndexArrow(0, bullCode); SetIndexArrow(1, bullCode); SetIndexArrow(2, bearCode); SetIndexArrow(3, bearCode); } //--- Etiquetas para la ventana de datos SetIndexLabel(0, "HAT Bull"); SetIndexLabel(1, "HAT Bull Sqz"); SetIndexLabel(2, "HAT Bear"); SetIndexLabel(3, "HAT Bear Sqz"); //--- Ocultar buffers internos de la ventana de datos for(int b = 4; b < 32; b++) SetIndexLabel(b, NULL); //--- Evitar basura en las primeras barras const int drawBegin = InpT3Period * 6; SetIndexDrawBegin(0, drawBegin); SetIndexDrawBegin(1, drawBegin); SetIndexDrawBegin(2, drawBegin); SetIndexDrawBegin(3, drawBegin); //--- Valor vacio SetIndexEmptyValue(0, EMPTY_VALUE); SetIndexEmptyValue(1, EMPTY_VALUE); SetIndexEmptyValue(2, EMPTY_VALUE); SetIndexEmptyValue(3, EMPTY_VALUE); //=== COEFICIENTES T3 === double a = InpVolumeFactor; double a2 = a * a; double a3 = a2 * a; g_c1 = -a3; g_c2 = 3.0 * a2 + 3.0 * a3; g_c3 = -(6.0 * a2 + 3.0 * a + 3.0 * a3); g_c4 = 1.0 + 3.0 * a + a3 + 3.0 * a2; g_alpha = 2.0 / (InpT3Period + 1.0); //--- Nombre corto IndicatorShortName("HAT(" + IntegerToString(InpT3Period) + ", " + DoubleToString(InpVolumeFactor, 2) + ")"); IndicatorDigits(Digits); g_lastAlertTime = 0; return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| 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[]) { if(rates_total < InpT3Period + 1) return(0); const double w1 = g_alpha; const double w2 = 1.0 - g_alpha; int limit; if(prev_calculated == 0) { //--- Inicializar barra mas antigua (indice rates_total-1 en modo series) //--- MQL4 buffers son series por defecto: indice 0 = barra mas reciente //--- Pero OnCalculate con arrays pasa datos como no-series (0=mas antigua) //--- Trabajamos con indexacion 0=mas antigua (igual que MQL5) int first = 0; BuffHaClose[first] = (open[first] + high[first] + low[first] + close[first]) / 4.0; BuffHaOpen[first] = (open[first] + close[first]) / 2.0; BuffHaHigh[first] = MathMax(high[first], MathMax(BuffHaOpen[first], BuffHaClose[first])); BuffHaLow[first] = MathMin(low[first], MathMin(BuffHaOpen[first], BuffHaClose[first])); e1c[first] = e2c[first] = e3c[first] = e4c[first] = e5c[first] = e6c[first] = BuffHaClose[first]; e1o[first] = e2o[first] = e3o[first] = e4o[first] = e5o[first] = e6o[first] = BuffHaOpen[first]; e1h[first] = e2h[first] = e3h[first] = e4h[first] = e5h[first] = e6h[first] = BuffHaHigh[first]; e1l[first] = e2l[first] = e3l[first] = e4l[first] = e5l[first] = e6l[first] = BuffHaLow[first]; BuffBullStrong[first] = EMPTY_VALUE; BuffBullSqueeze[first] = EMPTY_VALUE; BuffBearStrong[first] = EMPTY_VALUE; BuffBearSqueeze[first] = EMPTY_VALUE; limit = 1; } else { limit = prev_calculated - 1; } //--- === BUCLE PRINCIPAL === for(int i = limit; i < rates_total; i++) { //=== ETAPA 1: Heikin Ashi === BuffHaClose[i] = (open[i] + high[i] + low[i] + close[i]) / 4.0; BuffHaOpen[i] = (BuffHaOpen[i - 1] + BuffHaClose[i - 1]) / 2.0; BuffHaHigh[i] = MathMax(high[i], MathMax(BuffHaOpen[i], BuffHaClose[i])); BuffHaLow[i] = MathMin(low[i], MathMin(BuffHaOpen[i], BuffHaClose[i])); //=== ETAPA 2: T3 haClose === e1c[i] = w1 * BuffHaClose[i] + w2 * e1c[i - 1]; e2c[i] = w1 * e1c[i] + w2 * e2c[i - 1]; e3c[i] = w1 * e2c[i] + w2 * e3c[i - 1]; e4c[i] = w1 * e3c[i] + w2 * e4c[i - 1]; e5c[i] = w1 * e4c[i] + w2 * e5c[i - 1]; e6c[i] = w1 * e5c[i] + w2 * e6c[i - 1]; double t3Close = g_c1 * e6c[i] + g_c2 * e5c[i] + g_c3 * e4c[i] + g_c4 * e3c[i]; //=== ETAPA 3: T3 haOpen === e1o[i] = w1 * BuffHaOpen[i] + w2 * e1o[i - 1]; e2o[i] = w1 * e1o[i] + w2 * e2o[i - 1]; e3o[i] = w1 * e2o[i] + w2 * e3o[i - 1]; e4o[i] = w1 * e3o[i] + w2 * e4o[i - 1]; e5o[i] = w1 * e4o[i] + w2 * e5o[i - 1]; e6o[i] = w1 * e5o[i] + w2 * e6o[i - 1]; double t3Open = g_c1 * e6o[i] + g_c2 * e5o[i] + g_c3 * e4o[i] + g_c4 * e3o[i]; //=== ETAPA 4: T3 haHigh === e1h[i] = w1 * BuffHaHigh[i] + w2 * e1h[i - 1]; e2h[i] = w1 * e1h[i] + w2 * e2h[i - 1]; e3h[i] = w1 * e2h[i] + w2 * e3h[i - 1]; e4h[i] = w1 * e3h[i] + w2 * e4h[i - 1]; e5h[i] = w1 * e4h[i] + w2 * e5h[i - 1]; e6h[i] = w1 * e5h[i] + w2 * e6h[i - 1]; double t3High = g_c1 * e6h[i] + g_c2 * e5h[i] + g_c3 * e4h[i] + g_c4 * e3h[i]; //=== ETAPA 5: T3 haLow === e1l[i] = w1 * BuffHaLow[i] + w2 * e1l[i - 1]; e2l[i] = w1 * e1l[i] + w2 * e2l[i - 1]; e3l[i] = w1 * e2l[i] + w2 * e3l[i - 1]; e4l[i] = w1 * e3l[i] + w2 * e4l[i - 1]; e5l[i] = w1 * e4l[i] + w2 * e5l[i - 1]; e6l[i] = w1 * e5l[i] + w2 * e6l[i - 1]; double t3Low = g_c1 * e6l[i] + g_c2 * e5l[i] + g_c3 * e4l[i] + g_c4 * e3l[i]; //=== ETAPA 6: Tendencia === bool isBullish = (t3Close > t3Open); bool isBearish = (t3Open > t3Close); //=== ETAPA 7: Squeeze === bool isSqueeze = false; if(InpPercentSqueeze > 0.0) { double diff = MathAbs(t3Close - t3Open); double avgVal = (t3Close + t3Open) / 2.0; if(avgVal != 0.0) isSqueeze = ((diff / avgVal) * 100.0) < InpPercentSqueeze; } //=== ETAPA 8: Asignar a los 4 buffers de salida === //--- Limpiar todos primero BuffBullStrong[i] = EMPTY_VALUE; BuffBullSqueeze[i] = EMPTY_VALUE; BuffBearStrong[i] = EMPTY_VALUE; BuffBearSqueeze[i] = EMPTY_VALUE; if(isBullish) { if(isSqueeze) BuffBullSqueeze[i] = t3Low; // Bull squeeze: debajo del precio else BuffBullStrong[i] = t3Low; // Bull fuerte: debajo del precio } else if(isBearish) { if(isSqueeze) BuffBearSqueeze[i] = t3High; // Bear squeeze: encima del precio else BuffBearStrong[i] = t3High; // Bear fuerte: encima del precio } //=== ETAPA 9: Coloreo de velas === if(InpUseBarColor) { //--- En MQL4 no hay forma nativa de colorear velas individuales //--- desde un indicador. Se requeriria un objeto grafico por vela. //--- Omitido por rendimiento. } } //--- === ALERTAS === if(InpEnableAlerts && rates_total >= 3) { int last = rates_total - 2; int prev = last - 1; if(time[last] != g_lastAlertTime) { double prevT3C = g_c1 * e6c[prev] + g_c2 * e5c[prev] + g_c3 * e4c[prev] + g_c4 * e3c[prev]; double prevT3O = g_c1 * e6o[prev] + g_c2 * e5o[prev] + g_c3 * e4o[prev] + g_c4 * e3o[prev]; double lastT3C = g_c1 * e6c[last] + g_c2 * e5c[last] + g_c3 * e4c[last] + g_c4 * e3c[last]; double lastT3O = g_c1 * e6o[last] + g_c2 * e5o[last] + g_c3 * e4o[last] + g_c4 * e3o[last]; //--- Crossover alcista if((prevT3O > prevT3C) && (lastT3C > lastT3O)) { g_lastAlertTime = time[last]; SendAlertSignal("BUY", time[last]); } //--- Crossover bajista else if((prevT3C > prevT3O) && (lastT3O > lastT3C)) { g_lastAlertTime = time[last]; SendAlertSignal("SELL", time[last]); } } } return(rates_total); } //+------------------------------------------------------------------+ //| SendAlertSignal - Envia alerta multi-canal | //+------------------------------------------------------------------+ void SendAlertSignal(const string signal, const datetime barTime) { string prefix = "HAT(" + IntegerToString(InpT3Period) + ") "; string tf = ""; switch(Period()) { case PERIOD_M1: tf = "M1"; break; case PERIOD_M5: tf = "M5"; break; case PERIOD_M15: tf = "M15"; break; case PERIOD_M30: tf = "M30"; break; case PERIOD_H1: tf = "H1"; break; case PERIOD_H4: tf = "H4"; break; case PERIOD_D1: tf = "D1"; break; case PERIOD_W1: tf = "W1"; break; case PERIOD_MN1: tf = "MN"; break; default: tf = IntegerToString(Period()); break; } string msg = prefix + signal + " signal on " + Symbol() + " " + tf + " at " + TimeToString(barTime, TIME_DATE | TIME_MINUTES); if(InpAlertPopup) Alert(msg); if(InpAlertSound) { if(signal == "BUY") PlaySound("alert.wav"); else PlaySound("alert2.wav"); } if(InpAlertPush) SendNotification(msg); if(InpAlertEmail) SendMail("HAT Alert: " + signal + " " + Symbol(), msg); } //+------------------------------------------------------------------+ //| | //| REFERENCIA: Algoritmo original de dman103 | //| https://www.tradingview.com/script/OULfXjkq-Heikin-Ashi-Trend- | //| Indicator/ | //| | //| 1. haClose=(O+H+L+C)/4, haOpen=(haOpen[1]+haClose[1])/2 | //| 2. T3(src) = c1*e6+c2*e5+c3*e4+c4*e3 (6 EMAs cascada) | //| 3. Bull = T3(haClose)>T3(haOpen) -> plot at T3(haLow) | //| 4. Bear = T3(haOpen)>T3(haClose) -> plot at T3(haHigh) | //| 5. Squeeze = |diff|/avg*100 < threshold -> lighter color | //| | //| DIFERENCIAS MQL4 vs MQL5: | //| - No existe DRAW_COLOR_LINE/ARROW en MQL4 | //| - Se usan 4 buffers independientes (1 por color/estado) | //| - En modo linea con squeeze, si la tendencia cambia entre | //| fuerte y squeeze, la linea se interrumpe brevemente | //| (limitacion de usar buffers separados por color) | //| - extern en vez de input group | //| - Coloreo de velas no disponible nativamente en MQL4 | //| - EnumToString(Period()) no existe; se usa switch manual | //| | //+------------------------------------------------------------------+