#property copyright "www,forex-station.com"
#property link      "www,forex-station.com"

#property indicator_separate_window
#property indicator_buffers 1
#property indicator_color1 Gold
 
//---- input parameters
extern string    ____0____   = "Period of average";
extern int       MAPeriod   = 50;
extern string    ____1____   = "Type of average: SMA - 0, EMA - 1, SMMA - 2, LWMA - 3";
extern int       MaType      = 1;
extern string    ____2____   = "Type of price:cl=0,op=1,hi=2,lo=3,med=4";
extern int       PriceType   = 4;
extern string    ____3____   = "Moving Average Shift";
extern int       MaShift     = 0;
extern string    ____4____   = "Show oversold and overbought levels";
extern bool      ShowLevels  = true;
extern string    ____5____   = "Lines color (60%)";
extern color     LinesColor1 = Lime;
extern string    ____6____   = "Lines color (100%)";
extern color     LinesColor2 = Lime;
extern string    ____7____   = "Lines style: solid - 0, dashed - 1, dotted - 2";
extern int       LinesStyle  = 2;
extern double    Smooth      = 5;
extern string    _               = "alerts settings";
extern bool      alertsOn        = true;
extern bool      alertsOnCurrent = true;
extern bool      alertsMessage   = true;
extern bool      alertsSound     = false;
extern bool      alertsNotify    = false;
extern bool      alertsEmail     = false;
extern string    soundFile       = "alert2.wav";

//---- buffers
double ExtMapBuffer1[],trend[];

int init()
{
   IndicatorBuffers(2);
   SetIndexBuffer(0,ExtMapBuffer1); SetIndexStyle(0,DRAW_LINE);
   SetIndexBuffer(1,trend);
   string Short="MA Distance ("+MAPeriod+", "+MaType+", "+PriceType+", "+MaShift+")";
   IndicatorShortName(Short);
   SetIndexLabel(0,Short);
   
   SetLevelValue(0, 0.0);

   SetIndexDrawBegin(0,MAPeriod);
   return(0);
}

int deinit()
{
   // cleaning the mess
   if(ShowLevels == true)
   {
      ObjectDelete("dev1");
      ObjectDelete("dev2");
   
      ObjectDelete("dev3");
      ObjectDelete("dev4");
   }
   return(0);
}

int start()
{
   //main core    
   int i, counted_bars=IndicatorCounted();

   if(Bars<=MAPeriod) return(0);

   if(counted_bars<1)
      for(i=1;i<=MAPeriod;i++) ExtMapBuffer1[Bars-i]=0.0;

   i=Bars-MAPeriod-1;
   if(counted_bars>=MAPeriod) i=Bars-counted_bars-1;
   while(i>=0)
   {
      ExtMapBuffer1[i] = iSsm(priceSwitch(i) - iMA(NULL, 0, MAPeriod, MaShift, MaType, PriceType, i),Smooth,i);
      trend[i] = (i<Bars-1) ? (ExtMapBuffer1[i]>0) ? 1 : (ExtMapBuffer1[i]<0) ? -1 : trend[i+1] : 0;
      i--;
   }
   
   // Showing oversold/overbought levels
   if(ShowLevels == true)
   {
      int window = WindowFind("MA Distance ("+MAPeriod+", "+MaType+", "+PriceType+", "+MaShift+")");
      if(window == -1)
      {
         window = 1;
      }
      // counting stddev
      ArraySetAsSeries(ExtMapBuffer1, true);
      double dev = iStdDevOnArray(ExtMapBuffer1, Bars-counted_bars-1, 100,0,0,0);
      // lines creation
      ObjectCreate("dev1", OBJ_HLINE, window, TimeCurrent(), 1.2*dev);
      ObjectSetText("dev1", "+60%");
      ObjectSet("dev1", OBJPROP_COLOR, LinesColor1);
      ObjectSet("dev1", OBJPROP_STYLE, LinesStyle);
      
      ObjectCreate("dev2", OBJ_HLINE, window, TimeCurrent(), -1.8*dev); 
      ObjectSetText("dev2", "-60%");
      ObjectSet("dev2", OBJPROP_COLOR, LinesColor1);
      ObjectSet("dev2", OBJPROP_STYLE, LinesStyle);
      
      ObjectCreate("dev3", OBJ_HLINE, window, TimeCurrent(), 2*dev);
      ObjectSetText("dev3", "+100%");
      ObjectSet("dev3", OBJPROP_COLOR, LinesColor2);
      ObjectSet("dev3", OBJPROP_STYLE, LinesStyle);
      
      ObjectCreate("dev4", OBJ_HLINE, window, TimeCurrent(), -3*dev);  
      ObjectSetText("dev4", "-100%");
      ObjectSet("dev4", OBJPROP_COLOR, LinesColor2);
      ObjectSet("dev4", OBJPROP_STYLE, LinesStyle);      
   }
   if (alertsOn)
   {
     int whichBar = 1; if (alertsOnCurrent) whichBar = 0; 
     if (trend[whichBar] != trend[whichBar+1])
     if (trend[whichBar] == 1)
           doAlert("crossed zero up");
     else  doAlert("crossed zero down");       
   }
   return(0);
}

