﻿//+------------------------------------------------------------------+
//|                                                SwamiRSI_v1.mq5   |
//|                         Copyright © 2012, TrendLaboratory        |
//|         http://finance.groups.yahoo.com/group/TrendLaboratory    |
//|                                  E-mail: igorad2003@yahoo.co.uk  |
//|                         Converted from MQL4 to MQL5              |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2012, TrendLaboratory"
#property link      "http://finance.groups.yahoo.com/group/TrendLaboratory"

#property indicator_separate_window
#property indicator_buffers 4
#property indicator_plots   2

#property indicator_label1  "SwamiRSI"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDarkOrchid
#property indicator_width1  2

#property indicator_label2  "SummaryRSI"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrChocolate
#property indicator_width2  2

//---- input parameters
input int     VisualMode     =        0;    // 0=original, 1=trend
input int     Price          =        0;    // Applied Price (0=Close;1=Open;2=High;3=Low;4=Median;5=Typical;6=Weighted)
input int     StartLength    =       12;    // RSI Start Period
input int     EndLength      =       48;    // RSI End Period
input int     SampleLength   =       48;    // Sample RSI Period (0=off)
input int     Smooth         =        5;    // Smoothing Period
input color   UpTrendColor   =  clrLime;   // UpTrend Color
input color   DnTrendColor   =   clrRed;   // DownTrend Color
input color   FlatColor      = clrYellow;  // Flat Color (if CLR_NONE = 2-color mix)
input string  UniqueName     =    "RSI";   // Object name prefix
input int     ScaleMode      =        1;    // 0=J.Ehlers, 1=0..100
input int     SwamiBars      =      100;    // -1=off, 0=all bars, >0=any number

//---- indicator buffers
double RSIBuffer[];
double sumRSIBuffer[];
double loValueBuffer[];
double upValueBuffer[];

//---- internal state
int      win;
int      swamisize;
int      upLineR, upLineG, upLineB;
int      dnLineR, dnLineG, dnLineB;
int      flLineR, flLineG, flLineB;
double   fuzzWidth, maxValue, minValue;
string   short_name;

double   swamiRSI[];
double   ema[][2];
double   price_state[2];
int      prevtime[];
int      trend[][2];
int      prevtrendtime;

// Adjusted SampleLength (may be clamped in OnInit)
int      SampleLengthAdj;

//+------------------------------------------------------------------+
//| Helper: decompose MQL5 color into R, G, B                        |
//+------------------------------------------------------------------+
void colorToRGB(color clr, int &R, int &G, int &B)
{
   R =  clr        & 0xFF;
   G = (clr >>  8) & 0xFF;
   B = (clr >> 16) & 0xFF;
}

//+------------------------------------------------------------------+
//| Helper: compose R,G,B into MQL5 color (BGR order)                |
//+------------------------------------------------------------------+
color rgbToColor(int R, int G, int B)
{
   // MQL5 color = 0x00BBGGRR
   return (color)(R | (G << 8) | (B << 16));
}

