//+------------------------------------------------------------------+
//|                                                  funnels+mtf.mq4 |
//|                                                           Miyagi |
//|                                                        2012Jan05 |
//+------------------------------------------------------------------+
//#include <DEBUG.mqh>
//2012Jan09 modified / post #7
//2012Jan10 modified / post #10
//2012Jan17 modified / post #19
//2012Jan19 modified / post #24 / mtf
//2012Jan22 modified / post #28 / bug fix

#property copyright "Miyagi"
#property link      "http://www.forexfactory.com/showthread.php?t=332920"

//---- indicator settings
#property indicator_chart_window
#property indicator_buffers 4

#property indicator_color1 FireBrick
#property indicator_color2 MediumBlue
#property indicator_color3 Gray
#property indicator_color4 Gray

#property indicator_width1 2
#property indicator_width2 2
#property indicator_width3 2
#property indicator_width4 2

//---- input parameters
extern string  Settings_1_____________________ = "*** Funnel mouth";
extern int     Max_Range            = 0;
extern color   Color_Range          = ForestGreen;
extern bool    ShowPrice_Range      = true;
extern int     LineWidth_Range      = 2;
extern int     LineLength_Range     = 1;
extern string  Font_Range           = "Arial Black";
extern int     FontSize_Range       = 10;
extern color   Color_Median         = DarkSlateGray;
extern int     LineWidth_Median     = 1;
extern color   Color_FocalPoint     = WhiteSmoke;
extern int     LineWidth_FocalPoint = 1;
extern color   Color_Fibs           = ForestGreen;
extern int     LineWidth_Fibs       = 1;

extern string  Settings_2_____________________ = "*** Funnel high/low points";
extern color   Color_P1             = ForestGreen;
extern color   Color_P2             = GreenYellow;
extern color   Color_P3             = Yellow;
extern color   Color_P4             = CLR_NONE;
extern color   Color_P5             = CLR_NONE;
extern color   Color_P6             = CLR_NONE;
extern int     Width_Points         = 2;

extern string  Settings_3_____________________ = "*** Guide";
extern bool    Show_Guide           = true;
extern bool    ShowPrice_Guide      = true;
extern int     Shift_Guide          = 9;
extern int     LineWidth_Guide      = 2;
extern int     LineLength_Guide     = 2;

extern string  Settings_4_____________________ = "*** Mtf and other settings";
extern int     TimeFrame            = 0;
extern int     DaysBack             = 30;
extern bool    Show_Fractals        = true;
extern bool    Alert_Popup          = true;

//---- indicator buffers
double   L_high[], L_low[], F_upper[], F_lower[];

