//+------------------------------------------------------------------+
//|                                           Expert_Ch_v83_2_gk.mq4 |
//|                                            2009, author - Genkov |
//|                                                     Genkov@bk.ru |
//+------------------------------------------------------------------+
#property copyright "2009, author - Genkov"
#property link      "Genkov@bk.ru"
//+------------------------------------------------------------------+
extern double SL_B=200;             
extern double TP_B=50;      
extern double SL_S=200; 
extern double TP_S=50;  
extern double Lots=1.0; 
double TrailingStop=40; 
int Magic,i;
extern int AllB=240;        // кол-во баров для обсчета
int TestBar=0;              // индекс тестируемого бара
double RatePr=0;            // скорость изменения цены - pips/bars
int NB1=-1,NB2=-1,NB3,NB5;  // номера баров реперных точек
int Extrem=0;               // индексы Экстремальных точек: 
  /* Extrem = (0)  - фрактал не найден, 
     Extrem = (1)  - фрактал найден cверху, (любое положительное число)
     Extrem = (-1) - фрактал найден снизу   (любое отрицательное число)
  */
double Pr1=0,Pr2=0,Pr3,Pr5, // цены реперных баров с фракталами 
       Tk1,Tk2,Tk3,Tk4,Tk5; // цены опорных точек для построения линий канала 
double space;               // расстояние от цены до "стрелки"
double TLUp_[10],TLDn_[10], // значения граничных линий канала
       Med_[10];
int B_F=0;                  // кол-во баров для фракталов в периоде
datetime Tm1,Tm2,Tm3,Tm5;   // время баров опорных точек
string SH;                  // наклон канала
bool FraktUp=false;         // метка наличия значка фрактала сверху
bool FraktDn=false;         // метка наличия значка фрактала снизу
int q,w;
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
 void DelObj1()
  {
	ObjectDelete("Tr1");
	ObjectDelete("Tr2");
	ObjectDelete("Med");
  }
//+------------------------------------------------------------------+
 void Op_Sell_Ch()
  { 
  if(!OrderSend(Symbol(),OP_SELL,Lots,Bid,2,Ask+SL_S*Point,Bid-TP_S*Point," ",Magic,0,Red))
      { Print("  Ошибка открытия ордера SELL  # ",GetLastError()); }
      return(0);
     }
//+------------------------------------------------------------------+
 void Op_Buy_Ch()
  {
  if(!OrderSend(Symbol(),OP_BUY,Lots,Ask,2,Bid-SL_B*Point,Ask+TP_B*Point," ",Magic,0,Blue))
      { Print("  Ошибка открытия ордера SELL  # ",GetLastError()); }
      return(0);
     }
//+------------------------------------------------------------------+
 void Close_B_lot()
  {
  if(!OrderClose(OrderTicket(),OrderLots(),Bid,2,HotPink)) // закрытие 0.1 лота 
      {Print(" Зак.орд.#= ",OrderTicket(),"Ошибка #= ",GetLastError());
      RefreshRates();}
     }
//+------------------------------------------------------------------+
 void Close_S_lot()
  {
  if(!OrderClose(OrderTicket(),OrderLots(),Ask,2,Aqua)) // закрытие 0.1 лота 
      {Print(" Зак.орд.#= ",OrderTicket(),"Ошибка #= ",GetLastError());
      RefreshRates();}
     }  
