Gold_Fibo_EA_v1.27_AntiWick_Fusion.mq5

Zeroboyz - MQL5 Expert Advisor Source Code

360 lines
#property version "1.27_AntiWick_Fusion"
#property strict
#include <Trade\Trade.mqh>

CTrade trade;

// Input parameters
input int InpBuySpikePoints = 800; // Dual Engine input
input int InpSellSpikePoints = 800;
input int InpTrendPoints = 1000;
input int InpTrendBars = 5;
input int InpMinSameDirCandles = 3;

// Anti-Wick Filter inputs
input double InpWickRatioMax = 0.35;
input double InpMinBodyRatio = 0.50;

// v1.25 Protections inputs
input bool InpUseEMATrendFilter = true;
input int InpEMAPeriod = 200;
input ENUM_TIMEFRAMES InpEMATimeframe = PERIOD_M5;
input bool InpEnableWhipsawFilter = true;
input int InpMomentumBreakoutPts = 150;

// Scale-out Multi-Position inputs
input int InpNumPositions = 4;
input double InpBaseLot = 0.01;
input int InpTakeProfitPts1 = 300;
input int InpTakeProfitPts2 = 600;
input int InpTakeProfitPts3 = 900;
input int InpTakeProfitPts4 = 1200;

// Risk & Limits inputs
input double InpDailyLossLimitUSD = 50;
input double InpMaxDailyGivebackUSD = 30;
input int InpMaxWinsPerDay = 5;

// POE2 Warp / Rage Quit Stun inputs
input double InpWarpProfitUSD = 100;
input int InpWarpCooldownMin = 30;
input int InpMaxLossStreak = 2;
input int InpRageQuitStunMin = 60;

// Time & Friday inputs
input bool InpUseTimeFilter = false;
input int InpStartHour = 8;
input int InpEndHour = 20;
input bool InpUseFridayClose = true;
input int InpFridayCloseHour = 21;

// Magic number
input int InpMagicNumber = 127001;

// Global variables for daily statistics
datetime g_lastResetDate = 0;
double g_dailyProfitUSD = 0.0;
int g_dailyWins = 0;
int g_consecutiveLosses = 0;
datetime g_lastWarpTime = 0;
datetime g_lastRageQuitTime = 0;

