//+------------------------------------------------------------------+
//|               SpreadsSMA3Balance                                |
//|           Спред + SMA + Bollinger + ATR (амплитудный) + Панель  |
//|            v16.3 INTEGRATED
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, Roman Shiredchenko"
#property version   "16.3"
#property indicator_separate_window
#property tester_indicator "SpreadsSMA3Balance.ex5"
//#property tester_file "SpreadsSMA3Balance.ex5"
//-----------------------------------------------------------------
#property indicator_buffers 12
#property indicator_plots   8

#property indicator_label1  "Spread"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrGold
#property indicator_style1  STYLE_SOLID
#property indicator_width1  3

#property indicator_label2  "SMA"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrRed
#property indicator_style2  STYLE_SOLID
#property indicator_width2  2

#property indicator_label3  "Upper Band"
#property indicator_type3   DRAW_LINE
#property indicator_color3  clrLime
#property indicator_style3  STYLE_DOT
#property indicator_width3  1

#property indicator_label4  "Lower Band"
#property indicator_type4   DRAW_LINE
#property indicator_color4  clrDodgerBlue
#property indicator_style4  STYLE_DOT
#property indicator_width4  1

#property indicator_type5   DRAW_ARROW
#property indicator_type6   DRAW_ARROW
#property indicator_color5  clrDodgerBlue
#property indicator_color6  clrRed
#property indicator_width5  3
#property indicator_width6  3

#property indicator_label7  "CloseBuy"
#property indicator_type7   DRAW_ARROW
#property indicator_color7  clrLimeGreen
#property indicator_width7  2

#property indicator_label8  "CloseSell"
#property indicator_type8   DRAW_ARROW
#property indicator_color8  clrTomato
#property indicator_width8  2

input string   Symbol2                    = "NZDCHF";
input bool     Reverse2                   = true;
input string   Symbol3                    = "";
input bool     Reverse3                   = true;
input int      MAPeriod                   = 100;
input int      ATRPeriod                  = 50;
input double   BandMultiplier             = 2.0;
input double   Weight1                    = 1.0;
input double   Weight2                    = 1.0;
input double   Weight3                    = 1.0;
input bool     AutoApplyRecommendedWeights = true;
input bool     NormalizeWeights           = true;
input bool     ShowInfoPanel              = true;
input uchar    PanelTransparency          = 100;
input int      PanelVerticalOffset        = 100;

enum ENUM_WEIGHT_UPDATE_MODE { UPDATE_EVERY_TICK = 0, UPDATE_DAILY = 1, UPDATE_WEEKLY = 2 };
input ENUM_WEIGHT_UPDATE_MODE WeightUpdateMode = UPDATE_WEEKLY;

double SpreadBuffer[],SMABuffer[],UpperBandBuffer[],LowerBandBuffer[];
double BuyArrowBuffer[],SellArrowBuffer[],CloseBuyBuffer[],CloseSellBuffer[];
double Weight1Buffer[],Weight2Buffer[],Weight3Buffer[],ATRSpreadBuffer[];

string Symbols[3];
bool SymbolActive[3];
double PointSize[3];
double w1,w2,w3;
double current_spread_value=0,current_sma_value=0,current_upper_band=0,current_lower_band=0,current_amplitude=0;
string obj_prefix="SpreadPanel_";
bool is_tester=false;
datetime lastWeightUpdateTime = 0;

double CalculateNormalizedVolatility(string symbol, int period)
{
  double volatility = 0;
  if(!SymbolInfoInteger(symbol, SYMBOL_SELECT)) SymbolSelect(symbol, true);
  double closeArr[];
  int copied = CopyClose(symbol, _Period, 0, period+1, closeArr);
  if(copied > 1) {
    double sum_change = 0; int valid_changes = 0;
    for(int j = 1; j < copied; j++) {
      if(closeArr[j] > 0 && closeArr[j-1] > 0) {
        double change_pct = MathAbs(closeArr[j] - closeArr[j-1]) / closeArr[j-1] * 100.0;
        sum_change += change_pct; valid_changes++;
      }
    }
    if(valid_changes > 0) volatility = sum_change / valid_changes;
  }
  return volatility;
}

