//+------------------------------------------------------------------+
//| AdvancedSRBot.mq4 |
//| Copyright 2023, MetaQuotes Software Corp. |
//| [Link] |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Software Corp."
#property link "[Link]
#property version "2.00"
#property strict
//+------------------------------------------------------------------+
//| Input Parameters |
//+------------------------------------------------------------------+
// Trading Settings
input double LotSize = 0.1; // Tamaño del lote
input int MagicNumber = 12345; // Número mágico
input int Slippage = 3; // Deslizamiento (pips)
// Indicator Settings
input int ADX_Period = 14; // Período ADX
input int ADX_Threshold = 25; // Umbral ADX
input int DMI_Period = 14; // Período DMI
input int Stoch_PeriodK = 5; // Período %K Estocástico
input int Stoch_PeriodD = 3; // Período %D Estocástico
input int Stoch_Slowing = 3; // Ralentización Estocástico
input int MA_Period = 50; // Periodo Media Móvil (filtro tendencia)
// Volume Settings
input int Volume_Lookback = 20; // Periodo comparación volumen
input double Volume_Multiplier= 1.5; // Multiplicador volumen alto
// S/R Detection Settings
input int S_R_Lookback = 200; // Velas para analizar S/R
input int S_R_MinTouches = 3; // Mínimo toques para zona S/R
input double S_R_ZoneWidthPips= 30; // Ancho zona S/R en pips
input bool ShowZones = true; // Mostrar zonas en gráfico
input color SupportColor = clrBlue; // Color soportes
input color ResistanceColor = clrRed; // Color resistencias
// Risk Management
input double RiskPercent = 1.0; // Riesgo por operación (%)
input bool UseAtrSL = true; // Usar ATR para Stop Loss
input int AtrPeriod = 14; // Periodo ATR
input double AtrMultiplier = 2.0; // Multiplicador ATR
input double RewardRatio = 1.5; // Ratio Take Profit
// Time Filters
input int StartHour = 8; // Hora inicio trading
input int EndHour = 20; // Hora fin trading
input bool Monday = true; // Operar Lunes
input bool Tuesday = true; // Operar Martes
input bool Wednesday = true; // Operar Miércoles
input bool Thursday = true; // Operar Jueves
input bool Friday = true; // Operar Viernes
//+------------------------------------------------------------------+
//| Global Variables |
//+------------------------------------------------------------------+
int adxHandle, dmiPlusHandle, dmiMinusHandle, stochHandle, maHandle, atrHandle;
S_R_Zone supports[], resistances[];
datetime lastTradeTime;
//+------------------------------------------------------------------+
//| Estructura para zonas S/R |
//+------------------------------------------------------------------+
struct S_R_Zone
double price;
int touches;
datetime lastTouch;
bool isSupport;
double strength; // Fuerza de la zona (0-1)
};
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
// Crear handles para los indicadores
adxHandle = iADX(NULL, 0, ADX_Period);
dmiPlusHandle = iCustom(NULL, 0, "Examples\\ADX", ADX_Period, 0, MODE_PLUSDI, PRICE_CLOSE);
dmiMinusHandle = iCustom(NULL, 0, "Examples\\ADX", ADX_Period, 0, MODE_MINUSDI,
PRICE_CLOSE);
stochHandle = iStochastic(NULL, 0, Stoch_PeriodK, Stoch_PeriodD, Stoch_Slowing, MODE_SMA, 0,
MODE_MAIN, 0);
maHandle = iMA(NULL, 0, MA_Period, 0, MODE_SMA, PRICE_CLOSE);
atrHandle = iATR(NULL, 0, AtrPeriod);
if(adxHandle == INVALID_HANDLE || dmiPlusHandle == INVALID_HANDLE ||
dmiMinusHandle == INVALID_HANDLE || stochHandle == INVALID_HANDLE ||
maHandle == INVALID_HANDLE || atrHandle == INVALID_HANDLE)
Print("Error al crear handles de indicadores");
return(INIT_FAILED);
// Detectar zonas S/R iniciales
DetectSupportResistance();
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
// Liberar handles
IndicatorRelease(adxHandle);
IndicatorRelease(dmiPlusHandle);
IndicatorRelease(dmiMinusHandle);
IndicatorRelease(stochHandle);
IndicatorRelease(maHandle);
IndicatorRelease(atrHandle);
// Eliminar objetos de zonas si están visibles
if(ShowZones) DeleteZones();
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
// Verificar condiciones básicas para trading
if(!IsTradingAllowed()) return;
// Actualizar zonas S/R cada hora
static datetime lastUpdate = 0;
if(TimeCurrent() - lastUpdate >= 3600)
DetectSupportResistance();
lastUpdate = TimeCurrent();
// Verificar si ya hay órdenes abiertas para este magic number
if(CountOrders() > 0) return;
// Obtener datos de los indicadores
MqlRates rates[];
double adx[], dmiPlus[], dmiMinus[], stochMain[], stochSignal[], volume[], ma[], atr[];
CopyRates(NULL, 0, 0, 2, rates);
CopyBuffer(adxHandle, 0, 0, 3, adx);
CopyBuffer(dmiPlusHandle, 0, 0, 3, dmiPlus);
CopyBuffer(dmiMinusHandle, 0, 0, 3, dmiMinus);
CopyBuffer(stochHandle, 0, 0, 3, stochMain);
CopyBuffer(stochHandle, 1, 0, 3, stochSignal);
CopyTickVolume(NULL, 0, 0, Volume_Lookback, volume);
CopyBuffer(maHandle, 0, 0, 3, ma);
CopyBuffer(atrHandle, 0, 0, 1, atr);
// Calcular volumen promedio
double avgVolume = ArrayAverage(volume);
// Verificar condiciones de compra
CheckBuyConditions(rates, avgVolume, adx, dmiPlus, dmiMinus, stochMain, stochSignal, ma, atr[0]);
// Verificar condiciones de venta
CheckSellConditions(rates, avgVolume, adx, dmiPlus, dmiMinus, stochMain, stochSignal, ma, atr[0]);
//+------------------------------------------------------------------+
//| Verificar condiciones de compra |
//+------------------------------------------------------------------+
void CheckBuyConditions(MqlRates &rates[], double avgVolume, double &adx[], double &dmiPlus[],
double &dmiMinus[], double &stochMain[], double &stochSignal[], double &ma[], double
atrValue)
// Condición 1: Ruptura de resistencia con volumen alto, ADX > 25 y DMI+ > DMI-
if(IsResistanceBreak(rates) &&
rates[1].tick_volume > avgVolume * Volume_Multiplier &&
adx[1] > ADX_Threshold &&
dmiPlus[1] > dmiMinus[1] &&
rates[1].close > ma[1]) // Precio arriba de MA para tendencia alcista
double sl = CalculateStopLoss(true, rates, atrValue);
double tp = rates[1].close + (rates[1].close - sl) * RewardRatio;
double lotSize = CalculateLotSize(sl);
EnterTrade(OP_BUY, lotSize, sl, tp, "Compra por ruptura");
// Condición 2: Rechazo en soporte con envolvente alcista y cruce estocástico
if(IsSupportRejection(rates) &&
IsBullishEngulfing(rates) &&
stochMain[1] < stochSignal[1] && stochMain[0] > stochSignal[0] &&
rates[1].close > ma[1]) // Precio arriba de MA para tendencia alcista
double sl = CalculateStopLoss(true, rates, atrValue);
double tp = rates[1].close + (rates[1].close - sl) * RewardRatio;
double lotSize = CalculateLotSize(sl);
EnterTrade(OP_BUY, lotSize, sl, tp, "Compra por rechazo");
//+------------------------------------------------------------------+
//| Verificar condiciones de venta |
//+------------------------------------------------------------------+
void CheckSellConditions(MqlRates &rates[], double avgVolume, double &adx[], double &dmiPlus[],
double &dmiMinus[], double &stochMain[], double &stochSignal[], double &ma[], double
atrValue)
// Condición 1: Ruptura de soporte con volumen alto, ADX > 25 y DMI- > DMI+
if(IsSupportBreak(rates) &&
rates[1].tick_volume > avgVolume * Volume_Multiplier &&
adx[1] > ADX_Threshold &&
dmiMinus[1] > dmiPlus[1] &&
rates[1].close < ma[1]) // Precio abajo de MA para tendencia bajista
double sl = CalculateStopLoss(false, rates, atrValue);
double tp = rates[1].close - (sl - rates[1].close) * RewardRatio;
double lotSize = CalculateLotSize(sl);
EnterTrade(OP_SELL, lotSize, sl, tp, "Venta por ruptura");
// Condición 2: Rechazo en resistencia con envolvente bajista y cruce estocástico
if(IsResistanceRejection(rates) &&
IsBearishEngulfing(rates) &&
stochMain[1] > stochSignal[1] && stochMain[0] < stochSignal[0] &&
rates[1].close < ma[1]) // Precio abajo de MA para tendencia bajista
double sl = CalculateStopLoss(false, rates, atrValue);
double tp = rates[1].close - (sl - rates[1].close) * RewardRatio;
double lotSize = CalculateLotSize(sl);
EnterTrade(OP_SELL, lotSize, sl, tp, "Venta por rechazo");
}
//+------------------------------------------------------------------+
//| Funciones auxiliares mejoradas |
//+------------------------------------------------------------------+
// Detectar zonas de soporte y resistencia
void DetectSupportResistance()
ArrayResize(supports, 0);
ArrayResize(resistances, 0);
// Buscar mínimos locales para soportes
for(int i = 3; i < S_R_Lookback; i++)
double low1 = iLow(NULL, 0, i);
double low2 = iLow(NULL, 0, i+1);
double low3 = iLow(NULL, 0, i+2);
if(low2 < low1 && low2 < low3) // Mínimo local
AddToZoneArray(supports, low2, true);
// Buscar máximos locales para resistencias
for(int i = 3; i < S_R_Lookback; i++)
{
double high1 = iHigh(NULL, 0, i);
double high2 = iHigh(NULL, 0, i+1);
double high3 = iHigh(NULL, 0, i+2);
if(high2 > high1 && high2 > high3) // Máximo local
AddToZoneArray(resistances, high2, false);
// Filtrar y ordenar zonas
FilterAndSortZones(supports);
FilterAndSortZones(resistances);
// Dibujar zonas si está activado
if(ShowZones)
DeleteZones();
DrawZones(supports, SupportColor);
DrawZones(resistances, ResistanceColor);
// Añadir precio al array de zonas con cálculo de fuerza
void AddToZoneArray(S_R_Zone &zones[], double price, bool isSupport)
double zoneWidth = S_R_ZoneWidthPips * _Point;
for(int i = 0; i < ArraySize(zones); i++)
if(MathAbs(zones[i].price - price) <= zoneWidth)
zones[i].touches++;
zones[i].lastTouch = iTime(NULL, 0, 1);
// Calcular fuerza basada en toques y antigüedad
zones[i].strength = MathMin(zones[i].touches / 5.0, 1.0) *
(1 - (TimeCurrent() - zones[i].lastTouch) / (86400.0 * 30));
if(MathAbs(zones[i].price - price) < zoneWidth/2)
zones[i].price = (zones[i].price * zones[i].touches + price) / (zones[i].touches + 1);
return;
// Si no se encontró zona cercana, añadir nueva
int size = ArraySize(zones);
ArrayResize(zones, size+1);
zones[size].price = price;
zones[size].touches = 1;
zones[size].lastTouch = iTime(NULL, 0, 1);
zones[size].isSupport = isSupport;
zones[size].strength = 0.2; // Fuerza inicial
// Filtrar y ordenar zonas por relevancia
void FilterAndSortZones(S_R_Zone &zones[])
S_R_Zone tempZones[];
int count = 0;
// Filtrar por número mínimo de toques
for(int i = 0; i < ArraySize(zones); i++)
if(zones[i].touches >= S_R_MinTouches)
ArrayResize(tempZones, count+1);
tempZones[count] = zones[i];
count++;
// Ordenar por fuerza (más fuerte primero)
for(int i = 0; i < ArraySize(tempZones); i++)
for(int j = i+1; j < ArraySize(tempZones); j++)
{
if(tempZones[j].strength > tempZones[i].strength)
S_R_Zone temp = tempZones[i];
tempZones[i] = tempZones[j];
tempZones[j] = temp;
ArrayCopy(zones, tempZones);
// Verificar ruptura de resistencia mejorada
bool IsResistanceBreak(MqlRates &rates[])
double zoneWidth = S_R_ZoneWidthPips * _Point;
for(int i = 0; i < ArraySize(resistances); i++)
if(resistances[i].strength > 0.5) // Solo zonas con fuerza > 0.5
double zoneTop = resistances[i].price + zoneWidth;
double zoneBottom = resistances[i].price - zoneWidth;
// Verificar que el precio anterior estaba dentro de la zona
if(rates[1].high >= zoneBottom && rates[1].low <= zoneTop)
// Verificar ruptura por cierre fuera de la zona
if(rates[0].close > zoneTop && rates[0].open < zoneTop)
return true;
return false;
// Verificar rechazo en resistencia mejorado
bool IsResistanceRejection(MqlRates &rates[])
double zoneWidth = S_R_ZoneWidthPips * _Point;
for(int i = 0; i < ArraySize(resistances); i++)
if(resistances[i].strength > 0.5) // Solo zonas con fuerza > 0.5
double zoneTop = resistances[i].price + zoneWidth;
double zoneBottom = resistances[i].price - zoneWidth;
// Verificar que el precio tocó la zona pero cerró fuera
if(rates[0].high >= zoneBottom && rates[0].high <= zoneTop &&
rates[0].close < zoneBottom)
// Verificar volumen y sombra superior
if(rates[0].tick_volume > iVolume(NULL, 0, 1) &&
(rates[0].high - rates[0].close) > (rates[0].close - rates[0].low))
return true;
return false;
// Verificar ruptura de soporte mejorada
bool IsSupportBreak(MqlRates &rates[])
double zoneWidth = S_R_ZoneWidthPips * _Point;
for(int i = 0; i < ArraySize(supports); i++)
if(supports[i].strength > 0.5) // Solo zonas con fuerza > 0.5
{
double zoneTop = supports[i].price + zoneWidth;
double zoneBottom = supports[i].price - zoneWidth;
// Verificar que el precio anterior estaba dentro de la zona
if(rates[1].high >= zoneBottom && rates[1].low <= zoneTop)
// Verificar ruptura por cierre fuera de la zona
if(rates[0].close < zoneBottom && rates[0].open > zoneBottom)
return true;
return false;
// Verificar rechazo en soporte mejorado
bool IsSupportRejection(MqlRates &rates[])
double zoneWidth = S_R_ZoneWidthPips * _Point;
for(int i = 0; i < ArraySize(supports); i++)
{
if(supports[i].strength > 0.5) // Solo zonas con fuerza > 0.5
double zoneTop = supports[i].price + zoneWidth;
double zoneBottom = supports[i].price - zoneWidth;
// Verificar que el precio tocó la zona pero cerró fuera
if(rates[0].low >= zoneBottom && rates[0].low <= zoneTop &&
rates[0].close > zoneTop)
// Verificar volumen y sombra inferior
if(rates[0].tick_volume > iVolume(NULL, 0, 1) &&
(rates[0].close - rates[0].low) > (rates[0].high - rates[0].close))
return true;
return false;
// Verificar patrón envolvente alcista mejorado
bool IsBullishEngulfing(MqlRates &rates[])
// La vela anterior es bajista
if(rates[1].close >= rates[1].open) return false;
// La vela actual es alcista
if(rates[0].close <= rates[0].open) return false;
// El cuerpo actual engulle el cuerpo anterior
if(rates[0].open > rates[1].close || rates[0].close < rates[1].open) return false;
// Confirmación con volumen
if(rates[0].tick_volume < rates[1].tick_volume * 1.2) return false;
return true;
// Verificar patrón envolvente bajista mejorado
bool IsBearishEngulfing(MqlRates &rates[])
// La vela anterior es alcista
if(rates[1].close <= rates[1].open) return false;
// La vela actual es bajista
if(rates[0].close >= rates[0].open) return false;
// El cuerpo actual engulle el cuerpo anterior
if(rates[0].open < rates[1].close || rates[0].close > rates[1].open) return false;
// Confirmación con volumen
if(rates[0].tick_volume < rates[1].tick_volume * 1.2) return false;
return true;
// Calcular Stop Loss basado en estructura o ATR
double CalculateStopLoss(bool isBuy, MqlRates &rates[], double atrValue)
if(UseAtrSL)
return isBuy ? rates[0].close - atrValue * AtrMultiplier :
rates[0].close + atrValue * AtrMultiplier;
else
return isBuy ? iLow(NULL, 0, iLowest(NULL, 0, MODE_LOW, 20, 1)) :
iHigh(NULL, 0, iHighest(NULL, 0, MODE_HIGH, 20, 1));
// Calcular tamaño de lote basado en riesgo
double CalculateLotSize(double slPrice)
{
double riskAmount = AccountBalance() * RiskPercent / 100.0;
double pointValue = MarketInfo(Symbol(), MODE_TICKVALUE);
double pointsRisk = MathAbs(slPrice - (OrderType() == OP_BUY ? Bid : Ask)) / _Point;
if(pointValue == 0 || pointsRisk == 0) return LotSize;
double lots = riskAmount / (pointValue * pointsRisk);
lots = MathMin(lots, MarketInfo(Symbol(), MODE_MAXLOT));
lots = MathMax(lots, MarketInfo(Symbol(), MODE_MINLOT));
return NormalizeDouble(lots, 2);
// Entrar en una operación
void EnterTrade(int type, double lots, double sl, double tp, string comment)
double price = (type == OP_BUY) ? Ask : Bid;
double slPoints = MathAbs(price - sl) / _Point;
double tpPoints = MathAbs(price - tp) / _Point;
if(slPoints < MarketInfo(Symbol(), MODE_STOPLEVEL) ||
tpPoints < MarketInfo(Symbol(), MODE_STOPLEVEL))
Print("Stop Loss o Take Profit demasiado cerca. Operación cancelada.");
return;
}
int ticket = OrderSend(Symbol(), type, lots, price, Slippage, sl, tp, comment, MagicNumber, 0,
type == OP_BUY ? clrGreen : clrRed);
if(ticket < 0)
Print("Error al abrir orden: ", GetLastError());
else
lastTradeTime = TimeCurrent();
// Contar órdenes abiertas por este EA
int CountOrders()
int count = 0;
for(int i = 0; i < OrdersTotal(); i++)
if(OrderSelect(i, SELECT_BY_POS) && OrderMagicNumber() == MagicNumber &&
OrderSymbol() == Symbol())
count++;
}
return count;
// Verificar si el trading está permitido
bool IsTradingAllowed()
// Verificar horario de trading
int hour = TimeHour(TimeCurrent());
if(hour < StartHour || hour >= EndHour) return false;
// Verificar día de la semana
int weekday = TimeDayOfWeek(TimeCurrent());
if((weekday == 1 && !Monday) || (weekday == 2 && !Tuesday) ||
(weekday == 3 && !Wednesday) || (weekday == 4 && !Thursday) ||
(weekday == 5 && !Friday)) return false;
// Evitar operar justo después de una operación previa
if(TimeCurrent() - lastTradeTime < 60) return false;
// Verificar conexión y permisos de trading
if(!IsConnected() || !IsTradeAllowed()) return false;
return true;
}
// Dibujar zonas en el gráfico
void DrawZones(S_R_Zone &zones[], color zoneColor)
for(int i = 0; i < MathMin(ArraySize(zones), 5); i++) // Mostrar máximo 5 zonas más fuertes
string name = (zones[i].isSupport ? "Support_" : "Resistance_") + IntegerToString(i);
double upper = zones[i].price + S_R_ZoneWidthPips * _Point;
double lower = zones[i].price - S_R_ZoneWidthPips * _Point;
// Crear rectángulo para la zona
ObjectCreate(0, name, OBJ_RECTANGLE, 0, iTime(NULL, 0, 100), upper, TimeCurrent(), lower);
ObjectSetInteger(0, name, OBJPROP_COLOR, zoneColor);
ObjectSetInteger(0, name, OBJPROP_BACK, true);
ObjectSetInteger(0, name, OBJPROP_FILL, true);
ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);
// Añadir etiqueta con fuerza de la zona
string labelName = name + "_Label";
ObjectCreate(0, labelName, OBJ_TEXT, 0, TimeCurrent(), upper);
ObjectSetString(0, labelName, OBJPROP_TEXT,
StringFormat("%s %.2f (F:%.1f)", zones[i].isSupport ? "S" : "R",
zones[i].price, zones[i].strength));
ObjectSetInteger(0, labelName, OBJPROP_COLOR, zoneColor);
ObjectSetInteger(0, labelName, OBJPROP_ANCHOR, ANCHOR_RIGHT_UPPER);
// Eliminar objetos de zonas del gráfico
void DeleteZones()
for(int i = 0; i < ObjectsTotal(); i++)
string name = ObjectName(i);
if(StringFind(name, "Support_") == 0 || StringFind(name, "Resistance_") == 0)
ObjectDelete(name);
// Calcular promedio de un array
double ArrayAverage(double &arr[])
double sum = 0;
for(int i = 0; i < ArraySize(arr); i++) sum += arr[i];
return sum / ArraySize(arr);