//+------------------------------------------------------------------+
//| OnInit                                                           |
//+------------------------------------------------------------------+
int OnInit()
{
   colorToRGB(UpTrendColor, upLineR, upLineG, upLineB);
   colorToRGB(DnTrendColor, dnLineR, dnLineG, dnLineB);
   colorToRGB(FlatColor,    flLineR, flLineG, flLineB);

   short_name = "SwamiRSI_v1(" + IntegerToString(StartLength) + "," + IntegerToString(EndLength) + ")";
   IndicatorSetString(INDICATOR_SHORTNAME, short_name);

   //---- buffers
   SetIndexBuffer(0, RSIBuffer,    INDICATOR_DATA);
   SetIndexBuffer(1, sumRSIBuffer, INDICATOR_DATA);
   SetIndexBuffer(2, loValueBuffer,INDICATOR_DATA);
   SetIndexBuffer(3, upValueBuffer,INDICATOR_DATA);

   PlotIndexSetString(0,  PLOT_LABEL, "SwamiRSI("  + IntegerToString(SampleLength) + ")");
   PlotIndexSetString(1,  PLOT_LABEL, "SummaryRSI");
   PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, EndLength);
   PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, EndLength);

   // Buffers 2 & 3 are INDICATOR_DATA but we want them invisible (scale lines)
   PlotIndexSetInteger(2, PLOT_DRAW_TYPE, DRAW_NONE);
   PlotIndexSetInteger(3, PLOT_DRAW_TYPE, DRAW_NONE);

   ArrayInitialize(RSIBuffer,     EMPTY_VALUE);
   ArrayInitialize(sumRSIBuffer,  EMPTY_VALUE);
   ArrayInitialize(loValueBuffer, EMPTY_VALUE);
   ArrayInitialize(upValueBuffer, EMPTY_VALUE);

   swamisize = EndLength - StartLength + 1;
   ArrayResize(ema,      swamisize * 3);
   ArrayResize(prevtime, swamisize * 3);
   ArrayResize(swamiRSI, swamisize);
   ArrayResize(trend,    swamisize);

   SampleLengthAdj = SampleLength;
   if(SampleLengthAdj > 0)
   {
      if(SampleLengthAdj < StartLength) SampleLengthAdj = StartLength;
      if(SampleLengthAdj > EndLength)   SampleLengthAdj = EndLength;
   }

   if(ScaleMode == 0) { fuzzWidth = 1;                              maxValue = EndLength;   minValue = StartLength; }
   else               { fuzzWidth = 100.0 / (EndLength-StartLength); maxValue = 100;         minValue = 0;          }

   prevtrendtime = 0;
   price_state[0] = 0;
   price_state[1] = 0;
   ArrayInitialize(prevtime, 0);

   for(int i = 0; i < swamisize; i++)
   {
      trend[i][0] = 0;
      trend[i][1] = 0;
      ema[i][0]               = 0; ema[i][1]               = 0;
      ema[i+swamisize][0]     = 0; ema[i+swamisize][1]     = 0;
      ema[i+2*swamisize][0]   = 0; ema[i+2*swamisize][1]   = 0;
   }

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| OnDeinit                                                         |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   int w = ChartWindowFind(0, short_name);
   if(w >= 0)
      ObjectsDeleteAll(0, w, OBJ_RECTANGLE);
   ChartRedraw(0);
}

//+------------------------------------------------------------------+
//| EMA helper — stateful, keyed by index                            |
//+------------------------------------------------------------------+
double EMA(int index, double p, int per, int bar, const datetime &time[])
{
   if(prevtime[index] != (int)time[bar])
   {
      ema[index][1]   = ema[index][0];
      prevtime[index] = (int)time[bar];
   }

   int rates_total = ArraySize(time);
   if(bar >= rates_total - 2)
      ema[index][0] = p;
   else
      ema[index][0] = ema[index][1] + 1.0 / per * (p - ema[index][1]);

   return ema[index][0];
}

//+------------------------------------------------------------------+
//| Internal RSI calculation                                         |
//+------------------------------------------------------------------+
double _RSI(int index, double change, int shift, const datetime &time[])
{
   double TotChg    = MathAbs(change);
   double NetChgAvg = EMA(index,              change, index + StartLength, shift, time);
   double TotChgAvg = EMA(index + swamisize,  TotChg, index + StartLength, shift, time);

   double ChgRatio  = (TotChgAvg != 0) ? NetChgAvg / TotChgAvg : 0;

   double rsi = EMA(index + 2*swamisize, 2*ChgRatio + 0.5, Smooth, shift, time);

   if(rsi > 1) rsi = 1;
   if(rsi < 0) rsi = 0;

   return rsi;
}

//+------------------------------------------------------------------+
//| Plot one rectangle cell                                          |
//+------------------------------------------------------------------+
void Plot(double value, string name, color clr, double width,
          int bar, int w, const datetime &time[])
{
   if(bar + 1 >= ArraySize(time)) return;

   if(ObjectFind(0, name) < 0)
      ObjectCreate(0, name, OBJ_RECTANGLE, w,
                   time[bar+1], value - 0.5*width,
                   time[bar],   value + 0.5*width);

   ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
}

