//+------------+-----------------------------------------------------+
//| v.22.04.05 |                            Up And Down FIB ZONE.mq5 |
//|                     Copyright © 2005, MetaQuotes Software Corp.  |
//|                                      http://www.metaquotes.net/  |
//+------------------------------------------------------------------+

#property indicator_chart_window
#property indicator_buffers 3
#property indicator_plots   1

#property indicator_type1   DRAW_LINE
#property indicator_style1  STYLE_DOT
#property indicator_color1  clrNONE
#property indicator_label1  "Fisher"

#property indicator_level1  0
#property indicator_minimum -3
#property indicator_maximum  3

#define PREFIX "xxx"

input int    BarsToScan     = 1000;        // Bars to scan
input int    period         = 18;
input bool   Arrow          = true;
input int    ArrowSize      = 3;
input int    SIGNAL_BAR     = 1;
input color  clArrowBuy     = clrDodgerBlue;
input color  clArrowSell    = clrRed;
input color  BoxColorBuy    = clrDodgerBlue;
input color  BoxColorSell   = clrRed;
input int    BoxWidth       = 1;
input bool   FillBox        = true;
input int    FiboWidth      = 1;
input bool   ShowFiboLevels = true;
input color  PullbackColor  = clrSilver;
input int    PullbackBars   = 15;

double FiboLevels[] = {0.0, 0.236, 0.382, 0.5, 0.618, 0.786, 1.0, 1.618, 2.618, 4.236};

double ExtBuffer0[];
double BufValue1[];
double BufFish1[];

// Track last known period so we detect TF/symbol change
ENUM_TIMEFRAMES lastPeriod = PERIOD_CURRENT;
string          lastSymbol = "";

//+------------------------------------------------------------------+
void DeleteAllObjects()
  {
    int total = ObjectsTotal(0, 0, -1);
    for(int i = total - 1; i >= 0; i--)
      {
        string name = ObjectName(0, i);
        if(StringSubstr(name, 0, StringLen(PREFIX)) == PREFIX)
            ObjectDelete(0, name);
      }
  }

//+------------------------------------------------------------------+
int OnInit()
  {
    SetIndexBuffer(0, ExtBuffer0, INDICATOR_DATA);
    SetIndexBuffer(1, BufValue1,  INDICATOR_CALCULATIONS);
    SetIndexBuffer(2, BufFish1,   INDICATOR_CALCULATIONS);

    ArraySetAsSeries(ExtBuffer0, true);
    ArraySetAsSeries(BufValue1,  true);
    ArraySetAsSeries(BufFish1,   true);

    PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
    IndicatorSetString(INDICATOR_SHORTNAME, "Up and Down with Fibo");

    lastPeriod = Period();
    lastSymbol = _Symbol;

    return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
    DeleteAllObjects();
  }

//+------------------------------------------------------------------+
void CreatePullbackZone(datetime t, double h, double l, bool isBuy)
  {
    string   name    = PREFIX + "PZone" + TimeToString(t);
    datetime endTime = t + (datetime)(PullbackBars * PeriodSeconds(Period()));
    if(ObjectFind(0, name) >= 0) return;
    ObjectCreate(0, name, OBJ_RECTANGLE, 0, t, h, endTime, l);
    ObjectSetInteger(0, name, OBJPROP_COLOR,      PullbackColor);
    ObjectSetInteger(0, name, OBJPROP_STYLE,      STYLE_SOLID);
    ObjectSetInteger(0, name, OBJPROP_WIDTH,      1);
    ObjectSetInteger(0, name, OBJPROP_BACK,       true);
    ObjectSetInteger(0, name, OBJPROP_FILL,       true);
    ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
  }

//+------------------------------------------------------------------+
void CreateFiboLevels(datetime t, double h, double l, bool isBuy, color fiboColor)
  {
    if(!ShowFiboLevels) return;
    double   range   = h - l;
    datetime endTime = t + (datetime)PeriodSeconds(Period());
    for(int i = 0; i < ArraySize(FiboLevels); i++)
      {
        double fp = isBuy ? l + range * FiboLevels[i] : h - range * FiboLevels[i];
        string fn = PREFIX + "Fibo"  + TimeToString(t) + "_" + DoubleToString(FiboLevels[i], 3);
        string ln = PREFIX + "Label" + TimeToString(t) + "_" + DoubleToString(FiboLevels[i], 3);
        if(ObjectFind(0, fn) < 0)
          {
            ObjectCreate(0, fn, OBJ_TREND, 0, t, fp, endTime, fp);
            ObjectSetInteger(0, fn, OBJPROP_COLOR,      fiboColor);
            ObjectSetInteger(0, fn, OBJPROP_WIDTH,      FiboWidth);
            ObjectSetInteger(0, fn, OBJPROP_STYLE,      STYLE_DOT);
            ObjectSetInteger(0, fn, OBJPROP_RAY_RIGHT,  false);
            ObjectSetInteger(0, fn, OBJPROP_SELECTABLE, false);
          }
        if(ObjectFind(0, ln) < 0)
          {
            ObjectCreate(0, ln, OBJ_TEXT, 0, t, fp);
            ObjectSetString(0,  ln, OBJPROP_TEXT,       DoubleToString(FiboLevels[i]*100, 1) + "%");
            ObjectSetString(0,  ln, OBJPROP_FONT,       "Arial");
            ObjectSetInteger(0, ln, OBJPROP_FONTSIZE,   8);
            ObjectSetInteger(0, ln, OBJPROP_COLOR,      fiboColor);
            ObjectSetInteger(0, ln, OBJPROP_SELECTABLE, false);
          }
      }
  }

