//+------------------------------------------------------------------+
//|                                                    SSRC 1.01.mq5 |
//|                                              andrijmix@gmail.com |
//|                                           https://t.me/andrijmix |
//+------------------------------------------------------------------+
#property copyright "andrijmix@gmail.com"
#property link "https://t.me/andrijmix"
#property version "1.00"
#property indicator_separate_window
#property indicator_minimum - 1.3
#property indicator_maximum 1.3
#property indicator_buffers 1
#property indicator_plots 1
//--- plot Line
#property indicator_label1 "Line"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrLime
#property indicator_style1 STYLE_SOLID
#property indicator_width1 1

#property indicator_level1 0.9
#property indicator_level2 - 0.9
#property indicator_level3 0.75
#property indicator_level4 - 0.75

//---- input parameters
//---- Snake
input int maxBar = 10000; // Max bar, less - be faster
input int SnakeRange = 3;
input int FilterPeriod = 21;
input double MartFiltr = 2;
input ENUM_APPLIED_PRICE PriceConst = PRICE_WEIGHTED; // Price type

//---- buffers
double SRCBuffer[];
double Axis[];
double Mart[];
//---- SpearmanRankCorrelation
int rangeN = 14; //   = 30 maximum
double R2[];
double multiply;
int PriceInt[];
int SortInt[];

int h_ma;
double bufferMA[];
int bars = 0;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
    Print("Start");
    h_ma = iMA(NULL, 0, SnakeRange + 1, 0, MODE_LWMA, PriceConst);
    //--- indicator buffers mapping
    SetIndexBuffer(0, SRCBuffer);

     ArrayResize(R2, rangeN);
     ArrayResize(PriceInt, rangeN);
     ArrayResize(SortInt, rangeN);
    multiply = MathPow(10, _Digits);

    ArraySetAsSeries(SRCBuffer, true);
    ArraySetAsSeries(bufferMA, true);
    ArraySetAsSeries(Axis, true);
    ArraySetAsSeries(Mart, true);
    ArraySetAsSeries(R2, true);
    ArraySetAsSeries(PriceInt, true);
    ArraySetAsSeries(SortInt, true);
    IndicatorSetInteger(INDICATOR_DIGITS, _Digits);

    //---
    return (INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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[])
{
    ArraySetAsSeries(time, true);
//--- check if all data calculated 
   if(BarsCalculated(h_ma)<rates_total) return(0); 
//--- we can copy not all data 
   int to_copy; 
   if(prev_calculated>rates_total || prev_calculated<=0) to_copy=rates_total; 
   else 
     { 
      to_copy=rates_total-prev_calculated; 
      //--- last value is always copied 
      to_copy++; 
     } 
//--- try to copy 
   if(CopyBuffer(h_ma,0,0,to_copy,bufferMA)<=0) return(0); 
//--- return value of prev_calculated for next call 

    bars = maxBar <= 0 ? Bars(_Symbol, 0) : maxBar;


     if (rates_total - prev_calculated < 1)
         return (prev_calculated);
        
        

    int i, k, limit = 0, limit2 = 0, limit3 = 0;
    int counted_bars = rates_total-prev_calculated;

    //CopyBuffer(h_ma, 0, 0, bars, bufferMA);

    if (rangeN > 30)
        return (-1);


    limit =counted_bars + (rangeN + FilterPeriod + SnakeRange + 4);
        limit2 = counted_bars + (SnakeRange + 2);
        limit3 = counted_bars + (FilterPeriod + SnakeRange + 3);   

    ArrayResize(Axis, limit2+100);
    ArrayResize(Mart, limit3+100);

     for (int j = 10; j >= 0; j--)
       Print(time[j] + " MA " + DoubleToString(bufferMA[j], _Digits));

    for (i = limit3; i >= 0; i--)
   //   for (i = 1; i < limit3; i++)
      MartAxis(i);
//    for (int j = 10; j >= 0; j--)
//        Print(time[j] + " Axis " + DoubleToString(Axis[j], _Digits));
              
   //for (i = limit2; i >= 0; i--)
     for (i = 0; i < limit2; i++)
        SmoothOverMart(i);

//            Print("=======MART=========");
//    for (int jj = 10; jj >= 0; jj--)
//        Print(time[jj]  + " Mart " + DoubleToString(Mart[jj], _Digits));
 
  for (i = 0; i < limit; i++)
  /// for(i=limit; i>=0; i--)
    {
        if(ArraySize(SRCBuffer)<=i)break;
        for (k = 0; k < rangeN; k++)
        {

            PriceInt[k] =(int)(Mart[i + k] * multiply);
        }
        RankPrices(PriceInt);

        SRCBuffer[i] = SpearmanRankCorrelation(R2, rangeN);

        if (SRCBuffer[i] > 1.0)
            SRCBuffer[i] = 1.0;
        if (SRCBuffer[i] < -1.0)
            SRCBuffer[i] = -1.0;
    }
      //        SRCBuffer[0] = SRCBuffer[1];

    return (rates_total);
}
//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
{
    //---
}
//+------------------------------------------------------------------+

