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

// MomentumGlidePullback
// ---------------------
// Trend-continuation pullback rider. An established trend is defined by a fast EMA
// above/below a slow EMA whose own slope agrees (rising for longs, falling for shorts).
// Inside that trend we wait for a shallow pullback that cools RSI back across its
// midline, then "glide" back in with the trend the moment RSI crosses the midline
// again in the trend direction on a bar that closes back beyond the fast EMA.
// Stops and targets are ATR-scaled so risk adapts to the selected symbol/timeframe.
//
// All logic is evaluated on the most recently CLOSED bar (shift 1), matching the C#
// source which appends bar shift-1 to its rolling buffers and computes indicators
// on those buffers once per newly opened bar.

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

// --- inputs ---
input int    EmaFast       = 20;      // Fast EMA period
input int    EmaSlow       = 50;      // Slow EMA period
input int    SlopeLookback = 5;       // Bars back for slow-EMA slope
input int    RsiPeriod     = 14;      // RSI period
input double RsiMid        = 50.0;    // RSI midline (long trigger)
input double RsiCap        = 72.0;    // RSI exhaustion cap
input int    AtrPeriod     = 14;      // ATR period
input double AtrSlMult     = 1.8;     // ATR stop-loss multiplier
input double AtrTpMult     = 3.0;     // ATR take-profit multiplier
input double Lots          = 0.10;    // Order volume
input long   Magic         = 730501;  // Magic number

// --- indicator handles ---
int g_emaFast = INVALID_HANDLE;
int g_emaSlow = INVALID_HANDLE;
int g_rsi     = INVALID_HANDLE;
int g_atr     = INVALID_HANDLE;

int OnInit()
{
    trade.SetExpertMagicNumber(Magic);

    g_emaFast = iMA(_Symbol, _Period, EmaFast, 0, MODE_EMA, PRICE_CLOSE);
    g_emaSlow = iMA(_Symbol, _Period, EmaSlow, 0, MODE_EMA, PRICE_CLOSE);
    g_rsi     = iRSI(_Symbol, _Period, RsiPeriod, PRICE_CLOSE);
    g_atr     = iATR(_Symbol, _Period, AtrPeriod);

    if(g_emaFast == INVALID_HANDLE || g_emaSlow == INVALID_HANDLE ||
       g_rsi == INVALID_HANDLE || g_atr == INVALID_HANDLE)
    {
        Print("Failed to create indicator handles");
        return INIT_FAILED;
    }
    return INIT_SUCCEEDED;
}

void OnDeinit(const int reason)
{
    if(g_emaFast != INVALID_HANDLE) IndicatorRelease(g_emaFast);
    if(g_emaSlow != INVALID_HANDLE) IndicatorRelease(g_emaSlow);
    if(g_rsi     != INVALID_HANDLE) IndicatorRelease(g_rsi);
    if(g_atr     != INVALID_HANDLE) IndicatorRelease(g_atr);
}

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;
}

void OnTick()
{
    if(!IsNewBar()) return;

    // Highest buffer index we read is the slow EMA SlopeLookback bars before the
    // closed bar => index (1 + SlopeLookback). Copy a little extra for safety.
    int toCopy = SlopeLookback + 4;

    // Need enough history for the slowest indicator plus the slope lookback.
    int need = MathMax(EmaSlow + SlopeLookback, AtrPeriod) + 2;
    if(Bars(_Symbol, _Period) < need + 2) return;

    double emaFastBuf[], emaSlowBuf[], rsiBuf[], atrBuf[];
    ArraySetAsSeries(emaFastBuf, true);
    ArraySetAsSeries(emaSlowBuf, true);
    ArraySetAsSeries(rsiBuf,     true);
    ArraySetAsSeries(atrBuf,     true);

    if(CopyBuffer(g_emaFast, 0, 0, toCopy, emaFastBuf) < toCopy) return;
    if(CopyBuffer(g_emaSlow, 0, 0, toCopy, emaSlowBuf) < toCopy) return;
    if(CopyBuffer(g_rsi,     0, 0, toCopy, rsiBuf)     < toCopy) return;
    if(CopyBuffer(g_atr,     0, 0, toCopy, atrBuf)     < toCopy) return;

    // Indicator values on the closed bar (shift 1).
    double emaFast     = emaFastBuf[1];
    double emaSlow     = emaSlowBuf[1];
    double emaSlowPrev = emaSlowBuf[1 + SlopeLookback]; // slow EMA SlopeLookback bars earlier
    double rsiNow      = rsiBuf[1];
    double rsiPrev     = rsiBuf[2];                      // RSI one bar before the closed bar
    double atr         = atrBuf[1];
    if(atr <= 0.0) return;

    // The closed bar (shift 1).
    double lastOpen  = iOpen(_Symbol,  _Period, 1);
    double lastClose = iClose(_Symbol, _Period, 1);
    bool bullBar = lastClose > lastOpen;
    bool bearBar = lastClose < lastOpen;

    bool up   = emaFast > emaSlow && emaSlow > emaSlowPrev;
    bool down = emaFast < emaSlow && emaSlow < emaSlowPrev;

    double midLo = 100.0 - RsiMid;   // mirrored midline for shorts
    double capLo = 100.0 - RsiCap;   // mirrored exhaustion cap for shorts

    // Long: uptrend, RSI cools back below midline then crosses up through it
    //       (but not already exhausted), on a bullish bar reclaiming the fast EMA.
    bool longSig = up && rsiPrev < RsiMid && rsiNow >= RsiMid && rsiNow < RsiCap
                   && bullBar && lastClose > emaFast;

    // Short: mirror image.
    bool shortSig = down && rsiPrev > midLo && rsiNow <= midLo && rsiNow > capLo
                    && bearBar && lastClose < emaFast;

    // One position per magic at a time.
    if(HasPosition(Magic)) return;

    if(longSig)
    {
        double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
        double sl  = ask - AtrSlMult * atr;
        double tp  = ask + AtrTpMult * atr;
        trade.Buy(Lots, _Symbol, 0.0, sl, tp, "MGP-long");
    }
    else if(shortSig)
    {
        double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
        double sl  = bid + AtrSlMult * atr;
        double tp  = bid - AtrTpMult * atr;
        trade.Sell(Lots, _Symbol, 0.0, sl, tp, "MGP-short");
    }
}
