358 lines
No EOL
30 KiB
MQL4
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);
|
|
}
|
|
//+------------------------------------------------------------------+ |