#property copyright "Algobot"
#property link      "https://www.algobot.live"
#property version   "1.00"
#property strict

// DualRsiMomentumShift
// --------------------
// Trend-following momentum system: signal is the crossover of a FAST RSI over a SLOW
// RSI (both on the close series). Three gates must ALL agree before a trade:
//   1) DIRECTION  - fast crosses slow the right way.
//   2) REGIME     - price on the correct side of a trend EMA AND that EMA sloping our way.
//   3) MOMENTUM   - fast RSI beyond the midline (bullish/bearish territory) but not yet
//                   stretched past MaxEntryRsi (skip over-extended pushes).
// Risk is ATR-scaled: stop = AtrMult * ATR, target = RewardRatio * stop distance.
// One position per magic; optional early close when the RSI lines cross back.

#include <Trade\Trade.mqh>
CTrade trade;

//--- inputs (mirror DescribeParameters defaults) -----------------------------
input int    FastRsiPeriod       = 7;      // Fast RSI period - immediate momentum line
input int    SlowRsiPeriod       = 21;     // Slow RSI period - momentum baseline
input int    EmaTrendPeriod      = 50;     // Trend-regime EMA period
input double Midline             = 50.0;   // Momentum midline gate
input double MaxEntryRsi         = 72.0;   // Upper cap for longs (short cap = 100-this)
input int    AtrPeriod           = 14;     // ATR period for stop/target scaling
input double AtrMult             = 1.6;    // Stop distance = AtrMult * ATR
input double RewardRatio         = 1.7;    // TP distance as a multiple of stop distance
input int    ExitOnOppositeCross = 1;      // 1 = close on reverse cross; 0 = let SL/TP run
input int    MaxSpreadPoints     = 30;     // Skip entry if spread wider than this (points)
input double Lots                = 0.10;   // Trade volume
input long   Magic               = 7042;   // Magic number

//--- indicator handles -------------------------------------------------------
int g_hFast = INVALID_HANDLE;
int g_hSlow = INVALID_HANDLE;
int g_hEma  = INVALID_HANDLE;
int g_hAtr  = INVALID_HANDLE;

//--- runtime state -----------------------------------------------------------
int g_slowPeriod = 21;   // effective slow period (may be bumped by the guard)

//+------------------------------------------------------------------+
int OnInit()
{
   trade.SetExpertMagicNumber(Magic);

   // Guard: the fast line must genuinely be faster than the slow baseline.
   g_slowPeriod = SlowRsiPeriod;
   if(FastRsiPeriod >= g_slowPeriod)
      g_slowPeriod = FastRsiPeriod + 1;

   g_hFast = iRSI(_Symbol, _Period, FastRsiPeriod, PRICE_CLOSE);
   g_hSlow = iRSI(_Symbol, _Period, g_slowPeriod, PRICE_CLOSE);
   g_hEma  = iMA (_Symbol, _Period, EmaTrendPeriod, 0, MODE_EMA, PRICE_CLOSE);
   g_hAtr  = iATR(_Symbol, _Period, AtrPeriod);

   if(g_hFast == INVALID_HANDLE || g_hSlow == INVALID_HANDLE ||
      g_hEma  == INVALID_HANDLE || g_hAtr  == INVALID_HANDLE)
   {
      Print("DRMS: failed to create indicator handles");
      return INIT_FAILED;
   }

   return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   if(g_hFast != INVALID_HANDLE) IndicatorRelease(g_hFast);
   if(g_hSlow != INVALID_HANDLE) IndicatorRelease(g_hSlow);
   if(g_hEma  != INVALID_HANDLE) IndicatorRelease(g_hEma);
   if(g_hAtr  != INVALID_HANDLE) IndicatorRelease(g_hAtr);
}