double priceSwitch(int i)
{
   double price;
   switch(PriceType)
   {
      case PRICE_CLOSE:
         price = Close[i];
         break;
      case PRICE_OPEN:
         price = Open[i];
         break;
      case PRICE_HIGH:
         price = High[i];
         break;
      case PRICE_LOW:
         price = Low[i];
         break;
      case PRICE_MEDIAN:
         price = (High[i]+Low[i])/2;
         break;
         
      default :
         price = Close[i];
   }
   return(price);
}

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

double workSsm[][2];
#define _price  0
#define _ssm    1

double workSsmCoeffs[][4];
#define _period 0
#define _c1     1
#define _c2     2
#define _c3     3
#define Pi 3.14159265358979323846264338327950288

//
//
//
//
//

double iSsm(double price, double period, int i, int instanceNo=0)
{
   if (period<=1) return(price);
   if (ArrayRange(workSsm,0) !=Bars)                 ArrayResize(workSsm,Bars);
   if (ArrayRange(workSsmCoeffs,0) < (instanceNo+1)) ArrayResize(workSsmCoeffs,instanceNo+1);
   if (workSsmCoeffs[instanceNo][_period] != period)
   {
      workSsmCoeffs[instanceNo][_period] = period;
      double a1 = MathExp(-1.414*Pi/period);
      double b1 = 2.0*a1*MathCos(1.414*Pi/period);
         workSsmCoeffs[instanceNo][_c2] = b1;
         workSsmCoeffs[instanceNo][_c3] = -a1*a1;
         workSsmCoeffs[instanceNo][_c1] = 1.0 - workSsmCoeffs[instanceNo][_c2] - workSsmCoeffs[instanceNo][_c3];
   }

   //
   //
   //
   //
   //

      i = Bars-i-1;
      int s = instanceNo*2;   
          workSsm[i][s+_price] = price;
          workSsm[i][s+_ssm]   = workSsmCoeffs[instanceNo][_c1]*(workSsm[i][s+_price]+workSsm[i-1][s+_price])/2.0 + 
                                 workSsmCoeffs[instanceNo][_c2]*workSsm[i-1][s+_ssm]                              + 
                                 workSsmCoeffs[instanceNo][_c3]*workSsm[i-2][s+_ssm];
   return(workSsm[i][s+_ssm]);
}

//
//
//

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)+" 3 ma cross "+doWhat;
          if (alertsMessage) Alert(message);
          if (alertsNotify)  SendNotification(message);
          if (alertsEmail)   SendMail(_Symbol+" ma distance smoothed ",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("");
}



