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

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

//--- inputs (mirror DescribeParameters / GetInput defaults) ---
input int    LookbackBars      = 20;     // swing window for the shared extreme
input double EqualTolerancePct = 15.0;   // near-equal tolerance, % of avg range
input double RewardRisk        = 2.0;    // target = RewardRisk * risk
input double StopBufferPct     = 20.0;   // stop buffer beyond extreme, % of avg range
input int    RangePeriod       = 14;     // bars used to average bar range
input double Lots              = 0.10;   // order volume
input long   Magic             = 4242;   // EA magic number

//--- no indicators are used: pure price action ---

int OnInit()
{
    trade.SetExpertMagicNumber(Magic);
    return INIT_SUCCEEDED;
}

void OnTick()
{
    // Evaluate once per freshly closed bar.
    if(!IsNewBar()) return;

    // need = Max(LookbackBars + 2, RangePeriod + 1) + 1
    int need = MathMax(LookbackBars + 2, RangePeriod + 1) + 1;
    if(Bars(_Symbol, _Period) < need) return;

    // One position at a time for this magic — let SL/TP manage the exit.
    if(HasPosition(Magic)) return;

    // The two most recently CLOSED bars form the candidate tweezer.
    double bar1High = iHigh(_Symbol, _Period, 1);   // confirmation / second bar
    double bar1Low  = iLow (_Symbol, _Period, 1);
    double bar1Open = iOpen(_Symbol, _Period, 1);
    double bar1Close= iClose(_Symbol, _Period, 1);

    double bar2High = iHigh(_Symbol, _Period, 2);   // first bar of the pair
    double bar2Low  = iLow (_Symbol, _Period, 2);
    double bar2Close= iClose(_Symbol, _Period, 2);

    // Average recent bar range (pure price action) for tolerance & buffer sizing.
    double sumRange = 0.0;
    for(int s = 1; s <= RangePeriod; s++)
        sumRange += iHigh(_Symbol, _Period, s) - iLow(_Symbol, _Period, s);
    double avgRange = sumRange / RangePeriod;
    if(avgRange <= 0.0) return;

    double tol    = (EqualTolerancePct / 100.0) * avgRange;
    double buffer = (StopBufferPct     / 100.0) * avgRange;

    double mid1 = (bar1High + bar1Low) * 0.5;

    // ---- Tweezer BOTTOM (long) ----
    bool bar2IsSwingLow = true;
    for(int s = 3; s <= LookbackBars + 1; s++)
    {
        if(iLow(_Symbol, _Period, s) < bar2Low) { bar2IsSwingLow = false; break; }
    }
    bool tweezerBottom =
        bar2IsSwingLow &&
        MathAbs(bar1Low - bar2Low) <= tol &&   // near-equal lows
        bar1Close > bar1Open &&                // second bar bullish
        bar1Close > bar2Close &&               // closes back above prior bar
        bar1Close > mid1;                      // rejection: close in upper half

    if(tweezerBottom)
    {
        double entry = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
        double sl    = MathMin(bar1Low, bar2Low) - buffer;
        double risk  = entry - sl;
        if(risk > 0.0)
        {
            double tp = entry + RewardRisk * risk;
            if(trade.Buy(Lots, _Symbol, 0.0, sl, tp, "TweezerBottom"))
                PrintFormat("Tweezer bottom long @ %.5f SL %.5f TP %.5f", entry, sl, tp);
        }
        return;
    }

    // ---- Tweezer TOP (short) ----
    bool bar2IsSwingHigh = true;
    for(int s = 3; s <= LookbackBars + 1; s++)
    {
        if(iHigh(_Symbol, _Period, s) > bar2High) { bar2IsSwingHigh = false; break; }
    }
    bool tweezerTop =
        bar2IsSwingHigh &&
        MathAbs(bar1High - bar2High) <= tol &&  // near-equal highs
        bar1Close < bar1Open &&                 // second bar bearish
        bar1Close < bar2Close &&                // closes back below prior bar
        bar1Close < mid1;                       // rejection: close in lower half

    if(tweezerTop)
    {
        double entry = SymbolInfoDouble(_Symbol, SYMBOL_BID);
        double sl    = MathMax(bar1High, bar2High) + buffer;
        double risk  = sl - entry;
        if(risk > 0.0)
        {
            double tp = entry - RewardRisk * risk;
            if(trade.Sell(Lots, _Symbol, 0.0, sl, tp, "TweezerTop"))
                PrintFormat("Tweezer top short @ %.5f SL %.5f TP %.5f", entry, sl, tp);
        }
    }
}

void OnDeinit(const int reason)
{
    // no indicator handles to release
}

//+------------------------------------------------------------------+
//| Returns true once per freshly opened bar                         |
//+------------------------------------------------------------------+
bool IsNewBar()
{
    static datetime last = 0;
    datetime cur = iTime(_Symbol, _Period, 0);
    if(cur != last){ last = cur; return true; }
    return false;
}

//+------------------------------------------------------------------+
//| True if an open position for this magic exists on this symbol    |
//+------------------------------------------------------------------+
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;
}
