//+------------------------------------------------------------------+
//|                                                   iPolyCycle.mq4 |
//|                                                                  |
//| conversion from tradestation code                                |
//| original made by Paul A. Griffin                                 |
//| metatrader version mladen                                        |
//+------------------------------------------------------------------+
#property copyright "www.forex-tsd.com"
#property link      "www.forex-tsd.com"

//Modified, 18/oct/2025, by jeanlouie, www.forexfactory.com/jeanlouie
// - leading blank lag period filled in with lower tf values
// - type setting to enumeration, values 0-4

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+

#property indicator_separate_window
#property indicator_buffers 4+4
#property indicator_color1  DeepSkyBlue
#property indicator_color2  PaleVioletRed
#property indicator_color3  Blue
#property indicator_color4  Red
#property indicator_color5  DeepSkyBlue
#property indicator_color6  PaleVioletRed
#property indicator_color7  Blue
#property indicator_color8  Red

#property indicator_style5 STYLE_DOT
#property indicator_style6 STYLE_DOT
#property indicator_style7 STYLE_DOT
#property indicator_style8 STYLE_DOT

//
//
//
//
//

enum enType{
   enType_datafit,   //Data and fit
   enType_analytic,  //Analytic signal
   enType_phase,     //Phase shift
   enType_periods,   //Periods
   enType_rsi,       //Rsi
};

extern ENUM_APPLIED_PRICE Price  = PRICE_MEDIAN;
extern int Length = 17;
extern int Degree = 4;
extern enType Type   = enType_analytic;
extern bool GapFill = true;
extern ENUM_TIMEFRAMES GapLowerTF = PERIOD_M1;

//
//
//
//
//

double value1[];
double value2[];
double value3[];
double value4[];

double value1b[];
double value2b[];
double value3b[];
double value4b[];

//
//
//
//
//

double Polynomial[];
double Coefficient[];
double Cycle[];
double Signal[];
double Hilbert[];
double HilbertTransform[];
double SignalTransform[];

//
//
//
//
//

#define HilbertWidth 7
#define pi           3.141592653589793
int     Width;
int     DataSize;
string ShortName;

#define mtf(b, i) iCustom(_Symbol,GapLowerTF,WindowExpertName(),Price,Length,Degree,Type,b,i)

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