void CalculateMT4WeightsForTwoSymbols(double &weight1, double &weight2)
{
  string s1 = Symbols[0], s2 = Symbols[1];
  double price1 = SymbolInfoDouble(s1, SYMBOL_BID), price2 = SymbolInfoDouble(s2, SYMBOL_BID);
  double tickValue1 = SymbolInfoDouble(s1, SYMBOL_TRADE_TICK_VALUE), tickValue2 = SymbolInfoDouble(s2, SYMBOL_TRADE_TICK_VALUE);
  double tickSize1  = SymbolInfoDouble(s1, SYMBOL_TRADE_TICK_SIZE), tickSize2  = SymbolInfoDouble(s2, SYMBOL_TRADE_TICK_SIZE);
  if(tickSize1 <= 0) tickSize1 = SymbolInfoDouble(s1, SYMBOL_POINT);
  if(tickSize2 <= 0) tickSize2 = SymbolInfoDouble(s2, SYMBOL_POINT);
  if(tickValue1 <= 0) tickValue1 = 1.0;
  if(tickValue2 <= 0) tickValue2 = 1.0;
  double ynax = (tickValue1 / tickValue2) * (price1 / tickSize1) / (price2 / tickSize2);
  double min_delta = 9999, best_w1 = 1.0, best_w2 = 1.0;
  for(double x = 0.01; x <= 1.0; x += 0.01)
    for(double y = 0.01; y <= 1.0; y += 0.01) {
      double delta = MathAbs(y/x - ynax);
      if(delta < min_delta) { min_delta = delta; best_w1 = x; best_w2 = y; }
    }
  weight1 = best_w1; weight2 = best_w2;
}

void CalculateMT4WeightsForThreeSymbols(double &weight1, double &weight2, double &weight3)
{
  double w12_1, w12_2, w13_1, w13_3;
  string tempSymbols[2] = {Symbols[0], Symbols[1]};
  Symbols[0] = tempSymbols[0]; Symbols[1] = tempSymbols[1];
  CalculateMT4WeightsForTwoSymbols(w12_1, w12_2);
  tempSymbols[1] = Symbols[2];
  CalculateMT4WeightsForTwoSymbols(w13_1, w13_3);
  weight1 = (w12_1 + w13_1) / 2.0; weight2 = w12_2; weight3 = w13_3;
}

void CalculateHybridWeights(double &weight1, double &weight2, double &weight3)
{
  double vol_w1 = 0.0, vol_w2 = 0.0, vol_w3 = 0.0, mt4_w1 = 1.0, mt4_w2 = 1.0, mt4_w3 = 1.0;
  if(SymbolActive[0]) { double vol0 = CalculateNormalizedVolatility(Symbols[0], 20); vol_w1 = (vol0 > 0.001) ? (1.0 / vol0) : 1000.0; }
  if(SymbolActive[1]) { double vol1 = CalculateNormalizedVolatility(Symbols[1], 20); vol_w2 = (vol1 > 0.001) ? (1.0 / vol1) : 1000.0; }
  if(SymbolActive[2]) { double vol2 = CalculateNormalizedVolatility(Symbols[2], 20); vol_w3 = (vol2 > 0.001) ? (1.0 / vol2) : 1000.0; }
  if(SymbolActive[2]) CalculateMT4WeightsForThreeSymbols(mt4_w1, mt4_w2, mt4_w3);
  else CalculateMT4WeightsForTwoSymbols(mt4_w1, mt4_w2);
  weight1 = (vol_w1 * 0.5) + (mt4_w1 * 0.5);
  weight2 = (vol_w2 * 0.5) + (mt4_w2 * 0.5);
  weight3 = SymbolActive[2] ? ((vol_w3 * 0.5) + (mt4_w3 * 0.5)) : 0.0;
}

