//+------------------------------------------------------------------+
//|                                          EngulfingPivotHedge.mq5  |
//|                                                          Algobot  |
//|                                       https://www.algobot.live    |
//+------------------------------------------------------------------+
#property copyright "Algobot"
#property link      "https://www.algobot.live"
#property version   "1.00"
#property strict

// EngulfingPivotHedge
// -------------------
// Pure price-action system (NO indicators). Builds a rolling pivot framework
// (P / R1 / S1) from the recent closed-bar window, then trades two edges:
//   * REVERSAL  - bullish engulfing / bullish pin rejecting the S1/P support
//                 zone goes LONG; bearish engulfing / bearish pin rejecting the
//                 R1/P resistance zone goes SHORT (scalp targets).
//   * BREAKOUT  - a strong-bodied candle closing decisively beyond R1 (long)
//                 or below S1 (short) rides the expansion.
// A HEDGE leg (opposite side, separate magic) opens only if the primary trade
// runs adversely by a range-based threshold, capping drawdown when a reversal
// fails into a trend. Stops/targets are sized from a manual N-bar high-low range
// proxy (RangeProxy), so no indicator handles are ever used.

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

//--- inputs -------------------------------------------------------------------
input int    PivotLookback = 20;       // pivot window (closed bars)
input int    RangePeriod   = 14;       // range-proxy averaging period
input double ZoneFrac      = 0.50;     // zone width as fraction of range
input double SlRangeMult   = 1.00;     // SL = mult * range
input double TpRangeMult   = 1.50;     // TP = mult * range
input double HedgeTrigger  = 1.00;     // hedge trigger = mult * range
input double HedgeMult     = 1.00;     // hedge volume multiplier
input double Lots          = 0.10;     // primary volume
input long   Magic         = 770700;   // primary magic

//--- derived ------------------------------------------------------------------
long g_hedgeMagic = 0;

//+------------------------------------------------------------------+
//| Init                                                             |
//+------------------------------------------------------------------+
int OnInit()
{
    g_hedgeMagic = Magic + 1;
    trade.SetExpertMagicNumber(Magic);
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Deinit                                                           |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    // No indicator handles to release (pure price-action strategy).
}

//+------------------------------------------------------------------+
//| Tick                                                             |
//+------------------------------------------------------------------+
void OnTick()
{
    // Guard: need enough history (ctx.Bars(sym,tf) < _lookback + 3).
    if(Bars(_Symbol, _Period) < PivotLookback + 3) return;

    // Hedge management runs every tick so an adverse run is caught promptly.
    ManageHedge();

    // Entries only evaluate once per newly-closed bar.
    if(!IsNewBar()) return;

    // Two most-recently-closed candles for pattern detection (shift 1 & 2).
    double b1_open  = iOpen (_Symbol, _Period, 1);
    double b1_high  = iHigh (_Symbol, _Period, 1);
    double b1_low   = iLow  (_Symbol, _Period, 1);
    double b1_close = iClose(_Symbol, _Period, 1);

    double b2_open  = iOpen (_Symbol, _Period, 2);
    double b2_high  = iHigh (_Symbol, _Period, 2);
    double b2_low   = iLow  (_Symbol, _Period, 2);
    double b2_close = iClose(_Symbol, _Period, 2);

    // Rolling pivot built from the closed window [1.._lookback].
    double hh = -DBL_MAX, ll = DBL_MAX;
    for(int i = 1; i <= PivotLookback; i++)
    {
        double h = iHigh(_Symbol, _Period, i);
        double l = iLow (_Symbol, _Period, i);
        if(h > hh) hh = h;
        if(l < ll) ll = l;
    }

    double rng = RangeProxy();
    if(rng <= 0) return;

    double p  = (hh + ll + b1_close) / 3.0;
    double r1 = 2.0 * p - ll;
    double s1 = 2.0 * p - hh;

    // One primary trade at a time.
    if(HasPosition(Magic)) return;

    double zone = ZoneFrac * rng;
    bool nearSupport = MathAbs(b1_low  - s1) <= zone || MathAbs(b1_low  - p) <= zone;
    bool nearRes     = MathAbs(b1_high - r1) <= zone || MathAbs(b1_high - p) <= zone;

    bool bullRev = (BullEngulf(b1_open,b1_close, b2_open,b2_close) || BullPin(b1_open,b1_high,b1_low,b1_close)) && nearSupport;
    bool bearRev = (BearEngulf(b1_open,b1_close, b2_open,b2_close) || BearPin(b1_open,b1_high,b1_low,b1_close)) && nearRes;

    double body = MathAbs(b1_close - b1_open);
    double bRng = b1_high - b1_low;
    bool strongBull = b1_close > b1_open && bRng > 0 && body >= 0.5 * bRng;
    bool strongBear = b1_close < b1_open && bRng > 0 && body >= 0.5 * bRng;
    bool bullBreak = b1_close > r1 + zone && strongBull;
    bool bearBreak = b1_close < s1 - zone && strongBear;

    if(bullRev || bullBreak)
        OpenTrade(true,  rng, Magic, Lots);
    else if(bearRev || bearBreak)
        OpenTrade(false, rng, Magic, Lots);
}