void MartAxis(int Pos)
{
    if (Pos >= ArraySize(bufferMA))
        return;
    int SnakeWeight, i, w, ww, Shift;
    double SnakeSum;

    Axis[Pos] = bufferMA[Pos];
    Axis[Pos] = NormalizeDouble(Axis[Pos],_Digits);

//for (Shift = Pos; Shift < Pos + SnakeRange + 2; Shift++)
  for(Shift = Pos + SnakeRange + 2; Shift > Pos; Shift--)
     {
      SnakeSum = 0.0;
      SnakeWeight = 0;
      i = 0;
      w = Shift + SnakeRange;
      ww = Shift - SnakeRange;
      if(ww < Pos)
         ww = Pos;
      while(w >= Shift)
        {
         i++;
         SnakeSum = SnakeSum + i * SnakePrice(w);
         SnakeWeight = SnakeWeight + i;
         w--;
        }
      while(w >= ww)
        {
         i--;
         SnakeSum = SnakeSum + i * SnakePrice(w);
         SnakeWeight = SnakeWeight + i;
         w--;
        }
      Axis[Shift] = SnakeSum / SnakeWeight;
     }
   return;
  }
//----
double SnakePrice(int Shift)
{
    double close =NormalizeDouble(iClose(NULL, 0, Shift),_Digits);
    double open =NormalizeDouble(iOpen(NULL, 0, Shift),_Digits);
    double high =NormalizeDouble(iHigh(NULL, 0, Shift),_Digits);
    double low =NormalizeDouble(iLow(NULL, 0, Shift),_Digits);
    double median =NormalizeDouble(((high + low) / 2),_Digits);
    double typical =NormalizeDouble(((close + high + low) / 3),_Digits);
    double weighted =NormalizeDouble(((close + high + low + open) / 4),_Digits);

    
    switch (PriceConst)
    {
    case PRICE_CLOSE:
        return close;
    case PRICE_OPEN:
        return open;
    case PRICE_HIGH:
        return high;
    case PRICE_LOW:
        return low;
    case PRICE_MEDIAN:
        return (high + low) / 2;
    case PRICE_TYPICAL:
        return (close + high + low) / 3;
    case PRICE_WEIGHTED:
        return (2*close + high + low) / 4;
    default:
        return (iClose(NULL, 0, Shift));
    }
}

//+------------------------------------------------------------------+
void SmoothOverMart(int Shift)
{
    double t, b;
    t = Axis[ArrayMaximum(Axis,Shift, FilterPeriod)];
    b = Axis[ArrayMinimum(Axis, Shift,FilterPeriod)];
    Mart[Shift] = (2 * (2 + MartFiltr) * Axis[Shift] - (t + b)) / 2 / (1 + MartFiltr);

    return;
}
//+------------------------------------------------------------------+
double SpearmanRankCorrelation(double &Ranks[], int N)
{
    double res, z2 = 0;
    for (int i = 0; i < N; i++)
    {
        z2 += MathPow(Ranks[i] - i - 1, 2);
    }
    res = 1 - 6 * z2 / (MathPow(N, 3) - N);
    return (res);
}
//+------------------------------------------------------------------+
void RankPrices(int &InitialArray[])
{
    double dcounter, averageRank;
    int i, k, m, dublicat, counter, etalon;
    double TrueRanks[];
    ArrayResize(TrueRanks, rangeN);
    ArrayCopy(SortInt, InitialArray);
    for (i = 0; i < rangeN; i++)
        TrueRanks[i] = i + 1;
    ArraySetAsSeries(SortInt, true);
    ArraySort(SortInt);
    for (i = 0; i < rangeN - 1; i++)
    {
        if (SortInt[i] != SortInt[i + 1])
            continue;
        dublicat = SortInt[i];
        k = i + 1;
        counter = 1;
        averageRank = i + 1;
        while (k < rangeN)
        {
            if (SortInt[k] == dublicat)
            {
                counter++;
                averageRank += k + 1;
                k++;
            }
            else
                break;
        }
        dcounter = counter;
        averageRank = averageRank / dcounter;
        for (m = i; m < k; m++)
            TrueRanks[m] = averageRank;
        i = k;
    }
    for (i = 0; i < rangeN; i++)
    {
        etalon = InitialArray[i];
        k = 0;
        while (k < rangeN)
        {
            if (etalon == SortInt[k])
            {
                R2[i] = TrueRanks[k];
                break;
            }
            k++;
        }
    }
    return;
}


int IndicatorCountedMQL4(int prev_calculated)
{
    if (prev_calculated > 0)
        return (prev_calculated - 1);
    if (prev_calculated == 0)
        return (0);
    return (0);
}