//+------------------------------------------------------------------+
//|  BuyersSellers_Dashboard.mq5                                     |
//|  Buyers/Sellers ampeli pääkartalla — v2.0 (MQL5 port)            |
//+------------------------------------------------------------------+
#property copyright   "Custom"
#property version     "2.0"
#property strict

#property indicator_chart_window
#property indicator_plots   0
#property indicator_buffers 0

//--- В MQL5 для timeframe используем ENUM_TIMEFRAMES.
//--- PERIOD_CURRENT (0) используем как «выключено».
input ENUM_TIMEFRAMES TF1_Period   = PERIOD_M15;     // Timeline 1 (PERIOD_CURRENT = pois):
input ENUM_TIMEFRAMES TF2_Period   = PERIOD_H1;      // Timeline 2 (PERIOD_CURRENT = pois):
input ENUM_TIMEFRAMES TF3_Period   = PERIOD_H4;      // Timeline 3 (PERIOD_CURRENT = pois):
input ENUM_TIMEFRAMES TF4_Period   = PERIOD_D1;      // Timeline 4 (PERIOD_CURRENT = pois):
input ENUM_TIMEFRAMES TF5_Period   = PERIOD_CURRENT; // Timeline 5 (PERIOD_CURRENT = pois):
input int             LookbackBars = 50;             // Candles per time slot:
input ENUM_BASE_CORNER PanelCorner = CORNER_RIGHT_UPPER; // Paneel Corner:
input int             FontSize     = 10;
input color    BuyColor            = C'8,153,129';    // Color Buy:
input color    SellColor           = C'242,54,69';    // Color Sell: 
input color    NameColor           = clrDarkGray;     // Color Name:
string pfx = "BSD_";

//+------------------------------------------------------------------+
string TFName(ENUM_TIMEFRAMES tf)
{
   switch(tf)
   {
      case PERIOD_M1:  return("M1");
      case PERIOD_M5:  return("M5");
      case PERIOD_M15: return("M15");
      case PERIOD_M30: return("M30");
      case PERIOD_H1:  return("H1");
      case PERIOD_H4:  return("H4");
      case PERIOD_D1:  return("D1");
      case PERIOD_W1:  return("W1");
      case PERIOD_MN1: return("MN");
      default:         return("TF" + IntegerToString((int)tf));
   }
}

//+------------------------------------------------------------------+
void CalcDominance(const string sym, ENUM_TIMEFRAMES tf, int bars,
                   double &buyPct, double &sellPct)
{
   buyPct  = 50.0;
   sellPct = 50.0;

   int avail = Bars(sym, tf);
   if(avail <= 1) return;

   int lookback = MathMin(bars, avail - 1);
   if(lookback <= 0) return;

   //--- В MQL5 нет iOpen/iHigh/iLow/iClose — используем CopyOpen/High/Low/Close.
   double opens[], highs[], lows[], closes[];
   ArraySetAsSeries(opens,  true);
   ArraySetAsSeries(highs,  true);
   ArraySetAsSeries(lows,   true);
   ArraySetAsSeries(closes, true);

   if(CopyOpen (sym, tf, 0, lookback, opens)  != lookback) return;
   if(CopyHigh (sym, tf, 0, lookback, highs)  != lookback) return;
   if(CopyLow  (sym, tf, 0, lookback, lows)   != lookback) return;
   if(CopyClose(sym, tf, 0, lookback, closes) != lookback) return;

   int buys = 0, sells = 0;
   double pt = _Point;

   for(int i = 0; i < lookback; i++)
   {
      double o = opens[i];
      double h = highs[i];
      double l = lows[i];
      double c = closes[i];

      double body  = MathAbs(c - o);
      double total = h - l;
      if(total < pt) total = pt;

      double upWick   = h - MathMax(o , c);
      double downWick = MathMin(o , c) - l;

      bool isBuyer  = (c > o) && (downWick < body * 0.5) && (upWick   < total * 0.6);
      bool isSeller = (c < o) && (upWick   < body * 0.5) && (downWick < total * 0.6);

      if(isBuyer)       buys++;
      else if(isSeller) sells++;
   }

   int tot = buys + sells;
   if(tot == 0) { buyPct = 50.0; sellPct = 50.0; return; }
   buyPct  = (double)buys  / tot * 100.0;
   sellPct = (double)sells / tot * 100.0;
}

