//+------------------------------------------------------------------+
//|                                               AdpvCyberCycle.mq4 |
//|                        Copyright 2014, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2014, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
#property strict
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   2
//--- plot AdpvCycle
#property indicator_label1  "AdpvCycle"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrLime
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- plot AdpvTrigger
#property indicator_label2  "AdpvTrigger"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrRed
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1

//--- indicator buffers
#define Price(s) ((High[s]+Low[s])/2.0)
#define CLV(s) ((Close[s]-Low[s])-(High[s]-Close[s]))

input double   InpAlpha       = 0.07;
input bool     UseCLV          = true;
input bool     alertsOn        = true;            // Turn alerts on?
input bool     alertsOnCurrent = false;           // Alerts on still opened bar?
input bool     alertsMessage   = true;            // Alerts should display message?
input bool     alertsSound     = false;           // Alerts should play a sound?
input bool     alertsNotify    = false;           // Alerts should send a notification?
input bool     alertsEmail     = false;           // Alerts should send an email?
input string   soundFile       = "alert2.wav";    // Sound file

double AdpCycle[],AdpTrigger[],trend[];
double Smooth[], Cycle[], Q1[], I1[], DeltaPhase[], InstPeriod[], cPeriod[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   IndicatorDigits(8);
   IndicatorBuffers(3);
   ArraySetAsSeries(cPeriod,true);
   ArraySetAsSeries(Smooth,true);
   ArraySetAsSeries(Cycle,true);
   SetIndexBuffer(0,AdpCycle);
   SetIndexBuffer(1,AdpTrigger);
   SetIndexBuffer(2,trend);
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
   PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0.0);
   