void UpdateWeights()
{
  double base0 = Weight1, base1 = Weight2, base2 = Weight3;
  if(AutoApplyRecommendedWeights) CalculateHybridWeights(base0, base1, base2);
  for(int i = 0; i < 3; i++) {
    if(!SymbolActive[i]) continue;
    double tickSize = SymbolInfoDouble(Symbols[i], SYMBOL_TRADE_TICK_SIZE);
    double tickValue = SymbolInfoDouble(Symbols[i], SYMBOL_TRADE_TICK_VALUE);
    double tickVolume = SymbolInfoDouble(Symbols[i], SYMBOL_SESSION_VOLUME);
    if(tickSize <= 0.0) tickSize = SymbolInfoDouble(Symbols[i], SYMBOL_POINT);
    if(tickSize <= 0.0) tickSize = 1.0;
    if(tickValue <= 0.0) tickValue = 1.0;
    if(tickVolume <= 0.0) tickVolume = 1.0;
    if(tickVolume <= 1.0) {
      double altVol = SymbolInfoDouble(Symbols[i], SYMBOL_VOLUME_LIMIT);
      if(altVol > 0) tickVolume = altVol; else tickVolume = 100000;
    }
    double liquidity_factor = MathSqrt(tickVolume) * tickValue / tickSize;
    double adjust = MathLog10(MathMax(1.0, liquidity_factor));
    if(adjust < 0.5) adjust = 0.5;
    if(adjust > 3.0) adjust = 3.0;
    if(i == 0) base0 *= adjust;
    if(i == 1) base1 *= adjust;
    if(i == 2) base2 *= adjust;
  }
  w1 = base0; w2 = base1; w3 = base2;
  if(NormalizeWeights) {
    double sum = MathAbs(w1) + MathAbs(w2) + MathAbs(w3);
    if(sum > 0.001) { w1 /= sum; w2 /= sum; w3 /= sum; }
    else {
      int active_count = 0;
      if(SymbolActive[0]) { active_count++; w1 = 1.0; } else w1 = 0.0;
      if(SymbolActive[1]) { active_count++; w2 = 1.0; } else w2 = 0.0;
      if(SymbolActive[2]) { active_count++; w3 = 1.0; } else w3 = 0.0;
      if(active_count > 0) { double eq = 1.0 / active_count; if(SymbolActive[0]) w1 = eq; if(SymbolActive[1]) w2 = eq; if(SymbolActive[2]) w3 = eq; }
    }
  }
  if(!MathIsValidNumber(w1) || !MathIsValidNumber(w2) || !MathIsValidNumber(w3)) { w1 = Weight1; w2 = Weight2; w3 = Weight3; }
}

double CalculateSpreadValue(int bar_index)
{
  double balanced = 0.0; bool any_valid = false;
  if(SymbolActive[0] && PointSize[0] > 0.0) { double p = iClose(Symbols[0], _Period, bar_index); if(p != EMPTY_VALUE && p > 0.0) { balanced += p / PointSize[0] * w1; any_valid = true; } }
  if(SymbolActive[1] && PointSize[1] > 0.0) { double p = iClose(Symbols[1], _Period, bar_index); if(p != EMPTY_VALUE && p > 0.0) { balanced += (Reverse2 ? 1.0 : -1.0) * p / PointSize[1] * w2; any_valid = true; } }
  if(SymbolActive[2] && PointSize[2] > 0.0) { double p = iClose(Symbols[2], _Period, bar_index); if(p != EMPTY_VALUE && p > 0.0) { balanced += (Reverse3 ? 1.0 : -1.0) * p / PointSize[2] * w3; any_valid = true; } }
  if(!any_valid) { if(bar_index+1 < ArraySize(SpreadBuffer) && SpreadBuffer[bar_index+1] != EMPTY_VALUE) return SpreadBuffer[bar_index+1]; return EMPTY_VALUE; }
  return balanced;
}