//+------------------------------------------------------------------+
void CreateBox(datetime t, double h, double l, color boxColor, bool isBuy)
  {
    string name = PREFIX + "Box" + TimeToString(t);
    if(ObjectFind(0, name) >= 0) return;
    ObjectCreate(0, name, OBJ_RECTANGLE, 0, t, h, t + (datetime)PeriodSeconds(Period()), l);
    ObjectSetInteger(0, name, OBJPROP_COLOR,      boxColor);
    ObjectSetInteger(0, name, OBJPROP_STYLE,      STYLE_DOT);
    ObjectSetInteger(0, name, OBJPROP_WIDTH,      BoxWidth);
    ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
    ObjectSetInteger(0, name, OBJPROP_BACK,       FillBox);
    ObjectSetInteger(0, name, OBJPROP_FILL,       FillBox);
    CreateFiboLevels(t, h, l, isBuy, boxColor);
    CreatePullbackZone(t, h, l, isBuy);
  }

//+------------------------------------------------------------------+
void manageArr(int j, color clr, int theCode, bool up,
               const datetime &time[], const double &high[], const double &low[])
  {
    string objName = PREFIX + TimeToString(time[j]);
    if(ObjectFind(0, objName) == -1)
        ObjectCreate(0, objName, OBJ_ARROW, 0, time[j], 0);
    ObjectSetInteger(0, objName, OBJPROP_COLOR,      clr);
    ObjectSetInteger(0, objName, OBJPROP_ARROWCODE,  theCode);
    ObjectSetInteger(0, objName, OBJPROP_WIDTH,      ArrowSize);
    ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false);
    if(up)
      {
        ObjectSetDouble(0,  objName, OBJPROP_PRICE,  high[j]);
        ObjectSetInteger(0, objName, OBJPROP_ANCHOR, ANCHOR_BOTTOM);
      }
    else
      {
        ObjectSetDouble(0,  objName, OBJPROP_PRICE,  low[j]);
        ObjectSetInteger(0, objName, OBJPROP_ANCHOR, ANCHOR_TOP);
      }
  }

//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double   &open[],
                const double   &high[],
                const double   &low[],
                const double   &close[],
                const long     &tick_volume[],
                const long     &volume[],
                const int      &spread[])
  {
    if(rates_total < period + 2) return(0);

    ArraySetAsSeries(time,       true);
    ArraySetAsSeries(open,       true);
    ArraySetAsSeries(high,       true);
    ArraySetAsSeries(low,        true);
    ArraySetAsSeries(close,      true);
    ArraySetAsSeries(ExtBuffer0, true);
    ArraySetAsSeries(BufValue1,  true);
    ArraySetAsSeries(BufFish1,   true);

    // Detect TF or symbol change — delete all objects so they redraw cleanly
    bool fullRecalc = (prev_calculated == 0);
    if(Period() != lastPeriod || _Symbol != lastSymbol)
      {
        DeleteAllObjects();
        lastPeriod = Period();
        lastSymbol = _Symbol;
        fullRecalc = true;
      }

    // MQL4-identical limit calculation, capped by BarsToScan
    int counted_bars = prev_calculated;
    if(counted_bars > 0) counted_bars--;
    int limit = rates_total - counted_bars;
    if(fullRecalc && BarsToScan > 0)
        limit = MathMin(limit, BarsToScan);

    // Seed carry-over — same as MQL4: Value1/Fish1 = 0 on first call,
    // then from stored buffer on subsequent calls
    double Value1 = fullRecalc ? 0.0 : BufValue1[limit];
    double Fish1  = fullRecalc ? 0.0 : BufFish1[limit];

    // Fisher Transform — MQL4: for(i=0; i<limit; i++) i=0=newest
    for(int i = 0; i < limit; i++)
      {
        int hShift = iHighest(_Symbol, PERIOD_CURRENT, MODE_CLOSE, period, i);
        int lShift = iLowest (_Symbol, PERIOD_CURRENT, MODE_CLOSE, period, i);

        double MaxH  = high[hShift];
        double MinL  = low[lShift];
        double price = (open[i] + close[i]) / 2.0;

        double Value;
        if(MaxH - MinL == 0.0)
            Value = 0.33 * 2.0 * (0.0 - 0.5) + 0.67 * Value1;
        else
            Value = 0.33 * 2.0 * ((price - MaxH) / (MinL - MaxH) - 0.5) + 0.67 * Value1;

        Value = MathMin(MathMax(Value, -0.999), 0.999);

        if(1.0 - Value == 0.0)
            ExtBuffer0[i] = 0.5 + 0.5 * Fish1;
        else
            ExtBuffer0[i] = 0.5 * MathLog((1.0 + Value) / (1.0 - Value)) + 0.5 * Fish1;

        BufValue1[i] = Value;
        BufFish1[i]  = ExtBuffer0[i];

        Value1 = Value;
        Fish1  = ExtBuffer0[i];
      }

    // Arrow and box logic — MQL4: for(j=limit2; j>=0; j--)
    if(Arrow)
      {
        for(int j = limit; j >= 0; j--)
          {
            if(j + SIGNAL_BAR + 1 >= rates_total) continue;

            double sig_prev = ExtBuffer0[j + SIGNAL_BAR + 1];
            double sig_cur  = ExtBuffer0[j + SIGNAL_BAR];

            if(sig_prev > 0.0 && sig_cur < 0.0)
              {
                manageArr(j, clArrowBuy, 233, false, time, high, low);
                CreateBox(time[j], high[j], low[j], BoxColorBuy, true);
              }
            if(sig_prev < 0.0 && sig_cur > 0.0)
              {
                manageArr(j, clArrowSell, 234, true, time, high, low);
                CreateBox(time[j], high[j], low[j], BoxColorSell, false);
              }
          }
      }

    ChartRedraw(0);
    return(rates_total);
  }
//+------------------------------------------------------------------+