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

// StarPatternReversal
// ------------------------------------------------------------------------------------------
// Concept (a combination of exactly TWO allowed ideas):
//   1) Candlestick Patterns   -> classic three-candle Morning Star (bullish) / Evening Star
//                                (bearish) reversal formations.
//   2) Support and Resistance -> the pattern is only honoured when its middle "star" candle
//                                prints at a FRESH swing extreme (tested support / resistance).
//
// Style: mean-reversion / reversal.  Distinct LONG and SHORT rules (mirror images).
// ATR is used ONLY to size the protective-stop buffer (risk management), never as a signal.

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

//--- inputs ---------------------------------------------------------------------------------
input double StarBodyFactor    = 0.50;   // Star (middle) body must be small vs leading impulse body
input double ImpulseBodyFactor = 1.00;   // Lead & confirm bodies must be >= avgBody * factor
input int    ExtremeLookback   = 20;     // Lookback that defines the swing extreme (S/R)
input int    AtrPeriod         = 14;     // ATR period used purely for the protective-stop buffer
input double SlAtrBuffer       = 0.50;   // Extra room beyond the pattern extreme, in ATR multiples
input double RewardRisk        = 2.00;   // Take-profit distance as a multiple of measured risk
input double Lots              = 0.10;   // Trade volume
input long   Magic             = 7720;   // Magic number

//--- indicator handles ----------------------------------------------------------------------
int g_atr = INVALID_HANDLE;

//============================================================================================
int OnInit()
{
    trade.SetExpertMagicNumber((ulong)Magic);

    g_atr = iATR(_Symbol, _Period, AtrPeriod);
    if(g_atr == INVALID_HANDLE)
    {
        Print("Failed to create ATR handle");
        return INIT_FAILED;
    }
    return INIT_SUCCEEDED;
}

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

//============================================================================================
void OnTick()
{
    // ---- Act only on a NEW bar: the previously-forming bar has just closed (now shift 1) ----
    if(!IsNewBar()) return;

    // Need enough history so ATR / extremes / pattern window all have data.
    // (Mirrors the C# seeding requirement: ExtremeLookback + AtrPeriod + 6.)
    int barsAvail = Bars(_Symbol, _Period);
    int need      = ExtremeLookback + AtrPeriod + 6;
    if(barsAvail < need) return;

    // One position per magic on this symbol -- no pyramiding, no overlapping reversals.
    if(HasPosition(Magic)) return;

    // ---- The three pattern candles (shift 1 = most recently closed) --------------------
    //   c1 = lead impulse (shift 3), c2 = star (shift 2), c3 = confirm (shift 1).
    double c1o = iOpen (_Symbol, _Period, 3);
    double c1c = iClose(_Symbol, _Period, 3);
    double c1h = iHigh (_Symbol, _Period, 3);
    double c1l = iLow  (_Symbol, _Period, 3);

    double c2o = iOpen (_Symbol, _Period, 2);
    double c2c = iClose(_Symbol, _Period, 2);
    double c2h = iHigh (_Symbol, _Period, 2);
    double c2l = iLow  (_Symbol, _Period, 2);

    double c3o = iOpen (_Symbol, _Period, 1);
    double c3c = iClose(_Symbol, _Period, 1);
    double c3h = iHigh (_Symbol, _Period, 1);
    double c3l = iLow  (_Symbol, _Period, 1);

    double body1 = MathAbs(c1c - c1o);
    double body2 = MathAbs(c2c - c2o);
    double body3 = MathAbs(c3c - c3o);

    // ---- Average body + prior swing extremes over the window BEFORE the pattern ---------
    //   Window = ExtremeLookback bars immediately before c1 (shifts 4 .. 3+ExtremeLookback).
    double sumBody  = 0.0;
    int    cnt      = 0;
    double priorLow = DBL_MAX;
    double priorHigh= -DBL_MAX;
    for(int shift = 4; shift <= 3 + ExtremeLookback; shift++)
    {
        double o  = iOpen (_Symbol, _Period, shift);
        double c  = iClose(_Symbol, _Period, shift);
        double hi = iHigh (_Symbol, _Period, shift);
        double lo = iLow  (_Symbol, _Period, shift);
        sumBody += MathAbs(c - o);
        cnt++;
        if(lo < priorLow)  priorLow  = lo;
        if(hi > priorHigh) priorHigh = hi;
    }
    if(cnt == 0) return;
    double avgBody = sumBody / cnt;
    if(avgBody <= 0.0) return;

    double impulseMin = avgBody * ImpulseBodyFactor;
    double starMax    = body1   * StarBodyFactor;

    // ---- ATR (newest CLOSED bar = buffer index 1, matching the C# rolling-bars value) ---
    double atrBuf[];
    ArraySetAsSeries(atrBuf, true);
    if(CopyBuffer(g_atr, 0, 0, 3, atrBuf) < 3) return;
    double atr = atrBuf[1];
    if(atr <= 0.0) return;

    double c1Mid = (c1o + c1c) * 0.5;

    // ---------------- Morning Star (LONG) ----------------
    // Strong down impulse into support, small star at a fresh low, up impulse reclaiming
    // the midpoint of the leading candle's body.
    bool morning =
        c1c < c1o && body1 >= impulseMin &&     // c1 strong bearish impulse
        body2 <= starMax &&                     // c2 small indecision star
        c3c > c3o && body3 >= impulseMin &&     // c3 strong bullish impulse
        c3c >= c1Mid &&                         // confirmation reclaim
        c2l <= priorLow;                        // star reached prior support extreme

    // ---------------- Evening Star (SHORT) ----------------
    bool evening =
        c1c > c1o && body1 >= impulseMin &&     // c1 strong bullish impulse
        body2 <= starMax &&                     // c2 small indecision star
        c3c < c3o && body3 >= impulseMin &&     // c3 strong bearish impulse
        c3c <= c1Mid &&                         // confirmation reject
        c2h >= priorHigh;                       // star reached prior resistance extreme

    if(morning && !evening)
    {
        double entry      = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
        double patternLow = MathMin(MathMin(c1l, c2l), c3l);
        double sl         = patternLow - atr * SlAtrBuffer;
        double risk       = entry - sl;
        if(risk <= 0.0) return;
        double tp         = entry + risk * RewardRisk;

        sl = NormalizeDouble(sl, _Digits);
        tp = NormalizeDouble(tp, _Digits);

        if(trade.Buy(Lots, _Symbol, 0.0, sl, tp, "MorningStar@support"))
            PrintFormat("MorningStar LONG %s entry=%.5f sl=%.5f tp=%.5f", _Symbol, entry, sl, tp);
    }
    else if(evening && !morning)
    {
        double entry       = SymbolInfoDouble(_Symbol, SYMBOL_BID);
        double patternHigh = MathMax(MathMax(c1h, c2h), c3h);
        double sl          = patternHigh + atr * SlAtrBuffer;
        double risk        = sl - entry;
        if(risk <= 0.0) return;
        double tp          = entry - risk * RewardRisk;

        sl = NormalizeDouble(sl, _Digits);
        tp = NormalizeDouble(tp, _Digits);

        if(trade.Sell(Lots, _Symbol, 0.0, sl, tp, "EveningStar@resistance"))
            PrintFormat("EveningStar SHORT %s entry=%.5f sl=%.5f tp=%.5f", _Symbol, entry, 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;
}