//+------------------------------------------------------------------+
//| OnCalculate                                                      |
//+------------------------------------------------------------------+
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[])
{
   // Use oldest-first indexing (MQL4 style)
   ArraySetAsSeries(time,          false);
   ArraySetAsSeries(open,          false);
   ArraySetAsSeries(high,          false);
   ArraySetAsSeries(low,           false);
   ArraySetAsSeries(close,         false);
   ArraySetAsSeries(RSIBuffer,     false);
   ArraySetAsSeries(sumRSIBuffer,  false);
   ArraySetAsSeries(loValueBuffer, false);
   ArraySetAsSeries(upValueBuffer, false);

   win = ChartWindowFind(0, short_name);

   int limit;
   if(prev_calculated > 0)
      limit = prev_calculated - 1;
   else
   {
      limit = 0;
      ArrayInitialize(RSIBuffer, EMPTY_VALUE);
      if(win >= 0) ObjectsDeleteAll(0, win, OBJ_RECTANGLE);

      // Reset all EMA state
      ArrayInitialize(prevtime, 0);
      for(int j = 0; j < swamisize*3; j++) { ema[j][0] = 0; ema[j][1] = 0; }
      for(int j = 0; j < swamisize;   j++) { trend[j][0] = 0; trend[j][1] = 0; }
      prevtrendtime  = 0;
      price_state[0] = 0;
      price_state[1] = 0;
   }

   for(int shift = limit; shift < rates_total - 1; shift++)
   {
      if(prevtrendtime != (int)time[shift])
      {
         for(int i = 0; i < swamisize; i++) trend[i][1] = trend[i][0];
         price_state[1] = price_state[0];
         prevtrendtime  = (int)time[shift];
      }

      loValueBuffer[shift] = minValue;
      upValueBuffer[shift] = maxValue;

      // Replicate iMA(NULL,0,1,0,MODE_SMA,Price,shift) — period=1 so it's just the price
      switch(Price)
      {
         case 1:  price_state[0] = open[shift];  break;
         case 2:  price_state[0] = high[shift];  break;
         case 3:  price_state[0] = low[shift];   break;
         case 4:  price_state[0] = (high[shift] + low[shift]) / 2.0; break;
         case 5:  price_state[0] = (high[shift] + low[shift] + close[shift]) / 3.0; break;
         case 6:  price_state[0] = (high[shift] + low[shift] + close[shift]*2) / 4.0; break;
         default: price_state[0] = close[shift]; break;
      }

      double Change   = price_state[0] - price_state[1];
      double swamisum = 0;

      for(int i = 0; i < swamisize; i++)
      {
         double rsi = _RSI(i, Change, shift, time);

         if(VisualMode == 0)
         {
            swamiRSI[i] = rsi;
            swamisum   += rsi;
         }
         else
         {
            trend[i][0] = trend[i][1];
            if(rsi > 0.5) trend[i][0] = 1;
            if(rsi < 0.5) trend[i][0] = 0;
            swamiRSI[i] = trend[i][0];
            swamisum   += trend[i][0];
         }
      }

      if(ScaleMode == 0)
      {
         if(SampleLengthAdj > 0) RSIBuffer[shift]    = StartLength + (swamisize - 1) * swamiRSI[SampleLengthAdj - StartLength];
         sumRSIBuffer[shift] = StartLength + (swamisize - 1) * swamisum / swamisize;
      }
      else
      {
         if(SampleLengthAdj > 0) RSIBuffer[shift]    = 100 * swamiRSI[SampleLengthAdj - StartLength];
         sumRSIBuffer[shift] = 100 * swamisum / swamisize;
      }

      bool drawSwami = (SwamiBars > 0  && shift >= rates_total - 1 - SwamiBars) ||
                       (SwamiBars == 0 && shift <  rates_total - 1);

      if(drawSwami && win >= 0)
      {
         for(int i = 0; i < swamisize; i++)
         {
            int Color1, Color2, Color3;

            if((int)FlatColor < 0)  // CLR_NONE
            {
               Color1 = (int)(dnLineR + swamiRSI[i] * (upLineR - dnLineR));
               Color2 = (int)(dnLineG + swamiRSI[i] * (upLineG - dnLineG));
               Color3 = (int)(dnLineB + swamiRSI[i] * (upLineB - dnLineB));
            }
            else
            {
               if(swamiRSI[i] >= 0.5)
               {
                  Color1 = (int)(upLineR + 2*(1 - swamiRSI[i])*(flLineR - upLineR));
                  Color2 = (int)(upLineG + 2*(1 - swamiRSI[i])*(flLineG - upLineG));
                  Color3 = (int)(upLineB + 2*(1 - swamiRSI[i])*(flLineB - upLineB));
               }
               else
               {
                  Color1 = (int)(dnLineR + 2*swamiRSI[i]*(flLineR - dnLineR));
                  Color2 = (int)(dnLineG + 2*swamiRSI[i]*(flLineG - dnLineG));
                  Color3 = (int)(dnLineB + 2*swamiRSI[i]*(flLineB - dnLineB));
               }
            }

            double value = (ScaleMode == 0) ? (double)(i + StartLength) : (double)i * fuzzWidth;

            string text = UniqueName + "(" + IntegerToString(i + StartLength) + ") "
                        + IntegerToString(win) + " "
                        + TimeToString(time[shift]);

            Plot(value, text, rgbToColor(Color1, Color2, Color3), fuzzWidth, shift, win, time);
         }
      }
   }

   ChartRedraw(0);
   return(rates_total);
}
//+------------------------------------------------------------------+