int init()
{
   SetIndexBuffer(0,value1);
   SetIndexBuffer(1,value2);
   SetIndexBuffer(2,value3);
   SetIndexBuffer(3,value4); 

   SetIndexBuffer(0+4,value1b);
   SetIndexBuffer(1+4,value2b);
   SetIndexBuffer(2+4,value3b);
   SetIndexBuffer(3+4,value4b); 

   //
   //
   //
   //
   //
   
   Width    = MathFloor((Length-1.0)/2.0);
   DataSize = 2*Width+1;

      ArrayResize(Coefficient,Degree+1);
      ArrayResize(Polynomial, DataSize*(Degree+1)+1);
      ArrayResize(Cycle,      DataSize);
      ArrayResize(Signal,     DataSize);
      ArrayResize(Hilbert,    DataSize);
      ArrayResize(HilbertTransform, 2*HilbertWidth+1);
      ArrayResize(SignalTransform,  2*HilbertWidth+1);

   	//
   	//
   	//
   	//
   	//
   	
            for (int k = -HilbertWidth; k<=HilbertWidth; k++)
            {
               if (MathMod(k,2.0) == 0)
      	  	         HilbertTransform[HilbertWidth+k] = 0;
               else  HilbertTransform[HilbertWidth+k] = 2.0/(pi*k);		
               if (k == 0)
                     SignalTransform[HilbertWidth+k] = 1;
		          else SignalTransform[HilbertWidth+k] = 0;
            }
      
            //
            //
            //
            //
            //
      
                             for (k=-Width; k<=Width; k++) Polynomial[0*DataSize+Width+k] = 1;
            if (Degree >= 1) for (k=-Width; k<=Width; k++) Polynomial[1*DataSize+Width+k] = k;
 
            if (Degree >  1)
            {
               for (int p=1; p<=Degree; p++)
               for (k=-Width; k<=Width; k++)
               {
                  double temp1 = k*(2.0*p+1.0)/(p+1.0);
                  double temp2 = -(p/(p+1.0))*(2.0*Width+1+p)*(2.0*Width+1-p)/4.0;
      			   Polynomial[(p+1)*DataSize+Width+k] = temp1*Polynomial[p*DataSize+Width+k] + temp2*Polynomial[(p-1)*DataSize+Width+k];
               }
            }
 
            for (p=0; p<=Degree; p++)
            {
               double temp = MathPow(2,-2*p)/(2.0*p+1.0);
                  for (k=-p; k<=p; k++) temp *= (2.0*Width+1.0+k);
                      if (temp>0) temp = 1/MathSqrt(temp);
                  for (k=-Width; k<=Width; k++) Polynomial[p*DataSize+Width+k] = temp * Polynomial[p*DataSize+Width+k];
            }		          
      
      //
      //
      //
      //
      //
   
      SetIndexShift(0,0);
      SetIndexShift(1,0);
      SetIndexShift(2,0);
      SetIndexShift(3,0);
      
      SetIndexShift(0+4,0);
      SetIndexShift(1+4,0);
      SetIndexShift(2+4,0);
      SetIndexShift(3+4,0);
      
      
      //Type = MathMax(MathMin(Type,5),1);
      string addName = "";
         switch(Type)
         {
            case 0 :
                     addName = "Data and fit";
                        IndicatorDigits(6);
                        SetIndexStyle(0,DRAW_LINE,STYLE_SOLID);
                        SetIndexStyle(1,DRAW_LINE,STYLE_DOT);
                        SetIndexStyle(0+4,DRAW_LINE,STYLE_DOT);
                        SetIndexStyle(1+4,DRAW_LINE,STYLE_DOT);
                     break;
                     
            case 1 :
                     addName = "Analytic signal"; IndicatorDigits(6);
                        IndicatorDigits(6);
                        SetIndexStyle(0,DRAW_LINE,STYLE_SOLID); SetIndexShift(0,-HilbertWidth);
                        SetIndexStyle(1,DRAW_LINE,STYLE_SOLID); SetIndexShift(1,-HilbertWidth);
                        SetIndexStyle(0+4,DRAW_LINE,STYLE_DOT); //SetIndexShift(0+4,-HilbertWidth);
                        SetIndexStyle(1+4,DRAW_LINE,STYLE_DOT); //SetIndexShift(1+4,-HilbertWidth);
                     break;
            case 2 :
                     addName = "Phase shift";
                        IndicatorDigits(6);
                        SetIndexStyle(0,DRAW_ARROW); SetIndexArrow(0,159); SetIndexShift(0,-HilbertWidth);
                        SetIndexStyle(1,DRAW_ARROW); SetIndexArrow(1,159); SetIndexShift(1,-HilbertWidth);
                        SetIndexStyle(0+4,DRAW_ARROW); SetIndexArrow(0+4,159); //SetIndexShift(0+4,-HilbertWidth);
                        SetIndexStyle(1+4,DRAW_ARROW); SetIndexArrow(1+4,159); //SetIndexShift(1+4,-HilbertWidth);
                     break;
            case 3 : 
                     addName = "Periods";
                        IndicatorDigits(0);
                        SetIndexStyle(0,DRAW_LINE,STYLE_SOLID);
                        SetIndexStyle(1,DRAW_LINE,STYLE_SOLID);
                        SetIndexStyle(0+4,DRAW_LINE,STYLE_DOT);
                        SetIndexStyle(1+4,DRAW_LINE,STYLE_DOT);
                     break;
            case 4 :
                     addName = "Rsi";
                        IndicatorDigits(6); 
                        SetIndexStyle(0,DRAW_LINE,STYLE_SOLID);
                        SetIndexStyle(0+4,DRAW_LINE,STYLE_DOT);
         }
   
      //
      //
      //
      //
      //
      
   ShortName = addName+" ("+Length+","+Degree+")";
   IndicatorShortName(ShortName);
   return(0);
}
int deinit() { return(0); }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

double work[][2];
#define PhaseShift 0
#define _price     1

//
//
//
//
//