//+------------------------------------------------------------------+
void OnTick()
{
   // Act once per newly-closed bar.
   if(!IsNewBar()) return;

   // Pull the last two closed-bar readings so we can detect a crossover.
   // Index 1 = last closed bar ("now"); index 2 = the bar before ("prev").
   double fast[], slow[], ema[], atrBuf[];
   ArraySetAsSeries(fast,   true);
   ArraySetAsSeries(slow,   true);
   ArraySetAsSeries(ema,    true);
   ArraySetAsSeries(atrBuf, true);

   if(CopyBuffer(g_hFast, 0, 0, 3, fast)   < 3) return;
   if(CopyBuffer(g_hSlow, 0, 0, 3, slow)   < 3) return;
   if(CopyBuffer(g_hEma,  0, 0, 3, ema)    < 3) return;
   if(CopyBuffer(g_hAtr,  0, 0, 2, atrBuf) < 2) return;

   double fastNow = fast[1], prevFast = fast[2];
   double slowNow = slow[1], prevSlow = slow[2];
   double emaNow  = ema[1],  prevEma  = ema[2];
   double atr     = atrBuf[1];

   // Detect the crossover between the previous closed bar and this one.
   bool crossUp   = (prevFast <= prevSlow) && (fastNow > slowNow);
   bool crossDown = (prevFast >= prevSlow) && (fastNow < slowNow);

   double price     = iClose(_Symbol, _Period, 1);   // last closed bar's close
   bool   emaRising  = emaNow > prevEma;
   bool   emaFalling = emaNow < prevEma;

   //----- Manage an existing position: optional early exit on momentum flip ---
   if(HasPosition(Magic))
   {
      if(ExitOnOppositeCross == 1)
      {
         for(int i = PositionsTotal()-1; i >= 0; i--)
         {
            ulong t = PositionGetTicket(i);
            if(PositionSelectByTicket(t) &&
               PositionGetString(POSITION_SYMBOL) == _Symbol &&
               PositionGetInteger(POSITION_MAGIC) == Magic)
            {
               long ptype = PositionGetInteger(POSITION_TYPE);
               if(ptype == POSITION_TYPE_BUY && crossDown)
                  trade.PositionClose(t);
               else if(ptype == POSITION_TYPE_SELL && crossUp)
                  trade.PositionClose(t);
            }
         }
      }
      return;   // one position per magic - don't stack entries
   }

   // Spread gate for fresh entries.
   if((long)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) > MaxSpreadPoints) return;

   // ATR sets the risk distance; skip if it is degenerate.
   if(atr <= 0.0) return;
   double stopDist = AtrMult * atr;
   if(stopDist <= 0.0) return;

   double shortCap = 100.0 - MaxEntryRsi;   // mirror of the long over-extension cap

   //---- LONG: momentum shifts up in a rising-EMA uptrend, not yet over-extended ----
   bool longOk = crossUp
                 && fastNow > Midline
                 && fastNow < MaxEntryRsi
                 && price   > emaNow
                 && emaRising;
   if(longOk)
   {
      double entry = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
      double sl    = entry - stopDist;
      double tp    = entry + RewardRatio * stopDist;
      trade.Buy(Lots, _Symbol, 0.0, sl, tp, "DRMS long");
      PrintFormat("DRMS LONG fast %.1f x slow %.1f up, px>%.5f sl=%.5f tp=%.5f",
                  fastNow, slowNow, emaNow, sl, tp);
      return;
   }

   //---- SHORT: momentum shifts down in a falling-EMA downtrend, not yet over-extended ----
   bool shortOk = crossDown
                  && fastNow < Midline
                  && fastNow > shortCap
                  && price   < emaNow
                  && emaFalling;
   if(shortOk)
   {
      double entry = SymbolInfoDouble(_Symbol, SYMBOL_BID);
      double sl    = entry + stopDist;
      double tp    = entry - RewardRatio * stopDist;
      trade.Sell(Lots, _Symbol, 0.0, sl, tp, "DRMS short");
      PrintFormat("DRMS SHORT fast %.1f x slow %.1f down, px<%.5f sl=%.5f tp=%.5f",
                  fastNow, slowNow, emaNow, sl, tp);
   }
}

//+------------------------------------------------------------------+
bool IsNewBar()
{
   static datetime last = 0;
   datetime cur = iTime(_Symbol, _Period, 0);
   if(cur != last){ last = cur; return true; }
   return false;
}

//+------------------------------------------------------------------+
bool HasPosition(long magic)
{
   for(int i = PositionsTotal()-1; i >= 0; i--)
   {
      ulong t = PositionGetTicket(i);
      if(PositionSelectByTicket(t) &&
         PositionGetString(POSITION_SYMBOL) == _Symbol &&
         PositionGetInteger(POSITION_MAGIC) == magic) return true;
   }
   return false;
}
//+------------------------------------------------------------------+