//+------------------------------------------------------------------+
//| Hedge management                                                 |
//+------------------------------------------------------------------+
void ManageHedge()
{
    // base position present?
    long   baseSide      = 0;   // +1 buy, -1 sell, 0 none
    double basePriceOpen = 0.0;
    if(!GetPosition(Magic, baseSide, basePriceOpen)) return;

    // already hedged?
    if(HasPosition(g_hedgeMagic)) return;

    double rng = RangeProxy();
    if(rng <= 0) return;
    double trigger = HedgeTrigger * rng;

    if(baseSide > 0) // Side.Buy
    {
        double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
        if(basePriceOpen - bid >= trigger)
            OpenTrade(false, rng, g_hedgeMagic, Lots * HedgeMult);
    }
    else // Side.Sell
    {
        double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
        if(ask - basePriceOpen >= trigger)
            OpenTrade(true, rng, g_hedgeMagic, Lots * HedgeMult);
    }
}

//+------------------------------------------------------------------+
//| Open a trade (mirrors C# OpenTrade)                              |
//+------------------------------------------------------------------+
void OpenTrade(bool isLong, double rng, long magic, double vol)
{
    double sl = SlRangeMult * rng;
    double tp = TpRangeMult * rng;

    trade.SetExpertMagicNumber(magic);

    if(isLong)
    {
        double price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
        trade.Buy(vol, _Symbol, price, price - sl, price + tp, "EPH");
    }
    else
    {
        double price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
        trade.Sell(vol, _Symbol, price, price + sl, price - tp, "EPH");
    }
}

//+------------------------------------------------------------------+
//| Average true bar range over the recent window (RangeProxy)       |
//+------------------------------------------------------------------+
double RangeProxy()
{
    double sum = 0.0;
    for(int i = 1; i <= RangePeriod; i++)
        sum += iHigh(_Symbol, _Period, i) - iLow(_Symbol, _Period, i);
    return sum / RangePeriod;
}

//+------------------------------------------------------------------+
//| Pattern helpers (mirror C#)                                      |
//+------------------------------------------------------------------+
bool BullEngulf(double b1o, double b1c, double b2o, double b2c)
{
    return b1c > b1o && b2c < b2o &&
           b1c >= b2o && b1o <= b2c;
}

bool BearEngulf(double b1o, double b1c, double b2o, double b2c)
{
    return b1c < b1o && b2c > b2o &&
           b1o >= b2c && b1c <= b2o;
}

bool BullPin(double o, double h, double l, double c)
{
    double body  = MathAbs(c - o);
    double lower = MathMin(o, c) - l;
    double upper = h - MathMax(o, c);
    double rng   = h - l;
    if(rng <= 0) return false;
    return lower >= 2.0 * body && upper <= body && (c - l) >= 0.6 * rng;
}

bool BearPin(double o, double h, double l, double c)
{
    double body  = MathAbs(c - o);
    double lower = MathMin(o, c) - l;
    double upper = h - MathMax(o, c);
    double rng   = h - l;
    if(rng <= 0) return false;
    return upper >= 2.0 * body && lower <= body && (h - c) >= 0.6 * rng;
}

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

//+------------------------------------------------------------------+
//| Position helpers (filter by symbol + magic)                      |
//+------------------------------------------------------------------+
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;
}

// Returns the first open position for (symbol, magic). side: +1 buy, -1 sell.
bool GetPosition(long magic, long &side, double &priceOpen)
{
    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);
            side      = (ptype == POSITION_TYPE_BUY) ? 1 : -1;
            priceOpen = PositionGetDouble(POSITION_PRICE_OPEN);
            return true;
        }
    }
    return false;
}
//+------------------------------------------------------------------+