//+------------------------------------------------------------------+
void MakeLabel(const string name, const string text, int x, int y,
               color clr, int fsize, ENUM_BASE_CORNER corner)
{
   if(ObjectFind(0, name) < 0)
      ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);

   ObjectSetString (0, name, OBJPROP_TEXT,      text);
   ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
   ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
   ObjectSetInteger(0, name, OBJPROP_CORNER,    corner);
   ObjectSetInteger(0, name, OBJPROP_COLOR,     clr);
   ObjectSetInteger(0, name, OBJPROP_FONTSIZE,  fsize);
   ObjectSetInteger(0, name, OBJPROP_BACK,      false);
   ObjectSetInteger(0, name, OBJPROP_SELECTABLE,false);
   ObjectSetInteger(0, name, OBJPROP_HIDDEN,    true);

   //--- Привязка точки якоря к нужному углу, чтобы координаты
   //--- считались относительно того же угла, что задан в PanelCorner.
   ENUM_ANCHOR_POINT anchor = ANCHOR_LEFT_UPPER;
   switch(corner)
   {
      case CORNER_LEFT_UPPER:  anchor = ANCHOR_LEFT_UPPER;  break;
      case CORNER_RIGHT_UPPER: anchor = ANCHOR_RIGHT_UPPER; break;
      case CORNER_LEFT_LOWER:  anchor = ANCHOR_LEFT_LOWER;  break;
      case CORNER_RIGHT_LOWER: anchor = ANCHOR_RIGHT_LOWER; break;
   }
   ObjectSetInteger(0, name, OBJPROP_ANCHOR, anchor);
}

//+------------------------------------------------------------------+
void DeleteObjects()
{
   for(int i = ObjectsTotal(0) - 1; i >= 0; i--)
   {
      string nm = ObjectName(0, i);
      if(StringFind(nm, pfx) == 0) ObjectDelete(0, nm);
   }
}

//+------------------------------------------------------------------+
int OnInit()
{
   IndicatorSetString(INDICATOR_SHORTNAME, "B/S Dashboard");
   DeleteObjects();
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   DeleteObjects();
}

//+------------------------------------------------------------------+
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[])
{
   DeleteObjects();

   ENUM_TIMEFRAMES periods[5];
   periods[0] = TF1_Period;
   periods[1] = TF2_Period;
   periods[2] = TF3_Period;
   periods[3] = TF4_Period;
   periods[4] = TF5_Period;

   //--- Aktiiviset aikakartat (PERIOD_CURRENT = pois).
   int active = 0;
   for(int k = 0; k < 5; k++)
      if(periods[k] != PERIOD_CURRENT) active++;
   if(active == 0) return(rates_total);

   //--- Paneelin mitat
   int rowH    = FontSize + 8;
   int colW    = 68;
   int startX  = 10;
   int startY  = 20;

   //--- Otsikkorivi
   MakeLabel(pfx + "HDR_TF",   "TF",      startX,              startY, NameColor, FontSize, PanelCorner);
   MakeLabel(pfx + "HDR_BUY",  "Buyers",  startX + colW,       startY, NameColor, FontSize, PanelCorner);
   MakeLabel(pfx + "HDR_SELL", "Sellers", startX + colW * 2,   startY, NameColor, FontSize, PanelCorner);
   MakeLabel(pfx + "HDR_SIG",  "Signal",  startX + colW * 3,   startY, NameColor, FontSize, PanelCorner);

   int row = 0;
   for(int k = 0; k < 5; k++)
   {
      if(periods[k] == PERIOD_CURRENT) continue;

      double buyPct = 0.0, sellPct = 0.0;
      CalcDominance(_Symbol, periods[k], LookbackBars, buyPct, sellPct);

      string tfName = TFName(periods[k]);
      int    y      = startY + rowH * (row + 1) + 4;

      color  sigColor;
      string sigText;
      string bullet;
      if(buyPct >= 65)
      {
         sigColor = BuyColor;
         sigText  = "BUYERS";
         bullet   = "▲";
      }
      else if(sellPct >= 65)
      {
         sigColor = SellColor;
         sigText  = "SELLERS";
         bullet   = "▼";
      }
      else
      {
         sigColor = NameColor;
         sigText  = "NEUTRAL";
         bullet   = "●";
      }

      string buyStr  = StringFormat("%.0f%%", buyPct);
      string sellStr = StringFormat("%.0f%%", sellPct);

      MakeLabel(pfx + "TF_"   + IntegerToString(k), tfName,                 startX,            y, NameColor,  FontSize, PanelCorner);
      MakeLabel(pfx + "BUY_"  + IntegerToString(k), buyStr,                 startX + colW,     y, BuyColor,   FontSize, PanelCorner);
      MakeLabel(pfx + "SELL_" + IntegerToString(k), sellStr,                startX + colW * 2, y, SellColor,    FontSize, PanelCorner);
      MakeLabel(pfx + "SIG_"  + IntegerToString(k), bullet + " " + sigText, startX + colW * 3, y, sigColor,  FontSize, PanelCorner);

      row++;
   }

   ChartRedraw(0);
   return(rates_total);
}
//+------------------------------------------------------------------+
