mql5/Experts/Previous/ATR_QQE_EA_v4.mq5

695 lines
23 KiB
MQL5
Raw Permalink Normal View History

2025-07-20 16:41:20 +01:00
//+------------------------------------------------------------------+
//| ATR QQE Conservative Trading System.mq5 |
//| Conservative QQE with Enhanced Risk Management |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025"
#property link ""
#property version "4.00"
#property strict
#property description "Conservative QQE system with enhanced filters and risk management"
#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
#include <Trade\OrderInfo.mqh>
// EA input parameters
input group "Risk Management (Conservative Settings)"
input double Risk_Percent = 1.0; // Risk % of equity per trade (reduced)
input double SL_ATR_Multiplier = 4.0; // Stop Loss ATR Multiplier (increased)
input double TP_ATR_Multiplier = 8.0; // Take Profit ATR Multiplier (1:2 ratio)
input double Trail_Start_Multiplier = 4.0; // Trailing Start ATR Multiplier
input double Trail_Step_Multiplier = 1.0; // Trailing Step ATR Multiplier
input double Extra_Buffer_Pips = 10; // Extra buffer in pips (increased)
input int Max_Consecutive_Losses = 3; // Stop trading after X losses
input int Min_Hours_Between_Trades = 4; // Minimum hours between trades
input group "Market Condition Filters"
input bool Use_Trend_Filter = true; // Only trade with trend
input int Trend_MA_Period = 200; // Trend MA period
input bool Use_Volatility_Filter = true; // Use ADX volatility filter
input int ADX_Period = 14; // ADX period
input double ADX_Threshold = 25; // Minimum ADX for trading
input bool Use_Time_Filter = true; // Trade only during active hours
input int Start_Hour = 7; // Trading start hour (GMT)
input int End_Hour = 17; // Trading end hour (GMT)
input group "Enhanced QQE Parameters"
input int QQE_RSI_Period = 14; // QQE RSI Period
input int QQE_Smoothing_Factor = 8; // QQE Smoothing Factor (increased)
input double QQE_Signal_Threshold = 5; // Minimum distance between QQE lines
input bool Use_QQE_Divergence_Filter = true; // Additional divergence filter
input group "ATR and Trading Parameters"
input int ATR_Period = 14; // ATR Period
input int ATR_Shift = 1; // ATR shift
input bool Manage_SL = true; // Manage Stop Loss
input bool Manage_TP = true; // Manage Take Profit
input bool Use_Trailing_Stop = true; // Use Trailing Stop
input bool Allow_Long_Positions = true; // Allow Long Positions
input bool Allow_Short_Positions = true; // Allow Short Positions
input int Max_Positions = 1; // Maximum concurrent positions
input group "Timing Parameters"
input bool Process_On_Bar_Close = true; // Process only on bar close
// Global variables
CTrade trade;
CPositionInfo posInfo;
COrderInfo orderInfo;
double atrValue;
double point;
int digits;
datetime lastTradeTime = 0;
datetime lastBarTime = 0;
int atrHandle;
int rsiHandle;
int trendMAHandle;
int adxHandle;
int positionCount = 0;
ulong positionTickets[];
int consecutiveLosses = 0;
bool tradingHalted = false;
// QQE calculation variables
int Wilders_Period;
int StartBar;
// QQE buffers
double TrLevelSlow[]; // QQE Smoothed line
double AtrRsi[]; // ATR of RSI
double MaAtrRsi[]; // MA of ATR RSI
double Rsi[]; // Raw RSI values
double RsiMa[]; // QQE Main line (MA of RSI)
double MaMaAtrRsi[]; // MA of MA of ATR RSI
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
trade.SetExpertMagicNumber(123456);
digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
// Initialize QQE calculation parameters
Wilders_Period = QQE_RSI_Period * 2 - 1;
StartBar = MathMax(QQE_Smoothing_Factor, Wilders_Period);
// Create indicator handles
atrHandle = iATR(_Symbol, PERIOD_CURRENT, ATR_Period);
rsiHandle = iRSI(_Symbol, PERIOD_CURRENT, QQE_RSI_Period, PRICE_CLOSE);
trendMAHandle = iMA(_Symbol, PERIOD_CURRENT, Trend_MA_Period, 0, MODE_SMA, PRICE_CLOSE);
adxHandle = iADX(_Symbol, PERIOD_CURRENT, ADX_Period);
if(atrHandle == INVALID_HANDLE || rsiHandle == INVALID_HANDLE ||
trendMAHandle == INVALID_HANDLE || adxHandle == INVALID_HANDLE)
{
Print("Error creating indicator handles");
return(INIT_FAILED);
}
// Initialize QQE arrays
ArraySetAsSeries(TrLevelSlow, true);
ArraySetAsSeries(AtrRsi, true);
ArraySetAsSeries(MaAtrRsi, true);
ArraySetAsSeries(Rsi, true);
ArraySetAsSeries(RsiMa, true);
ArraySetAsSeries(MaMaAtrRsi, true);
RefreshPositions();
Print("Conservative ATR QQE Trading System initialized");
Print("Risk per trade: ", Risk_Percent, "%");
Print("Risk/Reward ratio: 1:", (TP_ATR_Multiplier/SL_ATR_Multiplier));
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
IndicatorRelease(atrHandle);
IndicatorRelease(rsiHandle);
IndicatorRelease(trendMAHandle);
IndicatorRelease(adxHandle);
Comment("");
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
if(Process_On_Bar_Close)
{
datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);
if(currentBarTime != lastBarTime)
{
lastBarTime = currentBarTime;
UpdateIndicators();
ManagePositions();
CheckForEntrySignals();
}
}
}
//+------------------------------------------------------------------+
//| Check if trading is allowed based on filters |
//+------------------------------------------------------------------+
bool IsTradingAllowed()
{
// Check if trading is halted due to consecutive losses
if(tradingHalted)
{
Print("Trading halted due to ", Max_Consecutive_Losses, " consecutive losses");
return false;
}
// Check minimum time between trades
if(TimeCurrent() - lastTradeTime < Min_Hours_Between_Trades * 3600)
{
return false;
}
// Check trading hours
if(Use_Time_Filter)
{
MqlDateTime dt;
TimeToStruct(TimeCurrent(), dt);
if(dt.hour < Start_Hour || dt.hour >= End_Hour)
return false;
}
// Check volatility filter (ADX)
if(Use_Volatility_Filter)
{
double adxBuffer[];
if(CopyBuffer(adxHandle, 0, 1, 1, adxBuffer) <= 0)
return false;
if(adxBuffer[0] < ADX_Threshold)
{
Print("ADX too low for trading: ", adxBuffer[0]);
return false;
}
}
return true;
}
//+------------------------------------------------------------------+
//| Check trend direction |
//+------------------------------------------------------------------+
int GetTrendDirection()
{
if(!Use_Trend_Filter)
return 0; // No filter
double maBuffer[], closeBuffer[];
if(CopyBuffer(trendMAHandle, 0, 1, 1, maBuffer) <= 0)
return 0;
if(CopyClose(_Symbol, PERIOD_CURRENT, 1, 1, closeBuffer) <= 0)
return 0;
if(closeBuffer[0] > maBuffer[0])
return 1; // Uptrend
else if(closeBuffer[0] < maBuffer[0])
return -1; // Downtrend
else
return 0; // Neutral
}
//+------------------------------------------------------------------+
//| Calculate lot size based on equity risk |
//+------------------------------------------------------------------+
double CalculateLotSize(double entryPrice, double stopLoss)
{
double accountBalance = AccountInfoDouble(ACCOUNT_BALANCE);
double riskAmount = accountBalance * Risk_Percent / 100.0;
double stopDistance = MathAbs(entryPrice - stopLoss);
if(stopDistance == 0) return 0;
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
if(tickValue == 0 || tickSize == 0) return 0;
double lotSize = riskAmount / (stopDistance / tickSize * tickValue);
// Apply lot size limits
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
lotSize = MathMax(lotSize, minLot);
lotSize = MathMin(lotSize, maxLot);
// Round to lot step
lotSize = MathFloor(lotSize / lotStep) * lotStep;
return lotSize;
}
//+------------------------------------------------------------------+
//| Exponential Moving Average |
//+------------------------------------------------------------------+
void CalculateEMA(int begin, int period, const double &price[], double &result[])
{
if(period <= 0) return;
if(begin < 0) begin = 0;
int priceSize = ArraySize(price);
int resultSize = ArraySize(result);
if(priceSize == 0 || resultSize == 0) return;
if(begin >= priceSize || begin >= resultSize) return;
double SmoothFactor = 2.0 / (1.0 + period);
for(int i = begin; i >= 0; i--)
{
if(i >= priceSize || i >= resultSize) continue;
if(i + 1 >= resultSize)
{
if(price[i] == EMPTY_VALUE)
result[i] = 0;
else
result[i] = price[i];
continue;
}
if(price[i] == EMPTY_VALUE)
result[i] = 0;
else
result[i] = price[i] * SmoothFactor + result[i + 1] * (1.0 - SmoothFactor);
}
}
//+------------------------------------------------------------------+
//| Calculate proper QQE values |
//+------------------------------------------------------------------+
bool CalculateQQE(int bars_needed)
{
int total_bars = bars_needed + StartBar + 10;
int available_bars = iBars(_Symbol, PERIOD_CURRENT);
if(available_bars < total_bars)
{
total_bars = available_bars - 1;
if(total_bars < StartBar + 10)
{
return false;
}
}
if(total_bars < 50)
return false;
if(!ArrayResize(Rsi, total_bars) ||
!ArrayResize(RsiMa, total_bars) ||
!ArrayResize(AtrRsi, total_bars) ||
!ArrayResize(MaAtrRsi, total_bars) ||
!ArrayResize(MaMaAtrRsi, total_bars) ||
!ArrayResize(TrLevelSlow, total_bars))
{
return false;
}
if(CopyBuffer(rsiHandle, 0, 0, total_bars, Rsi) != total_bars)
return false;
ArrayInitialize(RsiMa, 0.0);
ArrayInitialize(AtrRsi, 0.0);
ArrayInitialize(MaAtrRsi, 0.0);
ArrayInitialize(MaMaAtrRsi, 0.0);
ArrayInitialize(TrLevelSlow, 0.0);
if(total_bars > QQE_Smoothing_Factor)
CalculateEMA(total_bars - 1, QQE_Smoothing_Factor, Rsi, RsiMa);
for(int i = total_bars - 2; i >= 0; i--)
{
if(i + 1 < total_bars && i >= 0)
AtrRsi[i] = MathAbs(RsiMa[i + 1] - RsiMa[i]);
}
if(total_bars > Wilders_Period)
{
CalculateEMA(total_bars - 2, Wilders_Period, AtrRsi, MaAtrRsi);
CalculateEMA(total_bars - 2, Wilders_Period, MaAtrRsi, MaMaAtrRsi);
}
int start_index = total_bars - StartBar - 1;
if(start_index < 0) start_index = total_bars - 1;
double tr = 0;
double rsi1 = 0;
if(start_index >= 0 && start_index < total_bars)
{
tr = TrLevelSlow[start_index];
rsi1 = RsiMa[start_index];
}
for(int i = start_index - 1; i >= 0; i--)
{
if(i < 0 || i >= total_bars) continue;
double rsi0 = RsiMa[i];
double dar = MaMaAtrRsi[i] * 4.236;
double dv = tr;
if(rsi0 < tr)
{
tr = rsi0 + dar;
if((rsi1 < dv) && (tr > dv)) tr = dv;
}
else if(rsi0 > tr)
{
tr = rsi0 - dar;
if((rsi1 > dv) && (tr < dv)) tr = dv;
}
TrLevelSlow[i] = tr;
rsi1 = rsi0;
}
return true;
}
//+------------------------------------------------------------------+
//| Update indicators |
//+------------------------------------------------------------------+
void UpdateIndicators()
{
double atrBuffer[];
if(CopyBuffer(atrHandle, 0, ATR_Shift, 1, atrBuffer) > 0)
{
atrValue = NormalizeDouble(atrBuffer[0], digits);
if(atrValue <= 0)
atrValue = 0.0001;
}
else
{
Print("Error getting ATR value: ", GetLastError());
}
if(iBars(_Symbol, PERIOD_CURRENT) > StartBar + 50)
{
CalculateQQE(50);
}
}
//+------------------------------------------------------------------+
//| Check for QQE entry signals with enhanced filters |
//+------------------------------------------------------------------+
void CheckForEntrySignals()
{
if(positionCount >= Max_Positions || !IsTradingAllowed())
return;
if(ArraySize(RsiMa) < 3 || ArraySize(TrLevelSlow) < 3 || atrValue <= 0)
return;
// Additional validation
if(RsiMa[1] == 0 || TrLevelSlow[1] == 0 || RsiMa[2] == 0 || TrLevelSlow[2] == 0)
return;
double currentRsiMa = RsiMa[1];
double prevRsiMa = RsiMa[2];
double currentSmoothed = TrLevelSlow[1];
double prevSmoothed = TrLevelSlow[2];
// Check QQE signal strength
double signalStrength = MathAbs(currentRsiMa - currentSmoothed);
if(signalStrength < QQE_Signal_Threshold)
{
Print("QQE signal too weak: ", signalStrength);
return;
}
// Get trend direction
int trendDirection = GetTrendDirection();
bool buySignal = false;
bool sellSignal = false;
// Enhanced QQE signal detection
if(prevRsiMa <= prevSmoothed && currentRsiMa > currentSmoothed)
{
// Additional divergence filter
if(Use_QQE_Divergence_Filter)
{
if(currentRsiMa < 30) // Coming from oversold
buySignal = true;
}
else
{
buySignal = true;
}
}
if(prevRsiMa >= prevSmoothed && currentRsiMa < currentSmoothed)
{
if(Use_QQE_Divergence_Filter)
{
if(currentRsiMa > 70) // Coming from overbought
sellSignal = true;
}
else
{
sellSignal = true;
}
}
// Apply trend filter
if(Use_Trend_Filter)
{
if(buySignal && trendDirection != 1)
{
Print("Buy signal filtered out - not in uptrend");
buySignal = false;
}
if(sellSignal && trendDirection != -1)
{
Print("Sell signal filtered out - not in downtrend");
sellSignal = false;
}
}
double currentPrice;
double stopLoss, takeProfit;
double bufferInPoints = Extra_Buffer_Pips * 10 * point;
// Execute Buy Signal
if(buySignal && Allow_Long_Positions)
{
currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
stopLoss = NormalizeDouble(currentPrice - (atrValue * SL_ATR_Multiplier) - bufferInPoints, digits);
takeProfit = NormalizeDouble(currentPrice + (atrValue * TP_ATR_Multiplier), digits);
double lotSize = CalculateLotSize(currentPrice, stopLoss);
if(lotSize > 0 && !trade.Buy(lotSize, _Symbol, 0, stopLoss, takeProfit, "Conservative QQE Buy"))
{
Print("Buy order failed with error: ", GetLastError());
}
else if(lotSize > 0)
{
Print("Conservative QQE Buy executed - Lot: ", lotSize, " Entry: ", currentPrice);
Print("SL: ", stopLoss, " TP: ", takeProfit, " Risk: ", Risk_Percent, "%");
lastTradeTime = TimeCurrent();
}
}
// Execute Sell Signal
if(sellSignal && Allow_Short_Positions)
{
currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
stopLoss = NormalizeDouble(currentPrice + (atrValue * SL_ATR_Multiplier) + bufferInPoints, digits);
takeProfit = NormalizeDouble(currentPrice - (atrValue * TP_ATR_Multiplier), digits);
double lotSize = CalculateLotSize(currentPrice, stopLoss);
if(lotSize > 0 && !trade.Sell(lotSize, _Symbol, 0, stopLoss, takeProfit, "Conservative QQE Sell"))
{
Print("Sell order failed with error: ", GetLastError());
}
else if(lotSize > 0)
{
Print("Conservative QQE Sell executed - Lot: ", lotSize, " Entry: ", currentPrice);
Print("SL: ", stopLoss, " TP: ", takeProfit, " Risk: ", Risk_Percent, "%");
lastTradeTime = TimeCurrent();
}
}
}
//+------------------------------------------------------------------+
//| Track trade results and consecutive losses |
//+------------------------------------------------------------------+
void OnTradeTransaction(const MqlTradeTransaction& trans,
const MqlTradeRequest& request,
const MqlTradeResult& result)
{
if(trans.type == TRADE_TRANSACTION_DEAL_ADD)
{
CDealInfo deal;
if(deal.SelectByIndex(trans.deal))
{
if(deal.Magic() == 123456 && deal.Entry() == DEAL_ENTRY_OUT)
{
double profit = deal.Profit();
if(profit < 0)
{
consecutiveLosses++;
Print("Consecutive losses: ", consecutiveLosses);
if(consecutiveLosses >= Max_Consecutive_Losses)
{
tradingHalted = true;
Print("Trading halted after ", Max_Consecutive_Losses, " consecutive losses");
}
}
else
{
consecutiveLosses = 0; // Reset counter on winning trade
tradingHalted = false; // Resume trading
}
}
}
}
}
//+------------------------------------------------------------------+
//| Refresh list of position tickets |
//+------------------------------------------------------------------+
void RefreshPositions()
{
int total = PositionsTotal();
int count = 0;
for(int i = 0; i < total; i++)
{
if(posInfo.SelectByIndex(i))
{
if(posInfo.Symbol() == _Symbol && posInfo.Magic() == 123456)
count++;
}
}
ArrayResize(positionTickets, count);
positionCount = 0;
for(int i = 0; i < total; i++)
{
if(posInfo.SelectByIndex(i))
{
if(posInfo.Symbol() == _Symbol && posInfo.Magic() == 123456)
{
positionTickets[positionCount] = posInfo.Ticket();
positionCount++;
}
}
}
}
//+------------------------------------------------------------------+
//| Manage all positions |
//+------------------------------------------------------------------+
void ManagePositions()
{
RefreshPositions();
double bufferInPoints = Extra_Buffer_Pips * 10 * point;
// Display current info
string info = "Conservative QQE ATR System\n";
info += "Risk per trade: " + DoubleToString(Risk_Percent, 1) + "%\n";
info += "Risk/Reward: 1:" + DoubleToString(TP_ATR_Multiplier/SL_ATR_Multiplier, 1) + "\n";
info += "Current ATR: " + DoubleToString(atrValue, digits) + "\n";
if(ArraySize(RsiMa) > 1 && ArraySize(TrLevelSlow) > 1)
{
info += "RSI MA: " + DoubleToString(RsiMa[1], 2) + "\n";
info += "QQE Smoothed: " + DoubleToString(TrLevelSlow[1], 2) + "\n";
}
info += "Consecutive Losses: " + IntegerToString(consecutiveLosses) + "/" + IntegerToString(Max_Consecutive_Losses) + "\n";
info += "Positions: " + IntegerToString(positionCount);
Comment(info);
// Process existing positions with trailing stops
for(int i = 0; i < positionCount; i++)
{
if(posInfo.SelectByTicket(positionTickets[i]))
{
ProcessPosition(posInfo.PositionType(), posInfo.PriceOpen(),
posInfo.StopLoss(), posInfo.TakeProfit(),
posInfo.Ticket(), bufferInPoints);
}
}
}
//+------------------------------------------------------------------+
//| Process a single position with trailing stops |
//+------------------------------------------------------------------+
void ProcessPosition(ENUM_POSITION_TYPE posType, double openPrice,
double currentSL, double currentTP, ulong ticket,
double bufferInPoints)
{
if(!Use_Trailing_Stop || !Manage_SL)
return;
double newSL = currentSL;
double trailingStartLevel = 0;
double currentPrice = 0;
bool modifyPosition = false;
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if(posType == POSITION_TYPE_BUY)
{
currentPrice = bid;
trailingStartLevel = NormalizeDouble(openPrice + (atrValue * Trail_Start_Multiplier), digits);
if(currentPrice >= trailingStartLevel)
{
double trailingSL = NormalizeDouble(currentPrice - (atrValue * Trail_Step_Multiplier) - bufferInPoints, digits);
if(trailingSL > currentSL || currentSL == 0)
{
newSL = trailingSL;
modifyPosition = true;
}
}
}
else if(posType == POSITION_TYPE_SELL)
{
currentPrice = ask;
trailingStartLevel = NormalizeDouble(openPrice - (atrValue * Trail_Start_Multiplier), digits);
if(currentPrice <= trailingStartLevel)
{
double trailingSL = NormalizeDouble(currentPrice + (atrValue * Trail_Step_Multiplier) + bufferInPoints, digits);
if(trailingSL < currentSL || currentSL == 0)
{
newSL = trailingSL;
modifyPosition = true;
}
}
}
if(modifyPosition)
{
if(trade.PositionModify(ticket, newSL, currentTP))
{
Print("Trailing stop updated for position #", ticket, " New SL: ", newSL);
}
}
}