int      N, Tf, Wp;
color    Col[7];
double   Pmd, Rng;
datetime Time_0, Time_a, Time_l;
string   Prefix = "funnels+mtf";

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
{
   SetIndexBuffer(0, L_high)  ; SetIndexLabel(0, NULL);
   SetIndexBuffer(1, L_low)   ; SetIndexLabel(1, NULL);
   SetIndexBuffer(2, F_upper) ; SetIndexLabel(2, NULL);
   SetIndexBuffer(3, F_lower) ; SetIndexLabel(3, NULL);

   SetIndexStyle (0, DRAW_LINE) ;
   SetIndexStyle (1, DRAW_LINE) ;
   SetIndexStyle (2, DRAW_ARROW); SetIndexArrow (2, 159);
   SetIndexStyle (3, DRAW_ARROW); SetIndexArrow (3, 159);
   
   //set variables
   if (Digits == 5 || Digits == 3) Pmd = Point * 10.0; else Pmd = Point;
   if (Max_Range <= 0) Rng = 1000 * Pmd; else Rng = Max_Range * Pmd;
   if (TimeFrame <= 0) Tf = Period(); else Tf = TimeFrame;
   if (DaysBack  <= 0) Time_0 = iTime(NULL, Tf, iBars(NULL, Tf) - 1); else Time_0 = MathMax(iTime(NULL, Tf, iBars(NULL, Tf) - 1), iTime(NULL, PERIOD_D1, DaysBack));
   
   Col[0] = Color_Median; Col[1] = Color_P1; Col[2] = Color_P2; Col[3] = Color_P3; Col[4] = Color_P4; Col[5] = Color_P5; Col[6] = Color_P6;
   for (N = 2; N <= 6; N++) if (Col[N] < 0) { N--; break; }
   Wp = 2 + Width_Points * 2;

   Time_a = iTime(NULL, Tf, 1);
   Time_l = Time_0;
//----
return(0);
}

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
int deinit()
{
   DeleteObjects(Prefix);

//----
return(0);
}

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
{
   int i, h, l, n, limit, pos_last;
   double price;
   string msg;
   
   int             pos_high  [7],   pos_low  [7];
   double          price_high[7],   price_low[7];
   static double s_price_high[7], s_price_low[7];
   
   if (Time_0 >= iTime(NULL, Tf, 0)) return; else limit = iBarShift(NULL, Tf, Time_0);
   Time_0 = iTime(NULL, Tf, 0);

   //main loop
   for (i = limit; i > 0; i--) {

      if (Show_Fractals) DrawFractals(Tf, i, F_upper, F_lower);

      price_high[0] = MathMin(iOpen(NULL, Tf, i - 1) + Rng, iHigh(NULL, Tf, i));
      price_low [0] = MathMax(iOpen(NULL, Tf, i - 1) - Rng, iLow (NULL, Tf, i));

      price_high[N] = 0; pos_high[0] = i; 
      price_low [N] = 0; pos_low [0] = i;
      
      pos_last = iBarShift(NULL, Tf, Time_l);
      h = i + 1;
      l = i + 1;

      for(n = 1; n <= N; n++) {
         price = GetNextPeak(Tf, MODE_UPPER, h, pos_last);
         if(price != 0 && price >= price_high[n - 1]) {
            price_high[n] = price;
            pos_high[n] = h;
            h += 2;
         }
         else break;
         
         price = GetNextPeak(Tf, MODE_LOWER, l, pos_last);
         if(price != 0 && price <= price_low[n - 1]) {
            price_low[n] = price;
            pos_low[n] = l;
            l += 2;
         }
         else break;
      }
   
   //screen
      if (price_low[N] == 0 || price_high[1] - price_low[1] > Rng * 2.0 || pos_high[1] > pos_low[N] || pos_low[1] > pos_high[N]) continue;
      
   //draw
      DrawPoints(Tf, N, pos_high, price_high, Col, Wp);
      DrawPoints(Tf, N, pos_low , price_low , Col, Wp);
      DrawLines (Tf, N, pos_high, price_high, L_high);
      DrawLines (Tf, N, pos_low , price_low , L_low);
      DrawIntersection(Tf, pos_high[2], price_high[2], pos_high[1], price_high[1], pos_low[2], price_low[2], pos_low[1], price_low[1], Color_FocalPoint, LineWidth_FocalPoint);
   //range for the latest
      DrawRange   (Tf, i, price_high[1], price_low[1], Color_Range, CLR_NONE, false, LineLength_Range, 1, 1, FontSize_Range, Font_Range, STYLE_DOT);
      DrawFibRange(Tf, i, N, price_high, price_low, Color_Fibs, 1, STYLE_DOT);
   //range for older ones
      if (s_price_low[1] != 0) {
      DrawRange   (Tf, pos_last, s_price_high[1], s_price_low[1], Color_Range, Color_Median, ShowPrice_Range, LineLength_Range, LineWidth_Range, LineWidth_Median, FontSize_Range, Font_Range);
      DrawFibRange(Tf, pos_last, N, s_price_high, s_price_low, Color_Fibs, LineWidth_Fibs); }
      
   //store info
      ArrayCopy(s_price_high, price_high);
      ArrayCopy(s_price_low , price_low );
      s_price_high[0] = (price_high[1] + price_low[1]) / 2.0;
      s_price_low [0] = s_price_high[0];
      Time_l = iTime(NULL, Tf, i);

   //alert
      if(Alert_Popup && Time_l > Time_a && i == 1) { Time_a = Time_l;
         msg = WindowExpertName() + ": " + Symbol() + " {" + DoubleToStr((price_high[1] - price_low[1]) / Pmd, 0) + "p}";
         PlaySound("alert.wav");
         Alert(msg); }
   }   
   //main loop end

   //guide
   if (Show_Guide) DrawGuide(N, Shift_Guide, s_price_high, s_price_low, Col, ShowPrice_Guide, LineLength_Guide, LineWidth_Guide);

//----DEBUG();
return(0);
}

