//+------------------------------------------------------------------+
//|                                     07_EA_AllAverages_Cross.mq5  |
//|                                  Copyright 2026, FXおもしろラボ  |
//|                                      https://fx-omoshiro-lab.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, FXおもしろラボ"
#property link      "https://fx-omoshiro-lab.com"
#property version   "1.00"

#include <Trade\Trade.mqh>

CTrade         trade;

enum ENUM_MA_METHOD_ALL
  {
   MA_SMA=0,         // SMA - Simple Moving Average
   MA_EMA=1,         // EMA - Exponential Moving Average
   MA_WILDER=2,      // Wilder - Wilder Exponential Moving Average
   MA_LWMA=3,        // LWMA - Linear Weighted Moving Average
   MA_SINEWMA=4,     // SineWMA - Sine Weighted Moving Average
   MA_TRIMA=5,       // TriMA - Triangular Moving Average
   MA_LSMA=6,        // LSMA - Least Square Moving Average
   MA_SMMA=7,        // SMMA - Smoothed Moving Average
   MA_HMA=8,         // HMA - Hull Moving Average
   MA_ZLEMA=9,       // ZeroLagEMA - Zero-Lag Exponential Moving Average
   MA_DEMA=10,       // DEMA - Double Exponential Moving Average
   MA_T3=11,         // T3 - by T.Tillson
   MA_ITREND=12,     // ITrend - Instantaneous Trendline
   MA_MEDIAN=13,     // Median - Moving Median
   MA_GEOMEAN=14,    // GeoMean - Geometric Mean
   MA_REMA=15,       // REMA - Regularized EMA
   MA_ILRS=16,       // ILRS - Integral of Linear Regression Slope
   MA_IE2=17,        // IE/2 - Combination of LSMA and ILRS
   MA_TRIMAGEN=18,   // TriMAgen - Triangular MA generalized
   MA_VWMA=19,       // VWMA - Volume Weighted Moving Average
   MA_JSMOOTH=20     // JSmooth - Smoothing by Mark Jurik
  };

//--- Input Group: Trade Settings
input string               inpSettingsTrade        = "--- Trade Settings ---";
input double               inpLots                 = 0.1;              // Lots
input int                  inpStopLoss             = 0;                // Stop Loss (points, 0 = off)
input int                  inpTakeProfit           = 0;                // Take Profit (points, 0 = off)
input int                  inpTrailingStop         = 0;                // Trailing Stop (points, 0 = off)
input bool                 inpReverseAtCross       = true;             // Close position on opposite cross

//--- Input Group: Fast MA
input string               inpSettingsFast         = "--- Fast MA Settings ---";
input ENUM_MA_METHOD_ALL   inpFastMethod           = MA_HMA;           // Fast MA Method
input int                  inpFastPeriod           = 14;               // Fast MA Period

//--- Input Group: Slow MA
input string               inpSettingsSlow         = "--- Slow MA Settings ---";
input ENUM_MA_METHOD_ALL   inpSlowMethod           = MA_JSMOOTH;       // Slow MA Method
input int                  inpSlowPeriod           = 50;               // Slow MA Period

//--- Input Group: Universal MA Settings
input string               inpSettingsUni          = "--- Universal MA Settings ---";
input ENUM_APPLIED_PRICE   inpAppliedPrice         = PRICE_CLOSE;      // Applied Price
input double               inpT3VolumeFactor       = 0.7;              // Volume Factor (for T3)
input double               inpREMALambda           = 0.5;              // Lambda (for REMA)
input double               inpJurikPhase           = 0.0;              // Phase (for JSmooth)

//--- Input Group: ADX Trend Filter
input string               inpSettingsFilter       = "--- ADX Trend Filter ---";
input bool                 inpUseADX               = false;            // Use ADX Filter
input int                  inpADXPeriod            = 14;               // ADX Period
input double               inpADXThreshold         = 25.0;             // ADX Threshold (Minimum trend strength)

