ADX-and-DI/ADX_and_DI.mq4

358 lines
No EOL
30 KiB
MQL4

//╔════════════════════════════════════════════════════════════════════════╗
//║ ██████╗██╗ ██╗ █████╗ ██████╗ ██████╗ █████╗ █████╗ █████╗ ███╗ ██╗ ║
//║ ██╔═══╝╚██╗██╔╝██╔══██╗██╔══██╗██╔═══╝██╔══██╗██╔═══╝██╔══██╗████╗ ██║ ║
//║ █████╗ ╚███╔╝ ██║ ██║██████╔╝█████╗ ███████║██║ ██║ ██║██╔██╗██║ ║
//║ ██╔══╝ ██╔██╗ ██║ ██║██╔══██╗██╔══╝ ██╔══██║██║ ██║ ██║██║╚████║ ║
//║ ██████╗██╔╝ ██╗╚█████╔╝██████╔╝██████╗██║ ██║╚█████╗╚█████╔╝██║ ╚███║ ║
//║ ╚═════╝╚═╝ ╚═╝ ╚════╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝ ╚════╝ ╚════╝ ╚═╝ ╚══╝ ║
//╚═══════ 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);
}
//+------------------------------------------------------------------+