double CalculateSMA(int series_index, int period)
{
  if(series_index < 0 || series_index >= ArraySize(SpreadBuffer)) return EMPTY_VALUE;
  if(period <= 0) return SpreadBuffer[series_index];
  double sum = 0.0; int count = 0;
  int limit = MathMin(ArraySize(SpreadBuffer), series_index + period);
  for(int i = series_index; i < limit; i++) { if(i >= 0 && i < ArraySize(SpreadBuffer) && SpreadBuffer[i] != EMPTY_VALUE) { sum += SpreadBuffer[i]; count++; } }
  if(count == 0) return EMPTY_VALUE;
  return sum / count;
}

void CalculateBollinger(int series_index,int period,double multiplier,double &upper,double &lower)
{
  upper = lower = EMPTY_VALUE;
  if(series_index < 0 || series_index >= ArraySize(SMABuffer)) return;
  double sma = SMABuffer[series_index];
  if(sma == EMPTY_VALUE) return;
  double variance = 0.0; int count = 0;
  int limit = MathMin(ArraySize(SpreadBuffer), series_index + period);
  for(int i = series_index; i < limit; i++) {
    if(i >= 0 && i < ArraySize(SpreadBuffer) && SpreadBuffer[i] != EMPTY_VALUE) { double diff = SpreadBuffer[i] - sma; variance += diff * diff; count++; }
  }
  if(count > 1) { double stddev = MathSqrt(variance / count); upper = sma + multiplier * stddev; lower = sma - multiplier * stddev; }
  else { upper = sma; lower = sma; }
}

double CalculateSpreadRange(int bar_index, int period)
{
  if(period <= 1 || bar_index < 0) return 0.0;
  double max_spread = -DBL_MAX, min_spread = DBL_MAX;
  bool found_values = false;
  int start_bar = MathMax(0, bar_index);
  int end_bar = MathMin(ArraySize(SpreadBuffer)-1, bar_index + period - 1);
  for(int i = start_bar; i <= end_bar; i++) {
    double val = SpreadBuffer[i];
    if(val != EMPTY_VALUE && val != 0 && MathIsValidNumber(val)) { if(val > max_spread) max_spread = val; if(val < min_spread) min_spread = val; found_values = true; }
  }
  if(!found_values) return 0.0;
  double range = MathAbs(max_spread - min_spread);
  if(range < 0.000001) return MathAbs(SpreadBuffer[bar_index] > 0.000001 ? SpreadBuffer[bar_index] * 0.05 : 1.0);
  return range;
}

