//+------------------------------------------------------------------+
//|                                        Currency_Strength_Pro.mq5 |
//|                                          Copyright 2025, xxxxxxxx|
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property version   "2.30" // Fixed smoothing overflow bug
#property description "Displays the relative strength of 8 major currencies."

#property indicator_separate_window
#property indicator_buffers 8
#property indicator_plots   8
#property indicator_level1  0.0
#property indicator_levelstyle STYLE_DOT

//--- Plot Settings
#property indicator_label1  "USD"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrGreen
#property indicator_width1  2

#property indicator_label2  "EUR"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrDodgerBlue
#property indicator_width2  2

#property indicator_label3  "GBP"
#property indicator_type3   DRAW_LINE
#property indicator_color3  clrRed
#property indicator_width3  2

#property indicator_label4  "JPY"
#property indicator_type4   DRAW_LINE
#property indicator_color4  clrGold
#property indicator_width4  2

#property indicator_label5  "AUD"
#property indicator_type5   DRAW_LINE
#property indicator_color5  clrOrange
#property indicator_width5  1

#property indicator_label6  "CAD"
#property indicator_type6   DRAW_LINE
#property indicator_color6  clrMagenta
#property indicator_width6  1

#property indicator_label7  "CHF"
#property indicator_type7   DRAW_LINE
#property indicator_color7  clrSlateGray
#property indicator_width7  1

#property indicator_label8  "NZD"
#property indicator_type8   DRAW_LINE
#property indicator_color8  clrTurquoise
#property indicator_width8  1

#include <MyIncludes\Currency_Strength_Calculator.mqh>

//--- Input Parameters
input int             InpPeriod    = 14;           // ROC Period
input ENUM_TIMEFRAMES InpTimeframe = PERIOD_CURRENT; // Calculation Timeframe
input bool            InpSmooth    = true;         // Smooth Results?
input int             InpSmoothPer = 5;            // Smoothing Period
input bool            InpShowPanel = true;         // Show Dashboard Panel?

input group           "Visibility Settings"
input bool            InpShowUSD   = true;         // Show USD
input bool            InpShowEUR   = true;         // Show EUR
input bool            InpShowGBP   = true;         // Show GBP
input bool            InpShowJPY   = true;         // Show JPY
input bool            InpShowAUD   = true;         // Show AUD
input bool            InpShowCAD   = true;         // Show CAD
input bool            InpShowCHF   = true;         // Show CHF
input bool            InpShowNZD   = true;         // Show NZD

//--- Buffers
double BufUSD[], BufEUR[], BufGBP[], BufJPY[], BufAUD[], BufCAD[], BufCHF[], BufNZD[];

//--- Global Objects
CCurrencyStrengthCalculator *g_calculator;
string g_prefix;
int    g_window_idx;
bool   g_show_flags[8];

//+------------------------------------------------------------------+
int OnInit()
  {
   SetIndexBuffer(0, BufUSD, INDICATOR_DATA);
   SetIndexBuffer(1, BufEUR, INDICATOR_DATA);
   SetIndexBuffer(2, BufGBP, INDICATOR_DATA);
   SetIndexBuffer(3, BufJPY, INDICATOR_DATA);
   SetIndexBuffer(4, BufAUD, INDICATOR_DATA);
   SetIndexBuffer(5, BufCAD, INDICATOR_DATA);
   SetIndexBuffer(6, BufCHF, INDICATOR_DATA);
   SetIndexBuffer(7, BufNZD, INDICATOR_DATA);

// Initialize with EMPTY_VALUE to detect uncalculated areas
   PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetDouble(3, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   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);

// Store flags
   g_show_flags[0] = InpShowUSD;
   g_show_flags[1] = InpShowEUR;
   g_show_flags[2] = InpShowGBP;
   g_show_flags[3] = InpShowJPY;
   g_show_flags[4] = InpShowAUD;
   g_show_flags[5] = InpShowCAD;
   g_show_flags[6] = InpShowCHF;
   g_show_flags[7] = InpShowNZD;

   for(int i=0; i<8; i++)
     {
      PlotIndexSetInteger(i, PLOT_DRAW_BEGIN, InpPeriod + (InpSmooth ? InpSmoothPer : 0));

      if(!g_show_flags[i])
         PlotIndexSetInteger(i, PLOT_DRAW_TYPE, DRAW_NONE);
     }

   g_calculator = new CCurrencyStrengthCalculator();
   ENUM_TIMEFRAMES tf = (InpTimeframe == PERIOD_CURRENT) ? (ENUM_TIMEFRAMES)Period() : InpTimeframe;
   g_calculator.Init(InpPeriod, tf);

   g_prefix = "CS_Pro_" + IntegerToString(ChartID()) + "_";
   g_window_idx = ChartWindowFind();
   ObjectsDeleteAll(0, g_prefix);

   IndicatorSetString(INDICATOR_SHORTNAME, "Currency Strength");
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(CheckPointer(g_calculator) != POINTER_INVALID)
      delete g_calculator;
   ObjectsDeleteAll(0, g_prefix);
  }