// EMA handle
int g_emaHandle = INVALID_HANDLE;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){
   // Initialize EMA if required
   if(InpUseEMATrendFilter){
      g_emaHandle = iMA(_Symbol, InpEMATimeframe, InpEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
      if(g_emaHandle == INVALID_HANDLE){
         Print("Failed to create EMA handle");
         return INIT_FAILED;
      }
   }
   // Reset daily stats
   g_lastResetDate = DateCurrent();
   g_dailyProfitUSD = 0.0;
   g_dailyWins = 0;
   g_consecutiveLosses = 0;
   g_lastWarpTime = 0;
   g_lastRageQuitTime = 0;
   return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason){
   if(g_emaHandle != INVALID_HANDLE){
      IndicatorRelease(g_emaHandle);
   }
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){
   // Daily reset at midnight server time
   if(DateCurrent() != g_lastResetDate){
      g_lastResetDate = DateCurrent();
      g_dailyProfitUSD = 0.0;
      g_dailyWins = 0;
      g_consecutiveLosses = 0;
   }

   // Apply time filter if enabled
   if(InpUseTimeFilter){
      int hour = TimeHour(TimeCurrent());
      if(hour < InpStartHour || hour > InpEndHour) return;
   }

   // Friday close check
   if(CheckFridayClose()) return;

   // Warp and Rage Quit cooldowns
   if(CheckWarpCooldown()) return;
   if(CheckRageQuitStun()) return;

   // Daily loss & giveback limits
   if(CalculateNetDailyProfit() <= -InpDailyLossLimitUSD) return;
   if(g_dailyProfitUSD < 0 && MathAbs(g_dailyProfitUSD) > InpMaxDailyGivebackUSD) return;

   // Determine direction
   bool isBuy = IsSpike(true) && IsTrend(true);
   bool isSell = IsSpike(false) && IsTrend(false);

   // Anti-Wick filter
   if(!CheckAntiWick(isBuy, isSell)) return;

   // EMA trend alignment
   if(InpUseEMATrendFilter && !IsEMATrendAligned(isBuy, isSell)) return;

   // Whipsaw detection
   if(InpEnableWhipsawFilter && DetectWhipsaw()) return;

   // Execute positions if signal present
   if(isBuy) ExecuteMultiplePositions(ORDER_TYPE_BUY);
   if(isSell) ExecuteMultiplePositions(ORDER_TYPE_SELL);

   // Manage existing positions (breakeven, trailing)
   ManagePositions();
}

//+------------------------------------------------------------------+
//| Check for Friday close condition                                 |
//+------------------------------------------------------------------+
bool CheckFridayClose(){
   if(!InpUseFridayClose) return false;
   datetime now = TimeCurrent();
   if(TimeDayOfWeek(now) == 5){ // Friday
      int hour = TimeHour(now);
      if(hour >= InpFridayCloseHour) {
         CloseAllPositions();
         return true;
      }
   }
   return false;
}

//+------------------------------------------------------------------+
//| Warp cooldown check                                              |
//+------------------------------------------------------------------+
bool CheckWarpCooldown(){
   if(g_lastWarpTime==0) return false;
   datetime now = TimeCurrent();
   if((now - g_lastWarpTime) < (InpWarpCooldownMin*60)) return true;
   return false;
}

//+------------------------------------------------------------------+
//| Rage Quit stun check                                             |
//+------------------------------------------------------------------+
bool CheckRageQuitStun(){
   if(g_lastRageQuitTime==0) return false;
   datetime now = TimeCurrent();
   if((now - g_lastRageQuitTime) < (InpRageQuitStunMin*60)) return true;
   return false;
}

//+------------------------------------------------------------------+
//| Calculate net daily profit in USD (approx using SymbolInfoDouble)  |
//+------------------------------------------------------------------+
double CalculateNetDailyProfit(){
   double profit = 0.0;
   for(int i=PositionsTotal()-1; i>=0; i--){
      ulong ticket = PositionGetTicket(i);
      if(PositionGetInteger(POSITION_MAGIC) != InpMagicNumber) continue;
      profit += PositionGetDouble(POSITION_PROFIT);
   }
   // Convert to USD if needed - assuming account currency is USD for simplicity
   g_dailyProfitUSD = profit;
   return profit;
}

//+------------------------------------------------------------------+
//| Anti-Wick filter implementation                                 |
//+------------------------------------------------------------------+
bool CheckAntiWick(bool buySignal, bool sellSignal){
   MqlRates rates[];
   int copied = CopyRates(_Symbol, PERIOD_CURRENT, 0, 1, rates);
   if(copied <= 0) return false;
   double high = rates[0].high;
   double low = rates[0].low;
   double open = rates[0].open;
   double close = rates[0].close;
   double body = MathAbs(close - open);
   double totalRange = high - low;
   double upperWick = high - MathMax(open, close);
   double lowerWick = MathMin(open, close) - low;
   double wickRatio = (buySignal) ? upperWick/totalRange : lowerWick/totalRange;
   double bodyRatio = body/totalRange;
   if(wickRatio > InpWickRatioMax) return false;
   if(bodyRatio < InpMinBodyRatio) return false;
   return true;
}

//+------------------------------------------------------------------+
//| Spike detection function                                         |
//+------------------------------------------------------------------+
bool IsSpike(bool isBuy){
   // Simple implementation: compare current candle range against input points
   MqlRates rates[];
   if(CopyRates(_Symbol, PERIOD_CURRENT, 0, 2, rates) < 2) return false;
   double range = rates[0].high - rates[0].low;
   int points = isBuy ? InpBuySpikePoints : InpSellSpikePoints;
   return (range * _Point) >= points * _Point;
}

//+------------------------------------------------------------------+
//| Trend detection function                                         |
//+------------------------------------------------------------------+
bool IsTrend(bool isBuy){
   // Count consecutive candles in same direction meeting trend points
   MqlRates rates[];
   int copied = CopyRates(_Symbol, PERIOD_CURRENT, 0, InpTrendBars+1, rates);
   if(copied < InpTrendBars+1) return false;
   int sameDirCount = 0;
   for(int i=1; i<=InpTrendBars; i++){
      double prevClose = rates[i].close;
      double curClose = rates[i-1].close;
      if(isBuy && curClose > prevClose) sameDirCount++;
      if(!isBuy && curClose < prevClose) sameDirCount++;
   }
   return sameDirCount >= InpMinSameDirCandles && (sameDirCount * _Point) >= InpTrendPoints * _Point;
}

//+------------------------------------------------------------------+
//| EMA trend alignment check                                        |
//+------------------------------------------------------------------+
bool IsEMATrendAligned(bool isBuy, bool isSell){
   if(g_emaHandle == INVALID_HANDLE) return false;
   double ema[];
   if(CopyBuffer(g_emaHandle, 0, 0, 2, ema) < 2) return false;
   double lastClose = Close[0];
   double prevEma = ema[1];
   double curEma = ema[0];
   if(isBuy) return lastClose > curEma && curEma > prevEma;
   if(isSell) return lastClose < curEma && curEma < prevEma;
   return false;
}

//+------------------------------------------------------------------+
//| Whipsaw detection function                                       |
//+------------------------------------------------------------------+
bool DetectWhipsaw(){
   // Simple volatility based whipsaw detection
   MqlRates rates[];
   if(CopyRates(_Symbol, PERIOD_CURRENT, 0, 5, rates) < 5) return false;
   double sumRange = 0.0;
   for(int i=0;i<5;i++) sumRange += rates[i].high - rates[i].low;
   double avgRange = sumRange/5.0;
   // If current range exceeds threshold based on momentum breakout points
   double curRange = rates[0].high - rates[0].low;
   return curRange > (InpMomentumBreakoutPts * _Point) && curRange > 2*avgRange;
}

//+------------------------------------------------------------------+
//| Execute multiple positions with scaling                           |
//+------------------------------------------------------------------+
void ExecuteMultiplePositions(ENUM_ORDER_TYPE type){
   // Determine existing position count for this magic number
   int existing = 0;
   for(int i=PositionsTotal()-1;i>=0;i--){
      ulong ticket = PositionGetTicket(i);
      if(PositionGetInteger(POSITION_MAGIC)!=InpMagicNumber) continue;
      if(PositionGetInteger(POSITION_TYPE)==type) existing++;
   }
   if(existing >= InpNumPositions) return; // max positions reached
   // Determine lot size based on position index
   double lot = InpBaseLot * MathPow(2, existing);
   // Determine TP based on position index
   int tpPts = 0;
   switch(existing){
      case 0: tpPts = InpTakeProfitPts1; break;
      case 1: tpPts = InpTakeProfitPts2; break;
      case 2: tpPts = InpTakeProfitPts3; break;
      case 3: tpPts = InpTakeProfitPts4; break;
   }
   double price = (type==ORDER_TYPE_BUY) ? SymbolInfoDouble(_Symbol,SYMBOL_ASK) : SymbolInfoDouble(_Symbol,SYMBOL_BID);
   double tpPrice = (type==ORDER_TYPE_BUY) ? price + tpPts*_Point : price - tpPts*_Point;
   trade.SetExpertMagicNumber(InpMagicNumber);
   bool result = trade.PositionOpen(_Symbol, type, lot, price, 0, tpPrice, "");
   if(result){
      Print("Opened position ",EnumToString(type)," lot=",lot);
   } else {
      Print("Failed to open position: ",trade.ResultComment());
   }
}

//+------------------------------------------------------------------+
//| Manage open positions (breakeven, trailing)                      |
//+------------------------------------------------------------------+
void ManagePositions(){
   for(int i=PositionsTotal()-1;i>=0;i--){
      ulong ticket = PositionGetTicket(i);
      if(PositionGetInteger(POSITION_MAGIC)!=InpMagicNumber) continue;
      ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
      double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
      double curPrice = (posType==POSITION_TYPE_BUY) ? SymbolInfoDouble(_Symbol,SYMBOL_BID) : SymbolInfoDouble(_Symbol,SYMBOL_ASK);
      double profit = PositionGetDouble(POSITION_PROFIT);
      // Breakeven at 50% of TP distance
      double tp = PositionGetDouble(POSITION_TP);
      double breakevenPrice = (posType==POSITION_TYPE_BUY) ? openPrice + (tp - openPrice)/2 : openPrice - (openPrice - tp)/2;
      if(posType==POSITION_TYPE_BUY && curPrice >= breakevenPrice && PositionGetDouble(POSITION_SL)==0.0){
         trade.PositionModify(ticket, breakevenPrice, 0.0);
      }
      if(posType==POSITION_TYPE_SELL && curPrice <= breakevenPrice && PositionGetDouble(POSITION_SL)==0.0){
         trade.PositionModify(ticket, breakevenPrice, 0.0);
      }
      // Trailing stop 30% of TP distance
      double trail = 0.3 * MathAbs(tp - openPrice);
      double newSL = (posType==POSITION_TYPE_BUY) ? curPrice - trail : curPrice + trail;
      if(newSL > PositionGetDouble(POSITION_SL)){
         trade.PositionModify(ticket, newSL, 0.0);
      }
   }
}

//+------------------------------------------------------------------+
//| Close all positions (used for Friday close)                     |
//+------------------------------------------------------------------+
void CloseAllPositions(){
   for(int i=PositionsTotal()-1;i>=0;i--){
      ulong ticket = PositionGetTicket(i);
      if(PositionGetInteger(POSITION_MAGIC)!=InpMagicNumber) continue;
      trade.PositionClose(ticket);
   }
}

//+------------------------------------------------------------------+
//| Expert shutdown                                                  |
//+------------------------------------------------------------------+
void OnDeinit(const int reason){
   // Already defined earlier - keep for completeness
   if(g_emaHandle!=INVALID_HANDLE) IndicatorRelease(g_emaHandle);
}

//+------------------------------------------------------------------+