//--- Global Variables
string         detected_symbol;
int            handle_fast_ma;
int            handle_slow_ma;
int            handle_adx;

double         buffer_fast[];
double         buffer_slow[];
double         buffer_adx[];

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   detected_symbol = _Symbol;
   trade.SetExpertMagicNumber(20261007);
   
   // Create indicator handles from our previously compiled indicator OR standard ones if preferred
   // BUT, to keep the EA self-contained and ultra fast relying on our newly ported code,
   // we actually call our custom indicator '10_AllAverages_v2_5.ex5' built earlier!
   
   handle_fast_ma = iCustom(detected_symbol, _Period, "10_AllAverages_v2_5",
                            "--- General Settings ---",
                            inpFastMethod, inpFastPeriod, inpAppliedPrice,
                            inpT3VolumeFactor, inpREMALambda, inpJurikPhase);
                            
   if(handle_fast_ma == INVALID_HANDLE)
     {
      Print("Failed to create handle for Fast MA. Make sure '10_AllAverages_v2_5.ex5' is compiled in Indicators folder.");
      return(INIT_FAILED);
     }
     
   handle_slow_ma = iCustom(detected_symbol, _Period, "10_AllAverages_v2_5",
                            "--- General Settings ---",
                            inpSlowMethod, inpSlowPeriod, inpAppliedPrice,
                            inpT3VolumeFactor, inpREMALambda, inpJurikPhase);
                            
   if(handle_slow_ma == INVALID_HANDLE)
     {
      Print("Failed to create handle for Slow MA.");
      return(INIT_FAILED);
     }
     
   if(inpUseADX)
     {
      handle_adx = iADX(detected_symbol, _Period, inpADXPeriod);
      if(handle_adx == INVALID_HANDLE) return(INIT_FAILED);
      ArraySetAsSeries(buffer_adx, true);
     }

   ArraySetAsSeries(buffer_fast, true);
   ArraySetAsSeries(buffer_slow, true);
   
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   IndicatorRelease(handle_fast_ma);
   IndicatorRelease(handle_slow_ma);
   if(inpUseADX) IndicatorRelease(handle_adx);
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   // Only run on new bar to avoid repainting cross issues
   static datetime last_bar_time;
   datetime current_bar_time = iTime(detected_symbol, _Period, 0);
   if(current_bar_time == last_bar_time) return;
   
   // Get indicator data for last 2 closed bars (index 1 and 2)
   if(CopyBuffer(handle_fast_ma, 0, 1, 3, buffer_fast) <= 0) return;
   if(CopyBuffer(handle_slow_ma, 0, 1, 3, buffer_slow) <= 0) return;
   
   // Check for EMPTY_VALUE
   if(buffer_fast[1] == EMPTY_VALUE || buffer_slow[1] == EMPTY_VALUE || 
      buffer_fast[2] == EMPTY_VALUE || buffer_slow[2] == EMPTY_VALUE) return;

   // --- ADX Filter ---
   bool trend_ok = true;
   if(inpUseADX)
     {
      if(CopyBuffer(handle_adx, 0, 1, 2, buffer_adx) <= 0) return;
      if(buffer_adx[1] < inpADXThreshold) trend_ok = false;
     }

   // --- Cross Logic ---
   bool buy_cross = (buffer_fast[2] <= buffer_slow[2] && buffer_fast[1] > buffer_slow[1]);
   bool sell_cross = (buffer_fast[2] >= buffer_slow[2] && buffer_fast[1] < buffer_slow[1]);
   
   // --- Position Management ---
   ManageTrailingStop();
   
   if(buy_cross)
     {
      if(inpReverseAtCross) ClosePositions(POSITION_TYPE_SELL);
      if(trend_ok && !HasOpenPosition(POSITION_TYPE_BUY))
        {
         double ask = SymbolInfoDouble(detected_symbol, SYMBOL_ASK);
         double sl = (inpStopLoss > 0) ? ask - (inpStopLoss * SymbolInfoDouble(detected_symbol, SYMBOL_POINT)) : 0;
         double tp = (inpTakeProfit > 0) ? ask + (inpTakeProfit * SymbolInfoDouble(detected_symbol, SYMBOL_POINT)) : 0;
         trade.Buy(inpLots, detected_symbol, ask, sl, tp, "AllAverages Buy");
         last_bar_time = current_bar_time;
        }
     }
     
   if(sell_cross)
     {
      if(inpReverseAtCross) ClosePositions(POSITION_TYPE_BUY);
      if(trend_ok && !HasOpenPosition(POSITION_TYPE_SELL))
        {
         double bid = SymbolInfoDouble(detected_symbol, SYMBOL_BID);
         double sl = (inpStopLoss > 0) ? bid + (inpStopLoss * SymbolInfoDouble(detected_symbol, SYMBOL_POINT)) : 0;
         double tp = (inpTakeProfit > 0) ? bid - (inpTakeProfit * SymbolInfoDouble(detected_symbol, SYMBOL_POINT)) : 0;
         trade.Sell(inpLots, detected_symbol, bid, sl, tp, "AllAverages Sell");
         last_bar_time = current_bar_time;
        }
     }
  }