//+------------------------------------------------------------------+
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[])
  {
   g_window_idx = ChartWindowFind();

   if(!g_calculator.IsDataReady())
      return 0;

   int start_index;
   if(prev_calculated == 0)
     {
      start_index = 0;
      // Explicitly clear buffers on full recalc
      ArrayInitialize(BufUSD, EMPTY_VALUE);
      ArrayInitialize(BufEUR, EMPTY_VALUE);
      ArrayInitialize(BufGBP, EMPTY_VALUE);
      ArrayInitialize(BufJPY, EMPTY_VALUE);
      ArrayInitialize(BufAUD, EMPTY_VALUE);
      ArrayInitialize(BufCAD, EMPTY_VALUE);
      ArrayInitialize(BufCHF, EMPTY_VALUE);
      ArrayInitialize(BufNZD, EMPTY_VALUE);
     }
   else
      start_index = prev_calculated - 1;

   if(start_index == 0 && rates_total > 500)
      start_index = rates_total - 500;

// --- 1. Calculate Raw Strength ---
   for(int i = start_index; i < rates_total; i++)
     {
      int shift = rates_total - 1 - i;
      double strengths[8];
      g_calculator.CalculateStep(shift, strengths);

      BufUSD[i] = strengths[0];
      BufEUR[i] = strengths[1];
      BufGBP[i] = strengths[2];
      BufJPY[i] = strengths[3];
      BufAUD[i] = strengths[4];
      BufCAD[i] = strengths[5];
      BufCHF[i] = strengths[6];
      BufNZD[i] = strengths[7];
     }

// --- 2. Apply Smoothing ---
   if(InpSmooth && InpSmoothPer > 1)
     {
      double alpha = 2.0 / (InpSmoothPer + 1.0);

      // Determine start of smoothing loop
      int i = start_index;

      // If this is the very first bar of the calculation (e.g. index 0 or index 1000 after limit),
      // we cannot smooth it with the previous bar because the previous bar is EMPTY or non-existent.
      // So we skip the first bar of the batch (leaving it raw) and start smoothing from the next.

      if(i == 0)
         i = 1;
      else
         if(BufUSD[i-1] == EMPTY_VALUE)
            i++;

      for(; i < rates_total; i++)
        {
         // Safety check: if prev value is empty, we can't smooth.
         if(BufUSD[i-1] == EMPTY_VALUE)
            continue;

         BufUSD[i] = BufUSD[i] * alpha + BufUSD[i-1] * (1.0 - alpha);
         BufEUR[i] = BufEUR[i] * alpha + BufEUR[i-1] * (1.0 - alpha);
         BufGBP[i] = BufGBP[i] * alpha + BufGBP[i-1] * (1.0 - alpha);
         BufJPY[i] = BufJPY[i] * alpha + BufJPY[i-1] * (1.0 - alpha);
         BufAUD[i] = BufAUD[i] * alpha + BufAUD[i-1] * (1.0 - alpha);
         BufCAD[i] = BufCAD[i] * alpha + BufCAD[i-1] * (1.0 - alpha);
         BufCHF[i] = BufCHF[i] * alpha + BufCHF[i-1] * (1.0 - alpha);
         BufNZD[i] = BufNZD[i] * alpha + BufNZD[i-1] * (1.0 - alpha);
        }
     }

// --- 3. Update Dashboard ---
   if(InpShowPanel)
      DrawDashboard(rates_total - 1);

   return(rates_total);
  }

//+------------------------------------------------------------------+
//| Draw Dashboard Panel                                             |
//+------------------------------------------------------------------+
void DrawDashboard(int last_idx)
  {
   string currencies[] = {"USD", "EUR", "GBP", "JPY", "AUD", "CAD", "CHF", "NZD"};
   color colors[]      = {clrGreen, clrDodgerBlue, clrRed, clrGold, clrOrange, clrMagenta, clrSlateGray, clrTurquoise};
   double values[8];

   values[0] = BufUSD[last_idx];
   values[1] = BufEUR[last_idx];
   values[2] = BufGBP[last_idx];
   values[3] = BufJPY[last_idx];
   values[4] = BufAUD[last_idx];
   values[5] = BufCAD[last_idx];
   values[6] = BufCHF[last_idx];
   values[7] = BufNZD[last_idx];

// Handle NaN/Empty
   for(int i=0; i<8; i++)
      if(!MathIsValidNumber(values[i]) || values[i] == EMPTY_VALUE)
         values[i] = 0.0;

// Sort
   int indices[] = {0, 1, 2, 3, 4, 5, 6, 7};
   for(int i=0; i<8; i++)
      for(int j=0; j<7-i; j++)
         if(values[indices[j]] < values[indices[j+1]])
           {
            int temp = indices[j];
            indices[j] = indices[j+1];
            indices[j+1] = temp;
           }

   int x_base = 10;
   int x_step = 95;
   int y_pos = 20;
   int drawn_count = 0;

   for(int i=0; i<8; i++)
     {
      int idx = indices[i];

      if(!g_show_flags[idx])
         continue;

      string name = g_prefix + "Label_" + IntegerToString(i);

      string val_str;
      if(values[idx] == 0.0 && (BufUSD[last_idx] == EMPTY_VALUE || BufUSD[last_idx] == 0.0))
         val_str = "N/A";
      else
         val_str = StringFormat("%.3f", values[idx]);

      string text = StringFormat("%s: %s", currencies[idx], val_str);

      if(ObjectFind(0, name) < 0)
        {
         ObjectCreate(0, name, OBJ_LABEL, g_window_idx, 0, 0);
         ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_LOWER);
         ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y_pos);
         ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 9);
         ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_LEFT_LOWER);
        }

      ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x_base + drawn_count * x_step);
      ObjectSetString(0, name, OBJPROP_TEXT, text);
      ObjectSetInteger(0, name, OBJPROP_COLOR, colors[idx]);

      drawn_count++;
     }

   ChartRedraw();
  }
//+------------------------------------------------------------------+
