298 lines
10 KiB
MQL5
298 lines
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
|
||
|
|
}
|
||
|
|
}
|
||
|
|
//+------------------------------------------------------------------+
|