mql5/Experts/QQE_EA.mq5
darashikoh 7973517965 Title
feat: add ERMT PME 1.6 with live input-driven phase and basket protection

Body

create versioned 1.6 EA and module set
wire phase triggers and safety floors to live EA inputs
preserve runner sizing with original-position partial math
add equity-aware daily loss and drawdown protection
add FX basket de-risking for overlapping pairs
remove hidden profile overrides from manual trail and partial settings
keep 1.5 baseline intact while validating 1.6 behavior
2026-04-20 10:35:46 +01:00

298 lines
No EOL
10 KiB
MQL5

//+------------------------------------------------------------------+
//| QQE_ATR_EA.mq5 |
//| Generated by your AI Coding Partner |
//+------------------------------------------------------------------+
#property copyright "Your AI Coding Partner"
#property link ""
#property version "1.03"
#include <Trade\Trade.mqh>
//--- Custom Enumerations for Dropdown Menus
enum ENUM_SIGNAL_MODE
{
MODE_CROSSOVER = 0, // Crossover Only
MODE_THRESHOLD = 1, // Threshold (50 Level) Only
MODE_BOTH = 2 // Both Crossover and Threshold
};
//--- EA Inputs
input string General_Settings = "--- Trading Settings ---";
input double LotSize = 0.1; // Trading Lot Size
input ENUM_SIGNAL_MODE SignalMode = MODE_BOTH; // Entry Signal Logic
input ulong MagicNumber = 123456; // Magic Number
input string Risk_Settings = "--- Dynamic Risk (ATR) ---";
input int AtrPeriod = 14; // ATR Period
input double AtrSlMultiplier = 1.5; // ATR Stop Loss Multiplier
input double AtrTpMultiplier = 2.0; // ATR Take Profit Multiplier
input string Indicator_Settings = "--- QQE Settings ---";
input int SF = 5; // Smoothing Factor
input int RSI_Period = 14; // RSI Period
//--- Global Variables & Objects
CTrade trade;
int myRSI;
int myATR;
int Wilders_Period;
int StartBar;
int prev_calculated = 0;
datetime last_bar_time;
//--- Arrays for Standalone Indicator Calculations
double RsiMa[];
double TrLevelSlow[];
double AtrRsi[];
double MaAtrRsi[];
double Rsi[];
double MaMaAtrRsi[];
//+------------------------------------------------------------------+
//| Exponential Moving Average Engine |
//+------------------------------------------------------------------+
void CalculateEMA(int begin, int period, const double &price[], double &result[])
{
double SmoothFactor = 2.0 / (1.0 + period);
for (int i = begin; i >= 0; i--)
{
if (price[i] == EMPTY_VALUE) result[i] = 0;
else result[i] = price[i] * SmoothFactor + result[i + 1] * (1.0 - SmoothFactor);
}
}
//+------------------------------------------------------------------+
//| Core QQE Mathematics translated from Indicator Loop |
//+------------------------------------------------------------------+
bool UpdateQQEData()
{
int rates_total = iBars(_Symbol, _Period);
if(rates_total <= StartBar) return false;
// Manage dynamic arrays for EA processing
if(ArraySize(Rsi) != rates_total)
{
ArrayResize(Rsi, rates_total);
ArrayResize(RsiMa, rates_total);
ArrayResize(TrLevelSlow, rates_total);
ArrayResize(AtrRsi, rates_total);
ArrayResize(MaAtrRsi, rates_total);
ArrayResize(MaMaAtrRsi, rates_total);
ArraySetAsSeries(Rsi, true);
ArraySetAsSeries(RsiMa, true);
ArraySetAsSeries(TrLevelSlow, true);
ArraySetAsSeries(AtrRsi, true);
ArraySetAsSeries(MaAtrRsi, true);
ArraySetAsSeries(MaMaAtrRsi, true);
}
int counted = prev_calculated - 1;
if (counted < 1)
{
for (int i = 0; i < rates_total; i++)
{
TrLevelSlow[i] = 0.0; AtrRsi[i] = 0.0; MaAtrRsi[i] = 0.0;
Rsi[i] = 0.0; RsiMa[i] = 0.0; MaMaAtrRsi[i] = 0.0;
}
counted = rates_total - StartBar - 1;
}
else
{
counted = rates_total - counted - 1;
if (counted > rates_total - StartBar - 1) counted = rates_total - StartBar - 1;
}
if (CopyBuffer(myRSI, 0, 0, counted + 2, Rsi) != counted + 2) return false;
CalculateEMA(counted + 1, SF, Rsi, RsiMa);
for (int i = counted; i >= 0; i--) AtrRsi[i] = MathAbs(RsiMa[i + 1] - RsiMa[i]);
CalculateEMA(counted, Wilders_Period, AtrRsi, MaAtrRsi);
int i = counted + 1;
double tr = TrLevelSlow[i];
double rsi1 = RsiMa[i];
CalculateEMA(counted, Wilders_Period, MaAtrRsi, MaMaAtrRsi);
while (i > 0)
{
i--;
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;
}
prev_calculated = rates_total;
return true;
}
//+------------------------------------------------------------------+
//| Execute Trade with Dynamic ATR TP/SL |
//+------------------------------------------------------------------+
void ExecuteTrade(ENUM_ORDER_TYPE type)
{
// Fetch Current Price
double price = (type == ORDER_TYPE_BUY) ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID);
// Fetch Current ATR Value
double atrArray[];
ArraySetAsSeries(atrArray, true);
if(CopyBuffer(myATR, 0, 0, 1, atrArray) <= 0)
{
Print("Failed to copy ATR data. Order aborted.");
return;
}
double currentATR = atrArray[0];
// Calculate Dynamic Risk Distances
double slDistance = currentATR * AtrSlMultiplier;
double tpDistance = currentATR * AtrTpMultiplier;
double sl = 0, tp = 0;
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
if (type == ORDER_TYPE_BUY)
{
sl = price - slDistance;
tp = price + tpDistance;
trade.Buy(LotSize, _Symbol, NormalizeDouble(price, digits), NormalizeDouble(sl, digits), NormalizeDouble(tp, digits), "QQE Buy");
}
else if (type == ORDER_TYPE_SELL)
{
sl = price + slDistance;
tp = price - tpDistance;
trade.Sell(LotSize, _Symbol, NormalizeDouble(price, digits), NormalizeDouble(sl, digits), NormalizeDouble(tp, digits), "QQE Sell");
}
}
//+------------------------------------------------------------------+
//| Close Opposite Positions logic |
//+------------------------------------------------------------------+
void CloseOppositePositions(ENUM_POSITION_TYPE pos_type)
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == MagicNumber)
{
if(PositionGetInteger(POSITION_TYPE) == pos_type)
{
trade.PositionClose(ticket);
}
}
}
}
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Initialize Trade settings
trade.SetExpertMagicNumber(MagicNumber);
// Initialize QQE mathematical periods
Wilders_Period = RSI_Period * 2 - 1;
StartBar = MathMax(SF, Wilders_Period);
// Request RSI handle
myRSI = iRSI(_Symbol, _Period, RSI_Period, PRICE_CLOSE);
if(myRSI == INVALID_HANDLE)
{
Print("Error creating RSI indicator handle.");
return(INIT_FAILED);
}
// Request ATR handle
myATR = iATR(_Symbol, _Period, AtrPeriod);
if(myATR == INVALID_HANDLE)
{
Print("Error creating ATR indicator handle.");
return(INIT_FAILED);
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
IndicatorRelease(myRSI);
IndicatorRelease(myATR);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// 1. Only process data on a new bar to avoid mid-candle repainting
datetime current_time = iTime(_Symbol, _Period, 0);
if(current_time == last_bar_time) return;
// 2. Update QQE arrays mathematically
if(!UpdateQQEData()) return;
// 3. Evaluate Trading Logic on the last completed bar (Index 1) and the one before (Index 2)
double currentRsiMa = RsiMa[1];
double prevRsiMa = RsiMa[2];
double currentTr = TrLevelSlow[1];
double prevTr = TrLevelSlow[2];
// Crossover Logic: RsiMA (Blue line) crosses TrLevelSlow (Yellow line)
bool isCrossUp = (prevRsiMa <= prevTr && currentRsiMa > currentTr);
bool isCrossDn = (prevRsiMa >= prevTr && currentRsiMa < currentTr);
// Level 50 Logic (Per the original code's description)
bool isLevelCrossUp = (prevRsiMa < 50 && currentRsiMa >= 50 && currentRsiMa > currentTr);
bool isLevelCrossDn = (prevRsiMa > 50 && currentRsiMa <= 50 && currentRsiMa < currentTr);
// Filter signals based on the user's selected input mode
bool buySignal = false;
bool sellSignal = false;
if(SignalMode == MODE_CROSSOVER || SignalMode == MODE_BOTH)
{
if(isCrossUp) buySignal = true;
if(isCrossDn) sellSignal = true;
}
if(SignalMode == MODE_THRESHOLD || SignalMode == MODE_BOTH)
{
if(isLevelCrossUp) buySignal = true;
if(isLevelCrossDn) sellSignal = true;
}
// 4. Execute Trades
if (buySignal)
{
CloseOppositePositions(POSITION_TYPE_SELL);
if(PositionsTotal() == 0) ExecuteTrade(ORDER_TYPE_BUY);
last_bar_time = current_time; // Mark bar as handled
}
else if (sellSignal)
{
CloseOppositePositions(POSITION_TYPE_BUY);
if(PositionsTotal() == 0) ExecuteTrade(ORDER_TYPE_SELL);
last_bar_time = current_time; // Mark bar as handled
}
}
//+------------------------------------------------------------------+