int OnInit()
{
  is_tester = (MQLInfoInteger(MQL_TESTER) != 0) || (MQLInfoInteger(MQL_OPTIMIZATION) != 0);
  IndicatorSetInteger(INDICATOR_DIGITS, 5);
  IndicatorSetString(INDICATOR_SHORTNAME, "SpreadsSMA3Balance v16.3");
  Symbols[0] = _Symbol; Symbols[1] = Symbol2; Symbols[2] = Symbol3;
  SymbolActive[0] = true; SymbolActive[1] = (StringLen(Symbol2) > 0); SymbolActive[2] = (StringLen(Symbol3) > 0);
  for(int i=0;i<3;i++) {
    if(SymbolActive[i]) { SymbolSelect(Symbols[i], true); PointSize[i] = SymbolInfoDouble(Symbols[i], SYMBOL_POINT); if(PointSize[i] <= 0.0) PointSize[i] = 1.0; }
    else PointSize[i] = 1.0;
  }
  SetIndexBuffer(0, SpreadBuffer, INDICATOR_DATA); SetIndexBuffer(1, SMABuffer, INDICATOR_DATA);
  SetIndexBuffer(2, UpperBandBuffer, INDICATOR_DATA); SetIndexBuffer(3, LowerBandBuffer, INDICATOR_DATA);
  SetIndexBuffer(4, BuyArrowBuffer, INDICATOR_DATA); SetIndexBuffer(5, SellArrowBuffer, INDICATOR_DATA);
  SetIndexBuffer(6, CloseBuyBuffer, INDICATOR_DATA); SetIndexBuffer(7, CloseSellBuffer, INDICATOR_DATA);
  SetIndexBuffer(8, Weight1Buffer, INDICATOR_CALCULATIONS); SetIndexBuffer(9, Weight2Buffer, INDICATOR_CALCULATIONS);
  SetIndexBuffer(10, Weight3Buffer, INDICATOR_CALCULATIONS); SetIndexBuffer(11, ATRSpreadBuffer, INDICATOR_CALCULATIONS);
  //removed
  // Actually let me do it manually
  ArraySetAsSeries(SpreadBuffer,true); ArraySetAsSeries(SMABuffer,true); ArraySetAsSeries(UpperBandBuffer,true); ArraySetAsSeries(LowerBandBuffer,true);
  ArraySetAsSeries(BuyArrowBuffer,true); ArraySetAsSeries(SellArrowBuffer,true); ArraySetAsSeries(CloseBuyBuffer,true); ArraySetAsSeries(CloseSellBuffer,true);
  ArraySetAsSeries(Weight1Buffer,true); ArraySetAsSeries(Weight2Buffer,true); ArraySetAsSeries(Weight3Buffer,true); ArraySetAsSeries(ATRSpreadBuffer,true);
  PlotIndexSetDouble(4, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetDouble(5, PLOT_EMPTY_VALUE, EMPTY_VALUE);
  PlotIndexSetDouble(6, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetDouble(7, PLOT_EMPTY_VALUE, EMPTY_VALUE);
  PlotIndexSetInteger(4, PLOT_ARROW, 233); PlotIndexSetInteger(5, PLOT_ARROW, 234);
  PlotIndexSetInteger(6, PLOT_ARROW, 251); PlotIndexSetInteger(7, PLOT_ARROW, 251);
  UpdateWeights(); lastWeightUpdateTime = TimeCurrent();
  if(ShowInfoPanel && !is_tester) CreateInfoPanel(); else Comment("");
  return(INIT_SUCCEEDED);
}

int OnCalculate(const int rt, const int pc, 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(rt <= 0) return 0;
  if(ArraySize(SpreadBuffer) != rt) {
    ArrayResize(SpreadBuffer, rt); ArrayResize(SMABuffer, rt); ArrayResize(UpperBandBuffer, rt); ArrayResize(LowerBandBuffer, rt);
    ArrayResize(BuyArrowBuffer, rt); ArrayResize(SellArrowBuffer, rt); ArrayResize(CloseBuyBuffer, rt); ArrayResize(CloseSellBuffer, rt);
    ArrayResize(Weight1Buffer, rt); ArrayResize(Weight2Buffer, rt); ArrayResize(Weight3Buffer, rt); ArrayResize(ATRSpreadBuffer, rt);
    ArraySetAsSeries(SpreadBuffer,true); ArraySetAsSeries(SMABuffer,true); ArraySetAsSeries(UpperBandBuffer,true); ArraySetAsSeries(LowerBandBuffer,true);
    ArraySetAsSeries(BuyArrowBuffer,true); ArraySetAsSeries(SellArrowBuffer,true); ArraySetAsSeries(CloseBuyBuffer,true); ArraySetAsSeries(CloseSellBuffer,true);
    ArraySetAsSeries(Weight1Buffer,true); ArraySetAsSeries(Weight2Buffer,true); ArraySetAsSeries(Weight3Buffer,true); ArraySetAsSeries(ATRSpreadBuffer,true);
  }
  bool needUpdate = false;
  datetime ct = TimeCurrent();
  if(WeightUpdateMode == UPDATE_EVERY_TICK) needUpdate = true;
  else if(WeightUpdateMode == UPDATE_DAILY && ct - lastWeightUpdateTime >= 24*60*60) needUpdate = true;
  else if(WeightUpdateMode == UPDATE_WEEKLY && ct - lastWeightUpdateTime >= 7*24*60*60) needUpdate = true;
  MqlDateTime td; TimeToStruct(ct, td);
  if(WeightUpdateMode == UPDATE_WEEKLY && td.day_of_week == 5 && td.hour >= 23 && ct - lastWeightUpdateTime >= 6*24*60*60) needUpdate = true;
  if(needUpdate) { UpdateWeights(); lastWeightUpdateTime = ct; }
  int start = (pc == 0) ? rt - 1 : pc - 1;
  if(start < 0) start = 0;
  if(start >= rt) start = rt - 1;
  for(int i = start; i >= 0 && !IsStopped(); i--) {
    double s = CalculateSpreadValue(i);
    SpreadBuffer[i] = (s == EMPTY_VALUE && i+1 < ArraySize(SpreadBuffer)) ? SpreadBuffer[i+1] : s;
    if(i < ArraySize(Weight1Buffer)) Weight1Buffer[i] = w1;
    if(i < ArraySize(Weight2Buffer)) Weight2Buffer[i] = w2;
    if(i < ArraySize(Weight3Buffer)) Weight3Buffer[i] = w3;
    double sma = CalculateSMA(i, MAPeriod);
    if(i < ArraySize(SMABuffer)) SMABuffer[i] = (sma == EMPTY_VALUE) ? SpreadBuffer[i] : sma;
    double up=EMPTY_VALUE, dn=EMPTY_VALUE;
    CalculateBollinger(i, MAPeriod, BandMultiplier, up, dn);
    if(i < ArraySize(UpperBandBuffer)) UpperBandBuffer[i] = up;
    if(i < ArraySize(LowerBandBuffer)) LowerBandBuffer[i] = dn;
  }
  if(ArraySize(SpreadBuffer) > 0) current_spread_value = SpreadBuffer[0];
  if(ArraySize(SMABuffer) > 0) current_sma_value = SMABuffer[0];
  if(ArraySize(UpperBandBuffer) > 0) current_upper_band = UpperBandBuffer[0];
  if(ArraySize(LowerBandBuffer) > 0) current_lower_band = LowerBandBuffer[0];
  for(int j = 0; j < rt && j < ArraySize(ATRSpreadBuffer); j++) ATRSpreadBuffer[j] = CalculateSpreadRange(j, ATRPeriod);
  if(ArraySize(ATRSpreadBuffer) > 0) current_amplitude = ATRSpreadBuffer[0];
//--- сигналы открытия (только на пробой полосы)
  for(int i = start; i >= 0 && !IsStopped(); i--) {
    BuyArrowBuffer[i] = EMPTY_VALUE; SellArrowBuffer[i] = EMPTY_VALUE;
    if(i < rt-1 && i < ArraySize(UpperBandBuffer) && i < ArraySize(LowerBandBuffer) && i < ArraySize(SpreadBuffer)) {
      double up = UpperBandBuffer[i], up1 = UpperBandBuffer[i+1];
      double dn = LowerBandBuffer[i], dn1 = LowerBandBuffer[i+1];
      double sp = SpreadBuffer[i], sp1 = SpreadBuffer[i+1];
      if(sp != EMPTY_VALUE && sp1 != EMPTY_VALUE && up != EMPTY_VALUE && up1 != EMPTY_VALUE && dn != EMPTY_VALUE && dn1 != EMPTY_VALUE) {
        double off = MathMax(1.5, current_amplitude * 0.15);
        if(sp1 > up1 && sp <= up) { SellArrowBuffer[i] = up + off; }
        if(sp1 < dn1 && sp >= dn) { BuyArrowBuffer[i] = dn - off; }
      }
    }
  }
//--- сигналы закрытия (глобальный проход)
  int st = 0;
  for(int i = rt-1; i >= 0; i--) {
    CloseBuyBuffer[i] = EMPTY_VALUE; CloseSellBuffer[i] = EMPTY_VALUE;
    if(i < ArraySize(SpreadBuffer) && i < ArraySize(UpperBandBuffer) && i < ArraySize(LowerBandBuffer) && i < ArraySize(SMABuffer)) {
      double sp = SpreadBuffer[i], up = UpperBandBuffer[i], dn = LowerBandBuffer[i], sma = SMABuffer[i];
      if(sp != EMPTY_VALUE && up != EMPTY_VALUE && dn != EMPTY_VALUE && sma != EMPTY_VALUE) {
        if(i < rt-1) {
          if(BuyArrowBuffer[i] != EMPTY_VALUE) st = 2;
          if(SellArrowBuffer[i] != EMPTY_VALUE) st = 1;
        }
        if(st == 2 && sp > sma) { st = 0; CloseBuyBuffer[i] = sp; }
        if(st == 1 && sp < sma) { st = 0; CloseSellBuffer[i] = sp; }
      }
    }
  }
  if(ShowInfoPanel && !is_tester) UpdateInfoPanel();
  return rt;
}

void CreateInfoPanel()
{
  if(is_tester || !ShowInfoPanel) return;
  long cw = ChartGetInteger(0, CHART_WIDTH_IN_PIXELS), ch = ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS);
  int pw = (int)MathMin(600, cw * 0.8), ph = 160;
  int sx = MathMax(10, MathMin((int)((cw - pw) / 2), (int)cw - pw - 10));
  int sy = MathMax(50, MathMin((int)((ch - ph) / 3) + PanelVerticalOffset, (int)ch - ph - 50));
  string bg = obj_prefix+"BG";
  if(ObjectFind(0,bg)==-1) {
    ObjectCreate(0,bg,OBJ_RECTANGLE_LABEL,0,0,0);
    ObjectSetInteger(0,bg,OBJPROP_XDISTANCE,sx); ObjectSetInteger(0,bg,OBJPROP_YDISTANCE,sy);
    ObjectSetInteger(0,bg,OBJPROP_XSIZE,pw); ObjectSetInteger(0,bg,OBJPROP_YSIZE,ph);
    ObjectSetInteger(0,bg,OBJPROP_BGCOLOR,clrDarkSlateGray); ObjectSetInteger(0,bg,OBJPROP_BORDER_TYPE,BORDER_FLAT);
    ObjectSetInteger(0,bg,OBJPROP_COLOR,clrGray); ObjectSetInteger(0,bg,OBJPROP_BACK,true);
    ObjectSetInteger(0,bg,OBJPROP_CORNER,CORNER_LEFT_UPPER);
  }
  string lab[] = {"Spread","SMA","Bands","ATR","Weights","Signal","Mode"};
  string desc[] = {"Спред:","SMA:","Полосы:","ATR:","Веса:","Сигнал:","Режим:"};
  color cols[] = {clrGold,clrRed,clrDodgerBlue,clrLime,clrViolet,clrWhite,clrOrange};
  for(int i=0;i<7;i++) {
    string nm = obj_prefix+lab[i];
    if(ObjectFind(0,nm)==-1) {
      ObjectCreate(0,nm,OBJ_LABEL,0,0,0); ObjectSetString(0,nm,OBJPROP_FONT,"Consolas");
      ObjectSetInteger(0,nm,OBJPROP_FONTSIZE,10); ObjectSetInteger(0,nm,OBJPROP_COLOR,cols[i]);
      ObjectSetInteger(0,nm,OBJPROP_CORNER,CORNER_LEFT_UPPER); ObjectSetInteger(0,nm,OBJPROP_BACK,false);
      ObjectSetInteger(0,nm,OBJPROP_XDISTANCE,sx+20); ObjectSetInteger(0,nm,OBJPROP_YDISTANCE,sy+20+i*20);
      ObjectSetString(0,nm,OBJPROP_TEXT,desc[i]+" загрузка...");
    }
  }
  string ti = obj_prefix+"TITLE";
  if(ObjectFind(0,ti)==-1) {
    ObjectCreate(0,ti,OBJ_LABEL,0,0,0); ObjectSetString(0,ti,OBJPROP_FONT,"Consolas Bold");
    ObjectSetInteger(0,ti,OBJPROP_FONTSIZE,12); ObjectSetInteger(0,ti,OBJPROP_COLOR,clrGold);
    ObjectSetInteger(0,ti,OBJPROP_CORNER,CORNER_LEFT_UPPER); ObjectSetInteger(0,ti,OBJPROP_BACK,false);
    ObjectSetInteger(0,ti,OBJPROP_XDISTANCE,sx+20); ObjectSetInteger(0,ti,OBJPROP_YDISTANCE,sy+5);
  }
  string sn = Symbols[0]; if(SymbolActive[1]) sn += (Reverse2 ? " + ":" - ")+Symbols[1]; if(SymbolActive[2]) sn += (Reverse3 ? " + ":" - ")+Symbols[2];
  ObjectSetString(0,ti,OBJPROP_TEXT,"🎯 СПРЕД v16.3: "+sn);
}

void UpdateInfoPanel()
{
  if(is_tester || !ShowInfoPanel) return;
  static datetime lup = 0; datetime nw = TimeCurrent();
  if(nw - lup < 20) return; lup = nw;
  string sig = "НЕТ СИГНАЛА"; color sc = clrWhite;
  if(current_spread_value > current_upper_band) { sig = "ПРОДАВАТЬ"; sc = clrRed; }
  else if(current_spread_value < current_lower_band) { sig = "ПОКУПАТЬ"; sc = clrLime; }
  string txt[] = {
    StringFormat("Спред: %.5f",current_spread_value),
    StringFormat("SMA(%d): %.5f",MAPeriod,current_sma_value),
    StringFormat("Полосы: [%.5f / %.5f]",current_lower_band,current_upper_band),
    StringFormat("ATR(%d): %.5f",ATRPeriod,current_amplitude),
    StringFormat("Веса: W1=%.3f W2=%.3f W3=%.3f",w1,w2,w3),
    StringFormat("Сигнал: %s",sig),
    "Режим: "+(AutoApplyRecommendedWeights?"авто":"РУЧНЫЕ ВЕСА")
  };
  color cl[] = {clrGold,clrRed,clrDodgerBlue,clrLime,clrViolet,sc,clrOrange};
  string lab[] = {"Spread","SMA","Bands","ATR","Weights","Signal","Mode"};
  for(int i=0;i<7;i++) { string nm = obj_prefix+lab[i]; if(ObjectFind(0,nm)!=-1) { ObjectSetString(0,nm,OBJPROP_TEXT,txt[i]); ObjectSetInteger(0,nm,OBJPROP_COLOR,cl[i]); } }
  string ti = obj_prefix+"TITLE";
  if(ObjectFind(0,ti)!=-1) { string sn = Symbols[0]; if(SymbolActive[1]) sn += (Reverse2?" + ":" - ")+Symbols[1]; if(SymbolActive[2]) sn += (Reverse3?" + ":" - ")+Symbols[2]; ObjectSetString(0,ti,OBJPROP_TEXT,"🎯 SPREAD v16.3: "+sn); }
}

void OnDeinit(const int r) { if(!is_tester && ShowInfoPanel) { ObjectsDeleteAll(0, obj_prefix); Comment(""); } }
//+------------------------------------------------------------------+