int start()
{
   double radToDegrees = 180.0/pi;
   int i,j,k,p,r,limit,counted_bars=IndicatorCounted();

   if(counted_bars<0) return(-1);
   if(counted_bars>0) counted_bars--;
      limit=MathMin(Bars-counted_bars,Bars-1);
      if (ArrayRange(work,0) != Bars) ArrayResize(work,Bars);

   //
   //
   //
   //
   //

   for(i=limit, r=Bars-i-1; i>=0; i--,r++)
   {
      work[r][_price] = iMA(NULL,0,1,0,MODE_SMA,Price,i);
      value3[i] = EMPTY_VALUE;
      value4[i] = EMPTY_VALUE;
         if (i==(Bars-1))
               int EarliestWidth = - Width;
         else      EarliestWidth =   Width - 2.0*HilbertWidth-1.0;
   
      //
      //
      //
      //
      //
         
         for (p=0; p<=Degree; p++)
         {
            Coefficient[p]=0; for(j=-Width; j<=Width; j++) Coefficient[p] += Polynomial[p*DataSize+Width+j]*work[r-Width+j][_price];
         }
         
         double DC = Coefficient[0]*Polynomial[Width+Width];
	
	     	for (j=EarliestWidth; j<=Width; j++)
	     	{
            Cycle[Width+j] = 0; for (p=1; p<=Degree; p++) Cycle[Width+j] += Coefficient[p]*Polynomial[p*DataSize+Width+j];
         }
	     	for (j=EarliestWidth + HilbertWidth; j<=Width-HilbertWidth; j++)
	     	{
            Signal[Width+j]  = 0;
		      Hilbert[Width+j] = 0; 
            for (k =-HilbertWidth; k<=HilbertWidth; k++)
            {
			      Hilbert[Width+j] += HilbertTransform[HilbertWidth+k] * Cycle[Width+j+k];
			      Signal[Width+j]  += SignalTransform[HilbertWidth+k]  * Cycle[Width+j+k];
            }			      
            
            double temp = MathPow(Signal[Width+j],2)+MathPow(Hilbert[Width+j],2);
            if (temp>0)
            {
			      temp = MathPow(temp,-0.5);
			         Signal[Width+j]  = temp * Signal[Width+j];
			         Hilbert[Width+j] = temp * Hilbert[Width+j];
            }
         }
            
      //
      //
      //
      //
      //

         double T1 =  Signal[2*Width-HilbertWidth] *Signal[2*Width-HilbertWidth-1]+Hilbert[2*Width-HilbertWidth]*Hilbert[2*Width-HilbertWidth-1]; 
         double T2 = -Hilbert[2*Width-HilbertWidth]*Signal[2*Width-HilbertWidth-1]+Signal[2*Width-HilbertWidth]*Hilbert[2*Width-HilbertWidth-1]; 
            
         if (T1 != 0) 
	            work[r][PhaseShift] = MathArctan(T2/T1)*radToDegrees;
         else  work[r][PhaseShift] = work[r-1][PhaseShift];


      //
      //
      //
      //
      //

         temp = 0; for (j=0; (r-j)>=0 && MathAbs(temp) < 360; j++) temp += work[r-j][PhaseShift]; double period = j;
         temp = 0; for (j=0; (r-j)>=0 && MathAbs(temp) < 180; j++) temp += work[r-j][PhaseShift]; double halfPeriod = j;

         double LeadingSignalEdge  = Signal[Width+Width - HilbertWidth];
         double LeadingHilbertEdge = Hilbert[Width+Width - HilbertWidth];
         double LeadingCycleEdge   = Cycle[Width+Width];
            
               
      //
      //
      //
      //
      //
      
         switch(Type)
         {
            case 0 : 
               value1[i] = work[r][_price]-DC;
               value2[i] = LeadingCycleEdge;
               if (i==0)
                  for (j=EarliestWidth; j<=Width; j++)
		            {
                     value3[Width-j] = work[r-Width+j][_price]-DC;
                     value4[Width-j] = Cycle[Width+j];
                  }
               break;
            case 1 :
         		value1[i] = LeadingSignalEdge;
               value2[i] = LeadingHilbertEdge;
               if (i==0)
                  for (j=-Width+HilbertWidth+1; j<=Width-HilbertWidth; j++)
		            {
                     value3[Width-j] = Signal[Width+j];
                     value4[Width-j] = Hilbert[Width+j];
                  }
                  Comment(-Width+HilbertWidth,"  ",Width-HilbertWidth);
               break;
            case 2 :
               value1[i] = EMPTY_VALUE;
               value2[i] = EMPTY_VALUE;
                  if (T2>0) value1[i] = work[r][PhaseShift];
                  if (T2<0) value2[i] = work[r][PhaseShift];
               break;
            case 3 :
               value1[i] = period;
               value2[i] = halfPeriod;
               break;
            
            //
            //
            //
            //
            //
            
            case 4 :
               int count = MathFloor(period+HilbertWidth+1);
      	         double max = work[r][_price];
                  double min = work[r][_price];
                  for (j=1; j<=count; j++)
                     {
                        max = MathMax(max,work[r-j][_price]);
                        min = MathMin(min,work[r-j][_price]);
                     }
         	     if (max!=min) value1[i] = -1 + 2*(work[r][_price]-min)/(max-min); 
         }

                        			         
   }   
   
   if(GapFill && _Period>1 && GapLowerTF<_Period){
      value1b[HilbertWidth] = value1[0];
      value2b[HilbertWidth] = value2[0];
      value3b[HilbertWidth] = value3[0];
      value4b[HilbertWidth] = value4[0];
      
      value1b[HilbertWidth+1] = EMPTY_VALUE;
      value2b[HilbertWidth+1] = EMPTY_VALUE;
      value3b[HilbertWidth+1] = EMPTY_VALUE;
      value4b[HilbertWidth+1] = EMPTY_VALUE;
      
      for(i=HilbertWidth-1; i>=1; i--){
         int x = i==0?HilbertWidth:iBarShift(_Symbol,GapLowerTF,Time[i-1]-60);
         value1b[i] = mtf(0,x);
         value2b[i] = mtf(1,x);
         value3b[i] = mtf(2,x);
         value4b[i] = mtf(3,x);
      }
      
      value1b[0] = mtf(0,HilbertWidth);
      value2b[0] = mtf(1,HilbertWidth);
      value3b[0] = mtf(2,HilbertWidth);
      value4b[0] = mtf(3,HilbertWidth);
      
   }
   
   return(0);
}