//---
   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[])
{
//---
  long tickCnt[1];
  int s;
  int ticks=CopyTickVolume(Symbol(), 0, 0, 1, tickCnt);
  if(ticks!=1) return(rates_total);
  double DC, MedianDelta;
  
  Comment(tickCnt[0]);
  
  if(prev_calculated==0 || tickCnt[0]==1)
   {
    //--- last counted bar will be recounted
    int nLimit=rates_total-prev_calculated-1; // start index for calculations
    
    ArrayResize(cPeriod,Bars(_Symbol,_Period));
    ArrayResize(Smooth,Bars(_Symbol,_Period));
    ArrayResize(Cycle,Bars(_Symbol,_Period));
    ArrayResize(DeltaPhase,Bars(_Symbol,_Period));
    ArrayResize(InstPeriod,Bars(_Symbol,_Period));
    ArrayResize(Q1,Bars(_Symbol,_Period));
    ArrayResize(I1,Bars(_Symbol,_Period));
    
    //if (nLimit>rates_total-7) nLimit=rates_total-7;   
    
    for(s=nLimit-7; s>=0 && !IsStopped(); s--) 
    {
      Smooth[s] = (Price(s)+2*Price(s+1)+2*Price(s+2)+Price(s+3))/6.0;
      if(UseCLV) 
      {
        Cycle[s] = ((1.0-0.5*InpAlpha) * (1.0-0.5*InpAlpha) * (Smooth[s]-2.0*Smooth[s+1]+Smooth[s+2])
                  +2.0*(1.0-InpAlpha)*Cycle[s+1]-(1.0-InpAlpha)*(1.0-InpAlpha)*Cycle[s+2]) + CLV(s);//
      }
      else 
      {
        Cycle[s] = ((1.0-0.5*InpAlpha) * (1.0-0.5*InpAlpha) * (Smooth[s]-2.0*Smooth[s+1]+Smooth[s+2])
                  +2.0*(1.0-InpAlpha)*Cycle[s+1]-(1.0-InpAlpha)*(1.0-InpAlpha)*Cycle[s+2]);//
      }
      if (s > rates_total - 7) { Cycle[s]=(Price(s)-2.0*Price(s+1)+Price(s+2))/4.0; }
        Q1[s] = (0.0962*Cycle[s]+0.5769*Cycle[s+2]-0.5769*Cycle[s+4]-0.0962*Cycle[s+6])*(0.5+0.08*InstPeriod[s+1]);
        I1[s] = Cycle[s+3];
      
      if (Q1[s]!=0.0 && Q1[s+1]!=0.0) DeltaPhase[s] = (I1[s]/Q1[s]-I1[s+1]/Q1[s+1])/(1.0+I1[s]*I1[s+1]/(Q1[s]*Q1[s+1]));
      if (DeltaPhase[s] < 0.1)        DeltaPhase[s] = 0.1;
      if (DeltaPhase[s] > 0.9)        DeltaPhase[s] = 0.9;
      
      MedianDelta = Median(DeltaPhase, 5);//, s
      
      if (MedianDelta == 0.0) 
           DC = 15.0;
      else DC = (6.28318/MedianDelta) + 0.5;
      
      InstPeriod[s] = 0.33 * DC + 0.67 * InstPeriod[s+1];
      cPeriod[s]    = 0.15 * InstPeriod[s] + 0.85 * cPeriod[s+1];
      //+------------------------------------------------------------------+
      double Alpha = cPeriod[s];
      Alpha=2.0/(Alpha+1);
      if(UseCLV) 
      {
        AdpCycle[s]=((1.0 - 0.5 * Alpha) * (1.0 - 0.5 * Alpha) * (Smooth[s] - 2.0 * Smooth[s+1] + Smooth[s+2]) + 2.0 * 
                     (1.0 - Alpha) * AdpCycle[s+1] - (1.0 - Alpha) * (1.0 - Alpha) * AdpCycle[s+2]) + CLV(s);//
      }
      else 
      {
        AdpCycle[s]=((1.0 - 0.5 * Alpha) * (1.0 - 0.5 * Alpha) * (Smooth[s] - 2.0 * Smooth[s+1] + Smooth[s+2])+ 2.0 * 
                     (1.0 - Alpha) * AdpCycle[s+1] - (1.0 - Alpha) * (1.0 - Alpha)* AdpCycle[s+2]);//
      }
      AdpTrigger[s]=AdpCycle[s+1];
      trend[s] = (s<rates_total-1) ? (AdpCycle[s]>AdpTrigger[s]) ? 1 : (AdpCycle[s]<AdpTrigger[s]) ? -1 : trend[s+1] : 0;  
    }
  }
  if (alertsOn)
  {
     int whichBar = 1; if (alertsOnCurrent) whichBar = 0; 
     if (trend[whichBar] != trend[whichBar+1])
     {
        if (trend[whichBar] == 1) doAlert(" crossed up");
        if (trend[whichBar] ==-1) doAlert(" crossed down");       
     }         
   } 
  return(rates_total);
}
//+------------------------------------------------------------------+
double Median(double& arr[], int m_len) {//, int idx
  double MedianArr[];
  int copied;
  double result = 0.0;
  
  ArraySetAsSeries(MedianArr, true);
  ArrayResize(MedianArr, m_len);
  
  copied = ArrayCopy(MedianArr, arr, 0, 0, m_len);//, idx
  if (copied == m_len) {
    ArraySort(MedianArr);
    if (m_len %2 == 0) 
      result = (MedianArr[m_len/2] + MedianArr[(m_len/2)+1])/2.0;
    else
      result = MedianArr[m_len / 2];
  }
  else
    Print(__FILE__+__FUNCTION__+"median error - wrong number of elements copied."); 
  return result; 
}
//+------------------------------------------------------------------+

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

void doAlert(string doWhat)
{
   static string   previousAlert="nothing";
   static datetime previousTime;
   string message;
   
      if (previousAlert != doWhat || previousTime != Time[0]) {
          previousAlert  = doWhat;
          previousTime   = Time[0];

          //
          //
          //
          //
          //

          message = timeFrameToString(_Period)+" "+_Symbol+" at "+TimeToStr(TimeLocal(),TIME_SECONDS)+" AdpvCyberCycle "+doWhat;
             if (alertsMessage) Alert(message);
             if (alertsNotify)  SendNotification(message);
             if (alertsEmail)   SendMail(_Symbol+" AdpvCyberCycle ",message);
             if (alertsSound)   PlaySound(soundFile);
      }
}

//
//
//
//
//

string sTfTable[] = {"M1","M5","M15","M30","H1","H4","D1","W1","MN"};
int    iTfTable[] = {1,5,15,30,60,240,1440,10080,43200};

string timeFrameToString(int tf)
{
   for (int i=ArraySize(iTfTable)-1; i>=0; i--) 
         if (tf==iTfTable[i]) return(sTfTable[i]);
                              return("");
}