//+------------------------------------------------------------------+
//| Helper Functions                                                 |
//+------------------------------------------------------------------+
bool HasOpenPosition(ENUM_POSITION_TYPE type)
  {
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      ulong ticket = PositionGetTicket(i);
      if(PositionGetString(POSITION_SYMBOL) == detected_symbol && PositionGetInteger(POSITION_MAGIC) == 20261007)
        {
         if(PositionGetInteger(POSITION_TYPE) == type) return true;
        }
     }
   return false;
  }

void ClosePositions(ENUM_POSITION_TYPE type)
  {
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      ulong ticket = PositionGetTicket(i);
      if(PositionGetString(POSITION_SYMBOL) == detected_symbol && PositionGetInteger(POSITION_MAGIC) == 20261007)
        {
         if(PositionGetInteger(POSITION_TYPE) == type) trade.PositionClose(ticket);
        }
     }
  }

void ManageTrailingStop()
  {
   if(inpTrailingStop <= 0) return;
   
   double point = SymbolInfoDouble(detected_symbol, SYMBOL_POINT);
   double ts_price_dist = inpTrailingStop * point;
   
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      ulong ticket = PositionGetTicket(i);
      if(PositionGetString(POSITION_SYMBOL) == detected_symbol && PositionGetInteger(POSITION_MAGIC) == 20261007)
        {
         double open_price = PositionGetDouble(POSITION_PRICE_OPEN);
         double current_sl = PositionGetDouble(POSITION_SL);
         
         if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
           {
            double bid = SymbolInfoDouble(detected_symbol, SYMBOL_BID);
            if(bid - open_price > ts_price_dist)
              {
               double new_sl = NormalizeDouble(bid - ts_price_dist, (int)SymbolInfoInteger(detected_symbol, SYMBOL_DIGITS));
               if(current_sl == 0 || new_sl > current_sl)
                 {
                  trade.PositionModify(ticket, new_sl, PositionGetDouble(POSITION_TP));
                 }
              }
           }
         else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
           {
            double ask = SymbolInfoDouble(detected_symbol, SYMBOL_ASK);
            if(open_price - ask > ts_price_dist)
              {
               double new_sl = NormalizeDouble(ask + ts_price_dist, (int)SymbolInfoInteger(detected_symbol, SYMBOL_DIGITS));
               if(current_sl == 0 || new_sl < current_sl)
                 {
                  trade.PositionModify(ticket, new_sl, PositionGetDouble(POSITION_TP));
                 }
              }
           }
        }
     }
  }
