//╔════════════════════════════════════════════════════════════════════════╗ //║ ██████╗██╗ ██╗ █████╗ ██████╗ ██████╗ █████╗ █████╗ █████╗ ███╗ ██╗ ║ //║ ██╔═══╝╚██╗██╔╝██╔══██╗██╔══██╗██╔═══╝██╔══██╗██╔═══╝██╔══██╗████╗ ██║ ║ //║ █████╗ ╚███╔╝ ██║ ██║██████╔╝█████╗ ███████║██║ ██║ ██║██╔██╗██║ ║ //║ ██╔══╝ ██╔██╗ ██║ ██║██╔══██╗██╔══╝ ██╔══██║██║ ██║ ██║██║╚████║ ║ //║ ██████╗██╔╝ ██╗╚█████╔╝██████╔╝██████╗██║ ██║╚█████╗╚█████╔╝██║ ╚███║ ║ //║ ╚═════╝╚═╝ ╚═╝ ╚════╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝ ╚════╝ ╚════╝ ╚═╝ ╚══╝ ║ //╚═══════ Algorithms that observe ════ Signals that speak ════════════════╝ //+------------------------------------------------------------------+ //| Réplica fiel del indicador "ADX and DI" de BeikabuOyaji | //| TradingView Pine Script v4 → MQL5 | //| 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 iADX (usa EMA) | //| ni con iADXWilder (usa SMMA para el ADX final) | //| - Inicialización desde 0 (replica nz() de Pine Script) | //+------------------------------------------------------------------+ #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 7 // 3 visibles + 4 de cálculo interno #property indicator_plots 3 // DI+, DI-, ADX //--- Plot 0: DI+ (verde) #property indicator_label1 "DI+" #property indicator_type1 DRAW_LINE #property indicator_color1 clrGreen #property indicator_style1 STYLE_SOLID #property indicator_width1 1 //--- Plot 1: DI- (rojo) #property indicator_label2 "DI-" #property indicator_type2 DRAW_LINE #property indicator_color2 clrRed #property indicator_style2 STYLE_SOLID #property indicator_width2 1 //--- Plot 2: ADX (azul marino) #property indicator_label3 "ADX" #property indicator_type3 DRAW_LINE #property indicator_color3 clrNavy #property indicator_style3 STYLE_SOLID #property indicator_width3 2 //--- Nivel horizontal por defecto (se sobreescribe dinámicamente en OnInit) #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 (INDICATOR_DATA: visibles, accesibles vía CopyBuffer) 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 (INDICATOR_CALCULATIONS: no visibles) 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); } //--- Asignar buffers de datos (plots) SetIndexBuffer(0, DIPlusBuffer, INDICATOR_DATA); SetIndexBuffer(1, DIMinusBuffer, INDICATOR_DATA); SetIndexBuffer(2, ADXBuffer, INDICATOR_DATA); //--- Asignar buffers de cálculo interno SetIndexBuffer(3, SmoothedTRBuffer, INDICATOR_CALCULATIONS); SetIndexBuffer(4, SmoothedDMPlusBuffer, INDICATOR_CALCULATIONS); SetIndexBuffer(5, SmoothedDMMinusBuffer, INDICATOR_CALCULATIONS); SetIndexBuffer(6, DXBuffer, INDICATOR_CALCULATIONS); //--- Configurar inicio de dibujo // DI+/DI- requieren al menos 1 barra previa (para close[i-1], high[i-1]) // pero el suavizado de Wilder necesita ~InpPeriod barras para converger // ADX = SMA(DX, InpPeriod) necesita InpPeriod valores de DX adicionales PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, InpPeriod); // DI+ PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, InpPeriod); // DI- PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, InpPeriod * 2); // ADX //--- Valor vacío para barras sin datos PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, EMPTY_VALUE); //--- Configurar nivel horizontal según parámetro th IndicatorSetInteger(INDICATOR_LEVELS, 1); IndicatorSetDouble(INDICATOR_LEVELVALUE, 0, (double)InpThreshold); IndicatorSetInteger(INDICATOR_LEVELCOLOR, 0, clrBlack); IndicatorSetInteger(INDICATOR_LEVELSTYLE, 0, STYLE_SOLID); //--- Precisión de decimales mostrados IndicatorSetInteger(INDICATOR_DIGITS, 2); //--- Nombre corto string short_name = "ADX and DI (" + IntegerToString(InpPeriod) + ")"; IndicatorSetString(INDICATOR_SHORTNAME, short_name); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Función principal de cálculo | //| | //| Algoritmo (réplica del Pine Script de BeikabuOyaji): | //| | //| 1. True Range = max(H-L, |H-C[1]|, |L-C[1]|) | //| 2. +DM = (H-H[1] > L[1]-L) ? max(H-H[1], 0) : 0 | //| -DM = (L[1]-L > H-H[1]) ? max(L[1]-L, 0) : 0 | //| 3. Suavizado Wilder: | //| S(i) = S(i-1) - S(i-1)/len + valor | //| (equivale a running sum con decaimiento α=1/N) | //| 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); 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) for(int i = start; i < rates_total && !IsStopped(); i++) { //------------------------------------------------------- // 1. True Range: max(H-L, |H-C₋₁|, |L-C₋₁|) // Replica: max(max(high-low, abs(high-nz(close[1]))), abs(low-nz(close[1]))) //------------------------------------------------------- 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: // Solo el mayor movimiento (si es positivo) se cuenta //------------------------------------------------------- double up_move = high[i] - high[i - 1]; // UpMove double down_move = low[i - 1] - low[i]; // DownMove 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 // Equivalente a: S(i) = S(i-1) * (1 - 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) // DI+ = SmoothedDM+ / SmoothedTR * 100 // DI- = SmoothedDM- / SmoothedTR * 100 //------------------------------------------------------- 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 // (necesita InpPeriod valores de DX: barras 1..InpPeriod) 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 para calcular el primer SMA if(rates_total <= adx_first_bar) return(rates_total); //--- Calcular el primer valor de ADX como SMA de DX[1..InpPeriod] // DX es válido desde barra 1 (primera barra que tiene barra previa) // Pine Script sma(DX, len) en la barra InpPeriod toma las barras // [InpPeriod-len+1 .. InpPeriod] = [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 // Esto es algebraicamente equivalente a sumar los últimos InpPeriod // valores de DX y dividir entre InpPeriod, pero es O(1) por barra 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); } //+------------------------------------------------------------------+