// ===================================================================+
   int start()
    {
    int StopLevel=MarketInfo(Symbol(),MODE_STOPLEVEL);
   // кол-во баров для поиска фракталов по периодам    
		switch (Period())
		{
		 case 1:     B_F=12; space=0.0002; break;
		 case 5:     B_F=48; space=0.0003; break;
		 case 15:    B_F=24; space=0.0004; break;
		 case 30:    B_F=24; space=0.0004; break;
		 case 60:    B_F=12; space=0.0007; break;
		 case 240:   B_F=15; space=0.0012; break;
		 case 1440:  B_F=10; space=0.0030; break;
		 case 10080: B_F=6;  space=0.0040; break;
		}
// ====================================================================================+
	// построим канал:
	//начинаем считать с третьего бара (считая "0"), чтобы мог "определиться фрактал"
	TestBar=2;  // номер тестируемого бара
	NB1=-1; NB2=-1; Extrem=0;   // считаем, что номера баров и экстремум не определены 
	while(((NB1==-1) || (NB2==-1)) && (TestBar<AllB))
	{//w1
// ------------------------------------------------------------------------------------+
    //  Если: - при Extrem меньше (1)(возможны два варианта: "0" и "-1"), 
    //  и если индекс наименьшего значения цены совпадает с индексом исследуемого бара
// ------------------------------------------------------------------------------------+   
   if((Extrem<1) && (TestBar==iLowest(Symbol(),Period(),MODE_LOW,B_F*2+1,TestBar-B_F))) 
	 {//w2
	  // при варианте "0", когда на данный момент не было экстремума,
	  if(Extrem==0) 
	   {//w3
	   // присвоим: найденной экстремальной точке значения (-1), отметив таким образом,  
	   // что экстремум найден снизу, номер найденному бару (NB1) и значение цены (Pr1).   
	   Extrem = -1; NB1=TestBar; Pr1=Low[NB1];  
	   }//-w3
     else  if(Extrem!=0) // первая точеа была найдена раньше
		 // иначе значения номера и цены будут присвоены точке 2
		  {//w4
		  NB2=TestBar; Pr2=Low[NB2];   
		  }//-w4
	    // для контроля можно вывести значения - Print("   # бара= ",NB2," знач.цены= ",Pr2); 
		 }//-w2
// -------------------------------------------------------------------------------------+
   //  Если: - при Extrem больше (-1)(возможны: 0 и 1), и индекс наибольшего значения
   //  цены совпадает с номером исследуемого бара  - (по аналогии с выше изложенным)
// -------------------------------------------------------------------------------------+
		if((Extrem>-1) && (TestBar==iHighest(Symbol(),Period(),MODE_HIGH,B_F*2+1,TestBar-B_F))) 
		 {//w5
		  // если Extrem==0 
		  if(Extrem==0) 
		  {//w6
		  // запомним номер бара (NB1) и значение цены (Pr1).
		  Extrem = 1; NB1=TestBar; Pr1=High[NB1];  
		  }//-w6
       else // иначе значения номера и цены будут присвоены точке 2
		  {//w7
		   NB2=TestBar; Pr2=High[NB2];  
		   }//-w7
	     }//-w5
		 TestBar++;
      }//-w1
   // -----------------------------------------------------------------------------------+
	if((NB1==-1) || (NB2==-1)) // не нашли реперных точек на последних AllB барах
	 {
	 DelObj1(); ObjectDelete("Cross1"); ObjectDelete("Cross2");  ObjectDelete("Rep1");
	 ObjectDelete("Rep2"); ObjectDelete("Rep3"); ObjectDelete("Rep5"); 
    // Print(" === > нет баров для канала "); // только для контроля (можно удалить)
	 return(-1); 
	 }
	// ---------------------------------	
	// Расчитаем скорость изменения цены.
	RatePr=(Pr2-Pr1)/(NB2-NB1);
	// если она положительная, то канал нисходящий, иначе восходящий.
	if(RatePr>0) SH="нисходящий"; else  SH="восходящий";
   // определим время реперных точек для линий канала
	Tm1=Time[0];    Tm2=Time[NB2+50]; 
// **************************************************************************************   
   if(Extrem==1) // если первым был найден экстремум сверху 
    {//1(Extrem=1)
    // имея две реперные точки и скорость изменения цены,
    // определим опорные точки этой линии:
    // первая опорная точка - проекция реперной точки на "0" бар
    double Tk1=Pr1-NB1*RatePr;  
    // вторая опорная точка - проекция на левую сторону графика
    double Tk2=Tk1+(NB2+50)*RatePr; 
    // Паралельно найденной линии построим противоположную линию канала.
    // Найдем реперную точку или точку привязки для противоположной линии канала:	
    // Примем за начало поиска минимальную цену 2-ого бара.
	 Tk3=Low[2]-2*RatePr; // проекция 2-го бара на "0" бар	
	 for(i=3;i<=NB2;i++)  // поиск ведем начиная с 3-го бара  
	 {//2(1)Up
	 if(Low[i]<Tk3+i*RatePr) // по минимальному значению проекции цены на "0" баре
	  {//3(1)Up
	  Tk3=Low[i]-i*RatePr; // третья опорная точка (проекция на "0" бар)
	  Pr5=Low[i];          // третья реперная точка для нижней линии канала
	  NB5=i;               // номер бара третьей реперной точки
	  }//-3(1)Up
	 }//-2(1)Up
// теперь посмотрим на первые два бара "0" и "1", их Min могут оказаться ниже 2-го бара
   // если учитывать корректировку построения канала по этим двум барам, то канал будет 
   // расширяться вслед за движением цены вниз, если не учитывать, то движение цены вниз 
   // можно рассматривать как прорыв линии канала (возможное зарождение тренда!)
   // if(Low[0]<Tk3) {Tk3=Low[0]; Pr5=Low[0]; NB5=0;}
   // if(Low[1]<Tk3+RatePr) {Tk3=Tk3+RatePr; Pr5=Low[1]; NB5=1;}
	 datetime TmR1=Time[NB1];    datetime TmR2=Time[NB2];   datetime TmR5=Time[NB5];
// ------------------------------------------------------------------------------------------
// следующие орераторы только для визуального контроля работы программы(их можно удалить)
// отображающие значения наклона канала, скорости, номера и время реперных точек при Extrem=1
    string TNB1=TimeToStr(TmR1,TIME_DATE|TIME_MINUTES);
    string TNB2=TimeToStr(TmR2,TIME_DATE|TIME_MINUTES);
    string TNB5=TimeToStr(TmR5,TIME_DATE|TIME_MINUTES);
 //   Print(" канал- ",SH," ; скорость = ",DoubleToStr(NormalizeDouble(RatePr,8),8),
 //         " pip / bar"," ; Extrem= ",Extrem);
 //   Print(" NB2= ",NB2," ; время= ",TNB2," ; NB5= ",NB5,
 //         " ; время= ",TNB5," ; NB1= ",NB1," ; время= ",TNB1);
// ------------------------------------------------------------------------------------------
   // для наглядности исполнения программы пометим на графике реперные точки на линиях канала
   ObjectDelete("Rep1"); ObjectDelete("Rep2"); ObjectDelete("Rep3"); ObjectDelete("Rep5");
   ObjectCreate("Rep1",OBJ_ARROW, 0, TmR1, Pr1+2*space);
      ObjectSet("Rep1", OBJPROP_COLOR, Yellow);
      ObjectSet("Rep1", OBJPROP_ARROWCODE,72); 
   ObjectCreate("Rep2",OBJ_ARROW, 0, TmR2, Pr2+2*space);
      ObjectSet("Rep2", OBJPROP_COLOR, Yellow);
      ObjectSet("Rep2", OBJPROP_ARROWCODE,72); 
   ObjectCreate("Rep5",OBJ_ARROW, 0, TmR5, Pr5-space);
      ObjectSet("Rep5", OBJPROP_COLOR, Yellow);
      ObjectSet("Rep5", OBJPROP_ARROWCODE,71);
  ObjectDelete("Cross2");
// ------------------------------------------------------------------------------------------
    // расчитаем цены для координат опорных точек нижней границы канала:
    double Tk3=Pr5-RatePr*NB5; // цена нижней границы канала в "0" точке
    double Tk4=Tk3+RatePr*(NB2+50);// цена нижней границы канала в левой стороне графика
   }//- 1(Extrem=1)
// ******************************************************************************************
   else  if(Extrem==-1)// если первым был найден экстремум снизу 
	{//1(Extrem= -1)
	 // цены опорных точек нижней границы канала:
    Tk3=Pr1-NB1*RatePr;      // в "0" точке
    Tk4=Tk3+(NB2+50)*RatePr; // в левой точке 
    // Паралельно найденной линии построим противоположную линию канала.
    // Найдем реперную точку или точку привязки для противоположной линии канала.	
    // Примем за начало поиска цену 2-ого бара.
	 Tk1=High[2]-2*RatePr; // проекция 2-го бара на "0" бар	
	 for(i=3;i<=NB2;i++) // поиск ведем начиная с 3-го бара
	 {//2(-1)
	 if(High[i]>Tk1+i*RatePr) 
	  {//3(-1)
	  Tk1=High[i]-i*RatePr; // третья опорная точка (проекция на "0" бар)
	  Pr3=High[i];          // третья реперная точка для верхней линии канала
	  NB3=i;                // номер бара третьей реперной точки 
	  }//-3(-1)
	  // теперь посмотрим на первые два бара "0" и "1"
	  // if(High[0]>Tk1) {Tk1=High[0]; Pr3=High[0]; NB3=0;}
	  // if(High[1]>Tk1+RatePr) {Tk1=Tk1+RatePr; Pr3=High[1]; NB3=1;}
     TmR1=Time[NB1];    TmR2=Time[NB2];   datetime TmR3=Time[NB3];
	 }//- 2(-1)
// ---------------------------------------------------------------------------------
	// для контроля результатов выполнения введем следующие операторы 
	//(на работу программы они не влияют их можно удалить),
	// отображающие значения наклона (скорости) канала, скорости и номера 
	// и время реперных точек при Extrem = -1
   // TNB1=TimeToStr(TmR1,TIME_DATE|TIME_MINUTES);  
   // TNB2=TimeToStr(TmR2,TIME_DATE|TIME_MINUTES);
   // string TNB3=TimeToStr(TmR3,TIME_DATE|TIME_MINUTES);
   // Print(" канал- ",SH," ; скорость цены= ",DoubleToStr(NormalizeDouble(RatePr,8),8),
   //       " pip / bar"," ; Extrem= ",Extrem);
   // Print(" ; NB2= ",NB2," ; время= ",TNB2," ; NB3= ",NB3,
   //       " ; время= ",TNB3," ; NB1= ",NB1," ; время= ",TNB1);
// ----------------------------------------------------------------------------------
   // для наглядности пометим на графике реперные точки на линиях канала
   ObjectDelete("Rep1"); ObjectDelete("Rep2"); 
   ObjectDelete("Rep3"); ObjectDelete("Rep5");
   ObjectCreate("Rep1",OBJ_ARROW, 0, TmR1, Pr1-space);
      ObjectSet("Rep1", OBJPROP_COLOR, Yellow);
      ObjectSet("Rep1", OBJPROP_ARROWCODE,71); 
   ObjectCreate("Rep2",OBJ_ARROW, 0, TmR2, Pr2-space);
      ObjectSet("Rep2", OBJPROP_COLOR, Yellow);
      ObjectSet("Rep2", OBJPROP_ARROWCODE,71); 
   ObjectCreate("Rep3",OBJ_ARROW, 0, TmR3, Pr3+2*space);
      ObjectSet("Rep3", OBJPROP_COLOR, Yellow);
      ObjectSet("Rep3", OBJPROP_ARROWCODE,72);
 ObjectDelete("Cross1");
// ---------------------------------------------------------------------------------------------    
    // расчитаем цены опорных точек верхней границы канала:
    Tk1=Pr3-RatePr*NB3; // цена верхней границы канала в "0" точке
    Tk2=Tk1+RatePr*(NB2+50);// цена верхней границы канала в левой точке
	}//-1(Extrem= -1)
// ---------------------------------=============================================================
	// вычислим значения медианы канала и границ канала на последних 6-ти барах
   for(int i=0;i<6;i++)
    {
    TLUp_[i]=Tk1+i*RatePr;
    TLDn_[i]=Tk3+i*RatePr;
    Med_[i]=(TLUp_[i]+TLDn_[i])/2;
    }
// --------------------------------------------------------------------
	//Если было пересечение верхней линии канала, ставим звездочку и ставим Звуковой сигнал
	if(Bid>TLUp_[0]) 
	 {
	 bool TrUp=true; //bool TrDn=false; 
	 ObjectDelete("Cross1");  ObjectDelete("Cross2");
	 ObjectCreate("Cross1",OBJ_ARROW, 0, Tm1, High[1]+2*space);
    ObjectSet(	"Cross1", OBJPROP_COLOR, DeepPink);
    ObjectSet(	"Cross1", OBJPROP_ARROWCODE,171);
    PlaySound("alert.wav"); // Файл должен быть расположен в каталог_терминала\sounds
	 }
	//Если было пересечение нижней линии канала, ставим звездочку и ставим Звуковой сигнал
	if(Bid<TLDn_[0]) 
	 {
	 ObjectDelete("Cross2");  ObjectDelete("Cross1");
    /// Print(" было пересечение с нижней линией канала ");
	 ObjectCreate("Cross2",OBJ_ARROW, 0, Tm1, Low[1]-space);
    ObjectSet(	"Cross2", OBJPROP_COLOR, DodgerBlue);
    ObjectSet(	"Cross2", OBJPROP_ARROWCODE,171);
    PlaySound("alert.wav"); // Файл должен быть расположен в каталог_терминала\sounds 
	 }
// --------------------------------------------------------------------------------------- 
	// нанесем на график границы канала, предварительно удалив ранее имеющиеся
	DelObj1();  
   ObjectCreate("Tr1",OBJ_TREND,0,Tm2,Tk2,Tm1,Tk1);
		ObjectSet("Tr1",OBJPROP_COLOR,Lime); 
		ObjectSet("Tr1",OBJPROP_WIDTH,1); //2
		ObjectSet("Tr1",OBJPROP_STYLE,STYLE_SOLID); 
	ObjectCreate("Tr2",OBJ_TREND,0,Tm2,Tk4,Tm1,Tk3); 
		ObjectSet("Tr2",OBJPROP_COLOR,Lime); 
		ObjectSet("Tr2",OBJPROP_WIDTH,1); //2
		ObjectSet("Tr2",OBJPROP_STYLE,STYLE_SOLID); 
	ObjectCreate("Med",OBJ_TREND,0,Tm2,(Tk2+Tk4)/2,Tm1,(Tk1+Tk3)/2);
		ObjectSet("Med",OBJPROP_COLOR,Lime); 
		ObjectSet("Med",OBJPROP_WIDTH,1); 
		ObjectSet("Med",OBJPROP_STYLE,STYLE_DOT); 
// ---- Блок нанесения значков фрактала на график ---------------------------------
   if((High[2]>High[1]&&Bid<High[2]&&High[2]>High[3]&&High[2]>High[4]) || 
      (High[2]==High[1]&&Bid<High[1]&&High[2]>High[3]&&High[2]>High[4]))
    {
     double FraktalUp=High[2]; // фрактал сверху
     double FraktalDn=0;
     // если сформировался фрактал - нанесем значек, и если этот фрактал стал причиной
     // новой реперной точкой - удалим значек пересечения ценой верхней линии квартала.
     if(High[2]>=TLUp_[i]) ObjectDelete("Cross1");
     ObjectDelete("Fraktal"+(q-1));  //ObjectDelete("Frakt"+(w-1));
     ObjectCreate("Fraktal"+q,OBJ_ARROW, 0, Time[2], High[2]+2*space+0.0002);
        ObjectSet("Fraktal"+q, OBJPROP_COLOR, Orchid);
        ObjectSet("Fraktal"+q, OBJPROP_ARROWCODE,217);
        bool FraktUp=true; //bool FraktDn=false;// пригодится при торгах
        q++;
       }
   if((Low[2]<Low[1]&&Bid>Low[2]&&Low[2]<Low[3]&&Low[2]<Low[4]) ||
      (Low[2]==Low[1]&&Bid>Low[1]&&Low[2]<Low[3]&&Low[2]<Low[4]))
    {
     FraktalDn=Low[2]; // фрактал снизу 
     FraktalUp=0;
     if(Low[2]>=TLUp_[i]) ObjectDelete("Cross2");
     ObjectDelete("Frakt"+(w-1)); //ObjectDelete("Fraktal"+(q-1));
     ObjectCreate("Frakt"+w,OBJ_ARROW, 0, Time[2], Low[2]-2*space);
        ObjectSet("Frakt"+w, OBJPROP_COLOR, Orchid);
        ObjectSet("Frakt"+w, OBJPROP_ARROWCODE,218);
        FraktDn=true; FraktUp=false;
        w++;
       }
// ---------------------------------------------------------------+
// Блок условий открытия позиций.   Здесь приведен только пример  +
// для наглядности, и не является рекомендацией для использования!+          
// ---------------------------------------------------------------+
  if(OrdersTotal()<1) // играем в одну позицию  
   {
   // - 16- SELL ---
 	  if(Extrem==1 &&        // две реперные точки сверху
 	   RatePr>0 &&            // канал нисходящий
 	   (Tk1-Tk3)>20*Point &&  // размер канала > 20pip 
 	   Bid<High[1] &&          // цена ниже предыдущего бара
 	   (TLUp_[1]-High[1])<3*Point) // 1-ый бар в пределах 3pip от верхней линии канала
 	   {
 	   Print(" Open - 16-SELL === "); 
   //   SL_S=50;
   //   if(SL_S<StopLevel) SL_S=StopLevel;
   //   TP_S=80; 
		Op_Sell_Ch();
		return(0);
 	   }         
     // - 18- BUY ---
 	  if(Extrem==-1 &&        // две реперные точки снизу
 	   RatePr<0 &&            // канал восходящий
 	   (Tk1-Tk3)>20*Point &&  // размер канала > 20pip 
 	   Bid>Low[1] &&          // цена выше предыдущего бара
 	   (Low[1]-TLDn_[1])<3*Point) // 1-ый бар в пределах 3pip от нижней линии канала
 	   {
 	   Print(" Open - 18-BUY === "); 
  //    SL_B=50;
  //    if(SL_B<StopLevel) SL_B=StopLevel;
  //    TP_B=80; 
		Op_Buy_Ch();
		return(0);
 	   }         
    }
//------------------------------------------------------------------------+
// Отслеживание  открытых позиций                   SELL                  |
//+=======================================================================+
  for (i=OrdersTotal()-1; i>=0; i--)        // цикл выбора ордеров  SELL
   {//1-цикл выбора поз.
  if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
    {Print("Ошибка выбора ордера = ", GetLastError()); }
   if(OrderType()==OP_SELL )                // если открыт ордер в продажу
    { //2-тип_Sell
    if( (FraktalDn<=TLDn_[2] || 
       Low[2]<=TLDn_[2]) && 
       (Bid>Low[1] && Low[1]<=TLDn_[1]) && 
       (OrderOpenPrice()-Bid)*Point>0)          // не в убытке! 
      {//5
      Print(" закроем по нижней линии канала ");
      Close_S_lot();
      // и если канал восходящий 
      if(RatePr<0)
       {
       Print(" Откроем поз на покупку ");
       Op_Buy_Ch();
      }
     }//-5
    }//-2 тип_Sell
//------------------------------------------------------------------------+
// Отслеживание  открытых позиций                   BUY                   |
//+=======================================================================+
 else 
   if(OrderType()==OP_BUY )            // если открыт ордер в покупку
    { //4-тип_Buy
    if( (FraktalUp>=TLUp_[2] || 
       High[2]>=TLUp_[2]) && 
  // Если канал сформирован по нижним реперным точкам и восходящий, а цена пересекла верхнюю границу,  
  // и начинает идти вниз, "1" бар был выше "0" и выше "2", который находился на верхней линии канала, 
  // а это похоже на образование верхнего фрактала и увеличение размера канала. 
  // В тоже время Stochastic находится выше уровня 80.0,
  // и начинает опускаться вниз, для пересечения сигнальной линии. Значит не стоит ждать завершения 
  // формирования "0" бара т.к. с болщей вероятностью цена пойдет вниз. Поэтому закрываем позицию BUY.
      (Bid<High[1] && High[1]>=TLUp_[1]) && 
      (Ask-OrderOpenPrice())*Point>0)          // не в убытке!
     {//5
     Print(" закроем по верхней линии канала ");
     Close_B_lot();
     // и если канал нисходящий 
     if(RatePr>0)
      {
      Print(" Откроем поз на продажу ");
      Op_Sell_Ch();
      }
     }//-5
    }//-4-тип_Buy
   }//-1 цикл выбора поз.
//----------------
   return(0);
  }
//+------------------------------------------------------------------+