//+------------------------------------------------------------------+
double GetNextPeak(int timeframe, int mode, int& pos, int posmax)
{
   double f;
   
   while (pos < posmax) {
      if (mode == MODE_LOWER) {
         if (iLow(NULL, timeframe, pos + 1) >= iLow(NULL, timeframe, pos) && iLow(NULL, timeframe, pos) <= iLow(NULL, timeframe, pos - 1)) {
            f = iLow(NULL, timeframe, pos); }
         else f = 0;
      }
      else
      if (mode == MODE_UPPER) {
         if (iHigh(NULL, timeframe, pos + 1) <= iHigh(NULL, timeframe, pos) && iHigh(NULL, timeframe, pos) >= iHigh(NULL, timeframe, pos - 1)) {
            f = iHigh(NULL, timeframe, pos); }
         else f = 0;
      }
      if (f != 0) break; else pos++;
   }
return(f);
}
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
void DrawIntersection(int timeframe, int ax, double ay, int bx, double by, int cx, double cy, int dx, double dy, int clr, int width = 1, int style = STYLE_DOT, bool back = false)
{
   if (clr < 0) return;

   string obj;
   double price;
   datetime time_ax, time_bx, time_cx, time_dx, time_cor, time;

   time_ax = iTime(NULL, timeframe, ax);
   time_bx = iTime(NULL, timeframe, bx);
   time_cx = iTime(NULL, timeframe, cx);
   time_dx = iTime(NULL, timeframe, dx);

   CalcIntersection(time_ax, ay, time_bx, by, time_cx, cy, time_dx, dy, time, price);
      
   if (price == 0) return; else time_cor = iTime(NULL, timeframe, iBarShift(NULL, timeframe, time));
   
      obj = Prefix + "@" + time_bx + "_is#1";
      DrawLine(obj, time_bx, by, time_cor, price, clr, style, width);

      obj = Prefix + "@" + time_dx + "_is#2";
      DrawLine(obj, time_dx, dy, time_cor, price, clr, style, width);
}
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
void DrawRange(int timeframe, int x1, double y1, double y2, int clr, int clr2, bool showprice, int ln, int widthHL = 1, int widthMD = 1, int size = 10, string font = "Arial Black", int style = STYLE_SOLID, bool back = false, bool ray = false)
{
   string obj, text, description;
   datetime time_x1, time_x2, time_text;

   time_x1 = iTime(NULL, timeframe, x1);
   time_x2 = time_x1 + Period() * 60 * ln;
   time_text = time_x1 + Period() * 60 * (5 * ln);
   text = DoubleToStr((y1 - y2) / Pmd, 0) + "p";

   if (showprice) description = "%$";
   
   //high & low
   obj = Prefix + "@" + time_x1 + "_rangeHL";
   DrawFibLines(obj, time_x1, y1, time_x2, y2, clr, widthHL, style, description, back, ray);

   //median    
   obj = Prefix + "@" + time_x1 + "_rangeMD";
   DrawFibLines(obj, time_x1, (y1 + y2) / 2.0, time_x2, (y1 + y2) / 2.0, clr2, widthMD, style, description, back, ray);
   
   //text
   obj = Prefix + "@" + time_x1 + "_text";
   DrawText(obj, time_text, y2, text, clr, size, font);
}
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
void DrawFibRange(int timeframe, int x1, int n, double y1[], double y2[], int clr, int width = 1, int style = STYLE_SOLID, bool back = false, bool ray = false)
{
   if (clr < 0 || n < 3) return;

   int i, cnt;
   double level[4];
   string obj;
   datetime time_x1;
   
   time_x1 = iTime(NULL, timeframe, x1);
   
   //high
   cnt = CountFibs(n, y1, level);
   if (cnt > 0) {
      obj = Prefix + "@" + time_x1 + "_rfibH";
      DrawFib(obj, time_x1, y1[n], time_x1, y1[1], cnt, clr, width, style);
      for(i = 0; i < cnt; i++) SetFib (obj, i, level[i], DoubleToStr(level[i] * 100.0, 1)); }
   
   //low
   cnt = CountFibs(n, y2, level);
   if (cnt > 0) {
      obj = Prefix + "@" + time_x1 + "_rfibL";
      DrawFib(obj, time_x1, y2[n], time_x1, y2[1], cnt, clr, width, style);
      for(i = 0; i < cnt; i++) SetFib (obj, i, level[i], DoubleToStr(level[i] * 100.0, 1)); }
}
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
void DrawGuide(int n, int shift, double y1[], double y2[], int clr[], bool showprice, int ln, int width = 1, int style = STYLE_DOT, bool back = false, bool ray = false)
{
   int i;
   string obj, description;
   datetime time_x1, time_x2;

   time_x1 = Time[0] + Period() * 60 * shift;
   time_x2 = time_x1 + Period() * 60 * ln;
         
   if (showprice) description = "%$";
   
   for(i = 0; i <= n; i++) {
      obj = Prefix + "_guide#" + i;
      DrawFibLines(obj, time_x1, y1[i], time_x2, y2[i], clr[i], width, style, description, back, ray); }
}
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
void CalcIntersection(datetime ax, double ay, datetime bx, double by, datetime cx, double cy, datetime dx, double dy, datetime& x, double& y)
{
   datetime abx, acx, cdx;
   double aby, acy, cdy, ep_cdab, ep_cdac;
   
   abx = bx - ax; aby = by - ay;
   acx = cx - ax; acy = cy - ay;
   cdx = dx - cx; cdy = dy - cy;
      
   ep_cdab = (cdx * aby) - (cdy * abx);
   ep_cdac = (cdx * acy) - (cdy * acx);
     
   if (ep_cdab == 0) return; else {
      x = ax + abx * ep_cdac / ep_cdab;
      y = ay + aby * ep_cdac / ep_cdab; }
}
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
int CountFibs(int n, double price[], double& level[])
{
   int i, j, cnt;
   double fiblevel, range, diff;
   double fibs[4] = {0.236, 0.382, 0.500, 0.618};   
   
   range = price[n] - price[1];
   diff = MathAbs(range * 0.025);
   ArrayInitialize(level, 0.0);
   
   for(i = 0; i < 4; i++) {
      fiblevel = price[1] + range * fibs[i];
      for(j = cnt + 2; j < n; j++) if(MathAbs(price[j] - fiblevel) <= diff) {
         level[cnt] = fibs[i];
         cnt++;
         break; }
   }
return(cnt);
}
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
void DrawFractals(int timeframe, int pos, double& buffer_u[], double& buffer_l[])
{
   int i, pos_cur;
   double frac_upper, frac_lower;
   
   //adjust timeframe
   if(timeframe < Period()) { pos = iBarShift(NULL, 0, iTime(NULL, timeframe, pos), true);
      if (pos == -1) return; else timeframe = Period(); }
   
   for(i = pos + 2; i >= pos; i--) {
      pos_cur = iBarShift(NULL, 0, iTime(NULL, timeframe, i));
      frac_upper = iFractals(NULL, timeframe, MODE_UPPER, i);
      frac_lower = iFractals(NULL, timeframe, MODE_LOWER, i);
      if (frac_upper > 0) buffer_u[pos_cur] = frac_upper; else buffer_u[pos_cur] = EMPTY_VALUE;
      if (frac_lower > 0) buffer_l[pos_cur] = frac_lower; else buffer_l[pos_cur] = EMPTY_VALUE;
   }
}
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
void DrawPoints(int timeframe, int n, int x1[], double y1[], int clr[], int width = 5, bool back = false)
{
   int i;
   string obj;
   datetime time_x1;
   
   for(i = 1; i <= n; i++) {  
      time_x1 = iTime(NULL, timeframe, x1[i]);
      obj = Prefix + "@" + time_x1 + "_price:" + y1[i];     
      DrawPoint(obj, time_x1, y1[i], clr[i], width);
   }
}
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
void DrawLines(int timeframe, int n, int x[], double y[], double& buffer[])
{
   int i, x1_cur, x2_cur;
   
   for(i = 1; i <= n; i++) {
      x1_cur = iBarShift(NULL, 0, iTime(NULL, timeframe, x[i]));
      x2_cur = iBarShift(NULL, 0, iTime(NULL, timeframe, x[i + 1]));
      DrawBufferLine(x1_cur, y[i], x2_cur, y[i + 1], buffer);
   }
}
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
void DrawFibLines(string obj, datetime x1, double y1, datetime x2, double y2, int clr, int width = 1, int style = STYLE_SOLID, string description = "%$", bool back = false, bool ray = false)
{
   DrawFib(obj, x1, y1, x2, y2, 2, clr, width, style, back, ray);
   SetFib (obj, 0, 0.0, description);
   SetFib (obj, 1, 1.0, description);
}
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
void DrawText(string obj, datetime x1, double y1, string text, int clr, int size = 10, string font = "Arial Black", bool back = false)
{
   if(clr >= 0) {
      if(ObjectFind(obj) >= 0)
         ObjectMove(obj, 0, x1, y1);
      else ObjectCreate(obj, OBJ_TEXT, 0, x1, y1);
      ObjectSet(obj, OBJPROP_BACK, back);
      ObjectSetText(obj, text, size, font, clr);
   }
}
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
void DrawPoint(string obj, datetime x1, double y1, int clr, int width = 5, bool back = false)
{   
   if(clr >= 0) {
      if(ObjectFind(obj) >= 0) {
         ObjectMove(obj, 0, x1, y1);
         ObjectMove(obj, 1, x1, y1); }
      else ObjectCreate(obj, OBJ_TREND, 0, x1, y1, x1, y1);
      ObjectSet(obj, OBJPROP_COLOR, clr);
      ObjectSet(obj, OBJPROP_WIDTH, width);
      ObjectSet(obj, OBJPROP_RAY,   false);
      ObjectSet(obj, OBJPROP_BACK,  back);
   }
}
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
void DrawLine(string obj, datetime x1, double y1, datetime x2, double y2, int clr, int style = STYLE_SOLID, int width = 5, bool back = false)
{   
   if(clr >= 0) {
      if(ObjectFind(obj) >= 0) {
         ObjectMove(obj, 0, x1, y1);
         ObjectMove(obj, 1, x2, y2); }
      else ObjectCreate(obj, OBJ_TREND, 0, x1, y1, x2, y2);
      ObjectSet(obj, OBJPROP_COLOR, clr);
      ObjectSet(obj, OBJPROP_WIDTH, width);
      ObjectSet(obj, OBJPROP_STYLE, style);
      ObjectSet(obj, OBJPROP_RAY,   false);
      ObjectSet(obj, OBJPROP_BACK,  back);
   }
}
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
void DrawBufferLine(int x1, double y1, int x2, double y2, double& buffer[])
{
   int i;
   double slope;
   
   if (x1 == x2) return; else slope = (y2 - y1) / (x2 - x1);
   for (i = x1; i <= x2; i++) buffer[i] = y1 + slope * (i - x1);
}
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
void DrawFib(string obj, datetime x1, double y1, datetime x2, double y2, int n, int clr, int width = 1, int style = STYLE_SOLID, bool back = false, bool ray = false)
{
   if(clr >= 0) {
      if(ObjectFind(obj) >= 0) {
         ObjectMove(obj, 0, x1, y1);
         ObjectMove(obj, 1, x2, y2); }
      else ObjectCreate(obj, OBJ_FIBO, 0, x1, y1, x2, y2);
      ObjectSet(obj, OBJPROP_COLOR, CLR_NONE);
      ObjectSet(obj, OBJPROP_LEVELCOLOR, clr); 
      ObjectSet(obj, OBJPROP_LEVELSTYLE, style);
      ObjectSet(obj, OBJPROP_LEVELWIDTH, width);
      ObjectSet(obj, OBJPROP_BACK, back);
      ObjectSet(obj, OBJPROP_RAY, ray);
      ObjectSet(obj, OBJPROP_FIBOLEVELS, n);
   }
}
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
void SetFib(string obj, int level, double value, string description = "%$")
{
   ObjectSet(obj, OBJPROP_FIRSTLEVEL + level, value);
   ObjectSetFiboDescription(obj, level, description);
}
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
void DeleteObjects(string obj)
{
   int i;
   
   for(i = ObjectsTotal(); i >= 0; i--) {
      if(StringFind(ObjectName(i), obj, 0) > -1) ObjectDelete(ObjectName(i));
   }
}
//+------------------------------------------------------------------+

