//╔════════════════════════════════════════════════════════════════════════╗ //║ ██████╗██╗ ██╗ █████╗ ██████╗ ██████╗ █████╗ █████╗ █████╗ ███╗ ██╗ ║ //║ ██╔═══╝╚██╗██╔╝██╔══██╗██╔══██╗██╔═══╝██╔══██╗██╔═══╝██╔══██╗████╗ ██║ ║ //║ █████╗ ╚███╔╝ ██║ ██║██████╔╝█████╗ ███████║██║ ██║ ██║██╔██╗██║ ║ //║ ██╔══╝ ██╔██╗ ██║ ██║██╔══██╗██╔══╝ ██╔══██║██║ ██║ ██║██║╚████║ ║ //║ ██████╗██╔╝ ██╗╚█████╔╝██████╔╝██████╗██║ ██║╚█████╗╚█████╔╝██║ ╚███║ ║ //║ ╚═════╝╚═╝ ╚═╝ ╚════╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝ ╚════╝ ╚════╝ ╚═╝ ╚══╝ ║ //╚═══════ Algorithms that observe ════ Signals that speak ════════════════╝ //+------------------------------------------------------------------+ //| Réplica fiel del indicador "ADX and DI" de BeikabuOyaji | //| TradingView Pine Script v4 → MQL4 | //| Fuente: tradingview.com/script/VTPMMOrx-ADX-and-DI/ | //| Licencia original: Mozilla Public License 2.0 | //+------------------------------------------------------------------+ //| NOTAS TÉCNICAS: | //| - Suavizado Wilder (α=1/N) para TR, +DM, -DM → DI+, DI- | //| - SMA simple (media aritmética ventana deslizante) para ADX | //| - Esta combinación híbrida NO coincide con el iADX nativo | //| de MetaTrader 4 (que usa EMA para todo el cálculo) | //| - Inicialización desde 0 (replica nz() de Pine Script) | //+------------------------------------------------------------------+ //| DIFERENCIAS MQL4 vs MQL5 implementadas: | //| - #property indicator_buffers = solo plots visibles (3) | //| - IndicatorBuffers() en init() para declarar el total (7) | //| - SetIndexBuffer() sin tercer parámetro de tipo | //| - SetIndexStyle() / SetIndexLabel() / SetIndexDrawBegin() | //| - SetLevelValue() / SetLevelStyle() para línea horizontal | //| - Arrays en modo series por defecto → ArraySetAsSeries(false) | //| - OnCalculate() compatible con MT4 build 600+ | //+------------------------------------------------------------------+ #property copyright "Exobeacon Labs" #property link "https://www.exobeacon.com/" #property version "1.0" #property description "ADX and DI (by BeikabuOyaji) v1.1" #property description "Exact replica of BeikabuOyaji's ADX and DI indicator." #property description "Uses Wilder smoothing for DI and SMA for ADX." #property description " " #property description "———————————————————————" #property description "mql5.com/en/users/ulisescalb" #property description "github.com/Exobeacon-Labs" #property strict #property indicator_separate_window #property indicator_buffers 3 // MQL4: número de plots visibles // El total real (7) se declara con // IndicatorBuffers(7) en OnInit() //--- Plot 0: DI+ (verde) #property indicator_color1 clrGreen #property indicator_style1 STYLE_SOLID #property indicator_width1 1 //--- Plot 1: DI- (rojo) #property indicator_color2 clrRed #property indicator_style2 STYLE_SOLID #property indicator_width2 1 //--- Plot 2: ADX (azul marino) #property indicator_color3 clrNavy #property indicator_style3 STYLE_SOLID #property indicator_width3 2 //--- Nivel horizontal #property indicator_level1 20 #property indicator_levelcolor clrBlack #property indicator_levelstyle STYLE_SOLID //+------------------------------------------------------------------+ //| Parámetros de entrada | //| len → InpPeriod (período de suavizado y SMA) | //| th → InpThreshold (línea horizontal de referencia) | //+------------------------------------------------------------------+ input int InpPeriod = 14; // Período (len) input int InpThreshold = 20; // Umbral horizontal (th) //+------------------------------------------------------------------+ //| Buffers globales | //+------------------------------------------------------------------+ //--- Buffers de datos (visibles en Data Window y accesibles vía iCustom) double DIPlusBuffer[]; // Buffer 0 → Plot 0: DI+ double DIMinusBuffer[]; // Buffer 1 → Plot 1: DI- double ADXBuffer[]; // Buffer 2 → Plot 2: ADX //--- Buffers de cálculo interno (no visibles, no accesibles vía iCustom) double SmoothedTRBuffer[]; // Buffer 3: True Range suavizado (Wilder) double SmoothedDMPlusBuffer[]; // Buffer 4: +DM suavizado (Wilder) double SmoothedDMMinusBuffer[]; // Buffer 5: -DM suavizado (Wilder) double DXBuffer[]; // Buffer 6: DX (entrada para SMA → ADX) //+------------------------------------------------------------------+ //| Inicialización del indicador | //+------------------------------------------------------------------+ int OnInit() { //--- Validación de parámetros if(InpPeriod < 1) { PrintFormat("ADXandDI: Error - Período (%d) debe ser >= 1", InpPeriod); return(INIT_PARAMETERS_INCORRECT); } //--- MQL4: declarar el número TOTAL de buffers (visibles + cálculo) // #property indicator_buffers solo declara los visibles (3) // IndicatorBuffers() establece el total real (7) IndicatorBuffers(7); //--- Asignar buffers visibles (índices 0-2: accesibles vía iCustom) SetIndexBuffer(0, DIPlusBuffer); SetIndexBuffer(1, DIMinusBuffer); SetIndexBuffer(2, ADXBuffer); //--- Asignar buffers de cálculo interno (índices 3-6: no accesibles) SetIndexBuffer(3, SmoothedTRBuffer); SetIndexBuffer(4, SmoothedDMPlusBuffer); SetIndexBuffer(5, SmoothedDMMinusBuffer); SetIndexBuffer(6, DXBuffer); //--- MQL4: configurar estilo de cada plot con SetIndexStyle() // Equivale a #property indicator_type + color + style + width en MQL5 SetIndexStyle(0, DRAW_LINE, STYLE_SOLID, 1, clrGreen); SetIndexStyle(1, DRAW_LINE, STYLE_SOLID, 1, clrRed); SetIndexStyle(2, DRAW_LINE, STYLE_SOLID, 2, clrNavy); //--- MQL4: etiquetas para Data Window (equivale a indicator_label en MQL5) SetIndexLabel(0, "DI+"); SetIndexLabel(1, "DI-"); SetIndexLabel(2, "ADX"); //--- Ocultar buffers de cálculo en Data Window SetIndexLabel(3, NULL); SetIndexLabel(4, NULL); SetIndexLabel(5, NULL); SetIndexLabel(6, NULL); //--- MQL4: configurar inicio de dibujo // DI+/DI- necesitan ~InpPeriod barras para converger // ADX = SMA(DX, InpPeriod) necesita InpPeriod valores de DX adicionales SetIndexDrawBegin(0, InpPeriod); // DI+ SetIndexDrawBegin(1, InpPeriod); // DI- SetIndexDrawBegin(2, InpPeriod * 2); // ADX //--- MQL4: valor vacío para barras sin datos SetIndexEmptyValue(0, EMPTY_VALUE); SetIndexEmptyValue(1, EMPTY_VALUE); SetIndexEmptyValue(2, EMPTY_VALUE); //--- MQL4: configurar nivel horizontal dinámicamente según parámetro th SetLevelValue(0, (double)InpThreshold); SetLevelStyle(STYLE_SOLID, 1, clrBlack); //--- MQL4: precisión de decimales IndicatorDigits(2); //--- MQL4: nombre corto del indicador IndicatorShortName("ADX and DI (" + IntegerToString(InpPeriod) + ")"); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Función principal de cálculo | //| | //| NOTA MQL4: Los arrays de precio (high[], low[], close[]) y los | //| buffers del indicador están en modo AS_SERIES por defecto en | //| MQL4, es decir, índice 0 = barra más reciente. Para mantener | //| la lógica idéntica al código MQL5 (donde 0 = barra más antigua),| //| desactivamos el modo series con ArraySetAsSeries(array, false). | //| | //| Algoritmo (réplica del Pine Script de BeikabuOyaji): | //| 1. True Range = max(H-L, |H-C[1]|, |L-C[1]|) | //| 2. +DM / -DM con exclusividad mutua de Wilder | //| 3. Suavizado Wilder: S(i) = S(i-1) - S(i-1)/len + valor | //| 4. DI+ = SmoothedDM+ / SmoothedTR * 100 | //| DI- = SmoothedDM- / SmoothedTR * 100 | //| 5. DX = |DI+ - DI-| / (DI+ + DI-) * 100 | //| 6. ADX = SMA(DX, len) ← ¡SMA simple, NO Wilder/SMMA! | //+------------------------------------------------------------------+ 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 datos mínimos (necesitamos al menos 2 barras) if(rates_total < 2) return(0); //================================================================= // CRÍTICO MQL4: Desactivar modo series en TODOS los arrays // MQL4 indexa arrays como series por defecto (0 = más reciente) // Necesitamos indexación regular (0 = más antiguo) para mantener // la misma lógica que MQL5 y Pine Script //================================================================= ArraySetAsSeries(time, false); ArraySetAsSeries(open, false); ArraySetAsSeries(high, false); ArraySetAsSeries(low, false); ArraySetAsSeries(close, false); ArraySetAsSeries(tick_volume, false); ArraySetAsSeries(volume, false); ArraySetAsSeries(spread, false); //--- También los buffers del indicador ArraySetAsSeries(DIPlusBuffer, false); ArraySetAsSeries(DIMinusBuffer, false); ArraySetAsSeries(ADXBuffer, false); ArraySetAsSeries(SmoothedTRBuffer, false); ArraySetAsSeries(SmoothedDMPlusBuffer, false); ArraySetAsSeries(SmoothedDMMinusBuffer, false); ArraySetAsSeries(DXBuffer, false); int start; //================================================================= // FASE 1: True Range, Movimiento Direccional, Suavizado Wilder, // DI+, DI-, DX //================================================================= if(prev_calculated == 0) { //--- Primera ejecución completa: inicializar barra 0 // Pine Script nz() retorna 0 para valores inexistentes, // así que los buffers suavizados empiezan desde 0 SmoothedTRBuffer[0] = 0.0; SmoothedDMPlusBuffer[0] = 0.0; SmoothedDMMinusBuffer[0] = 0.0; DIPlusBuffer[0] = 0.0; DIMinusBuffer[0] = 0.0; DXBuffer[0] = 0.0; ADXBuffer[0] = EMPTY_VALUE; start = 1; } else { //--- Recalcular solo la última barra (o desde donde se quedó) start = prev_calculated - 1; } //--- Bucle principal: indexación de izquierda a derecha (0 = más antigua) // gracias a ArraySetAsSeries(false) aplicado arriba for(int i = start; i < rates_total && !IsStopped(); i++) { //------------------------------------------------------- // 1. True Range: max(H-L, |H-C₋₁|, |L-C₋₁|) //------------------------------------------------------- double prev_close = close[i - 1]; double tr = MathMax(MathMax(high[i] - low[i], MathAbs(high[i] - prev_close)), MathAbs(low[i] - prev_close)); //------------------------------------------------------- // 2. Movimiento Direccional (+DM, -DM) // Regla de exclusividad mutua de Wilder //------------------------------------------------------- double up_move = high[i] - high[i - 1]; double down_move = low[i - 1] - low[i]; double dm_plus, dm_minus; if(up_move > down_move && up_move > 0.0) dm_plus = up_move; else dm_plus = 0.0; if(down_move > up_move && down_move > 0.0) dm_minus = down_move; else dm_minus = 0.0; //------------------------------------------------------- // 3. Suavizado de Wilder (forma running sum) // S(i) = S(i-1) - S(i-1)/len + valor //------------------------------------------------------- double prev_str = SmoothedTRBuffer[i - 1]; double prev_sdmp = SmoothedDMPlusBuffer[i - 1]; double prev_sdmm = SmoothedDMMinusBuffer[i - 1]; SmoothedTRBuffer[i] = prev_str - prev_str / InpPeriod + tr; SmoothedDMPlusBuffer[i] = prev_sdmp - prev_sdmp / InpPeriod + dm_plus; SmoothedDMMinusBuffer[i] = prev_sdmm - prev_sdmm / InpPeriod + dm_minus; //------------------------------------------------------- // 4. DI+ y DI- (en porcentaje) //------------------------------------------------------- double smoothed_tr = SmoothedTRBuffer[i]; if(smoothed_tr > 0.0) { DIPlusBuffer[i] = SmoothedDMPlusBuffer[i] / smoothed_tr * 100.0; DIMinusBuffer[i] = SmoothedDMMinusBuffer[i] / smoothed_tr * 100.0; } else { DIPlusBuffer[i] = 0.0; DIMinusBuffer[i] = 0.0; } //------------------------------------------------------- // 5. DX = |DI+ - DI-| / (DI+ + DI-) * 100 //------------------------------------------------------- double di_sum = DIPlusBuffer[i] + DIMinusBuffer[i]; if(di_sum > 0.0) DXBuffer[i] = MathAbs(DIPlusBuffer[i] - DIMinusBuffer[i]) / di_sum * 100.0; else DXBuffer[i] = 0.0; } //================================================================= // FASE 2: ADX = SMA(DX, len) // Media aritmética simple de ventana deslizante sobre DX // ¡CRÍTICO: es SMA, NO Wilder/SMMA/RMA! //================================================================= int adx_first_bar = InpPeriod; // Primera barra con ADX válido int adx_start; if(prev_calculated == 0) { //--- Marcar todas las barras sin ADX válido for(int i = 0; i < MathMin(adx_first_bar, rates_total); i++) ADXBuffer[i] = EMPTY_VALUE; //--- Verificar que hay suficientes barras if(rates_total <= adx_first_bar) return(rates_total); //--- Calcular el primer valor de ADX como SMA de DX[1..InpPeriod] double sum = 0.0; for(int j = 1; j <= InpPeriod; j++) sum += DXBuffer[j]; ADXBuffer[adx_first_bar] = sum / InpPeriod; adx_start = adx_first_bar + 1; } else { adx_start = MathMax(prev_calculated - 1, adx_first_bar + 1); } //--- Calcular SMA incremental para las barras restantes // SMA(i) = SMA(i-1) + (DX[i] - DX[i-InpPeriod]) / InpPeriod for(int i = adx_start; i < rates_total && !IsStopped(); i++) { double new_val = DXBuffer[i]; double old_val = DXBuffer[i - InpPeriod]; ADXBuffer[i] = ADXBuffer[i - 1] + (new_val - old_val) / InpPeriod; } return(rates_total); } //+------------------------------------------------------------------+