Советник для торговли в канале

Genkov | 10 августа, 2009

Введение

Хочу поделиться опытом с начинающими трейдерами, владеющими основами языка MQL4, вариантом программы, способной помогать трейдеру при торговле в канале. Прежде чем приступать к торговле в канале, необходимо четко представлять себе, по какому принципу построен канал и как будет происходить изменение размера канала и его направление в зависимости от движения цены. Каждая линия канала строится на основе сформировавшихся фракталов в пределах видимости баров на графике.


Полуавтоматический советник трейдера для торговли в канале

Стандартное начало программ здесь я пропускаю. Его можно посмотреть в приложенных файлах.

Для начала определимся отрезками с количеством баров для поиска фракталов по периодам. Здесь же установим величину отступа (space) для "стрелок", которые будут отображаться на графике.

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;
}

Построение канала начинаем с поиска реперных точек (баров), через которые будем проводить линии канала. Для точек, обнаруженных сверху или снизу, введем как примечание на уровне глобальных переменных следующие условности:

Зададим начальные значения неопределенности для реперных точек и положения их на графике в виде:

NB1=-1; NB2=-1; Extrem=0;

Тестирование начинаем с третьего бара (считая "0") по соображениям возможности "формирования фрактала".

TestBar=2;  // номер тестируемого бара

Организуем цикл while для обнаружения совпадения тестируемого бара с максимальным или минимальным значением на выше обозначенных отрезках. Условием цикла будет выражение с отрицательными значениями номеров баров, также номер тестируемого бара должен быть меньше общего количества тестируемых баров.

while(((NB1==-1) || (NB2==-1)) && (TestBar<AllB))

Сначала предположим, что экстремальные точки есть снизу (Low), и протестируем бары по совпадению с минимальными значениями. И если будут найдены 2-е точки, то условия цикла становятся ложными и дальше оператор не выполняется.

Ниже приведен Фрагмент цикла while для нахождения точек снизу графика. По аналогии построен поиск верхних реперных точек.

//начинаем считать с третьего бара (считая "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
// -------------------------------------------------------------------------------------+

Если будет найдена только одна точка, то условия цикла остаются истинными, и оператор продолжит работу с максимальными точками сверху. Если не будут найдены 2 максимальные точки (High), значит, на данный момент нет точек для построения канала.

if((NB1==-1) || (NB2==-1)) // не нашли реперных точек на последних AllB барах

Имея две реперные точки, рассчитываем скорость изменения цены:

RatePr=(Pr2-Pr1)/(NB2-NB1);

Теперь находим первую опорную точку линии канала как проекцию первой реперной точки на "0" бар:

Вторую опорную точку линии канала определяем в левую сторону графика на величину видимости, например: на 50 баров левее второй реперной точки.

double Tk2=Tk1+(NB2+50)*RatePr;

Теперь параллельно найденной линии построим противоположную линию канала:

Для поиска третьей реперной точки будем тестировать бары, находящиеся между двумя найденными реперными точками от NB1 до NB2 (можно и от "0" до NB2, и со 2-го бара до NB2). Тестировать будем по экстремумам противоположного направления найденным точкам. Например, если точки найдены сверху (High)графика, тестируем бары по минимумам (Low). После нахождения третьей реперной точки, в том же фрагменте определяются и две другие опорные точки для противоположной линии канала.

В приведенном ниже фрагменте даны подробные комментарии.

// Паралельно найденной линии построим противоположную линию канала.
// Найдем реперную точку или точку привязки для противоположной линии канала: 
// Примем за начало поиска минимальную цену 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

После того как найдена третья реперная точка, можно нанести эти точки на график, предварительно удалив имеющиеся.

Ниже приведен фрагмент программы для нанесения реперных точек на график.

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");

Результатом работы этого фрагмента на графике должны появиться значки над/под реперными точками, см. ниже.

После определения трех реперных точек и четырех опорных можно нанести линии на график:

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);

Вычислим значения медианы канала и границ канала на последних 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
  }

Если на последних барах сформировался фрактал, нанесем значок на график:

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);

Выше обращено внимание на отдельные особенности и фрагменты программы в части построения самого канала.

Теперь несколько соображений о возможности торговли в этом канале.

Допустим, направление канала восходящее, и текущая свеча, опускаясь вниз, подошла близко к нижней границе канала или пересекла границу. После этого момента возможны следующие варианты движения цены:

Здесь надо сделать оговорку, что, говоря о пересечениях баром нижней границы, нужно иметь в виду минимальное значения бара.

Для открытия позиции BUY, на первый взгляд, подходят первые три варианта движения цены. Рассмотрим их. Надо иметь в виду, что построение коридора (канала) в данном советнике производится на основе сформировавшихся фракталов. Поэтому надо учитывать возможность изменения направления коридора после закрепления фрактала, т.е. на последних трёх барах.

Рассмотрим первый вариант. В этом варианте следующий бар от бара с минимальным значением будет открыт выше минимального значения предыдущего бара. И минимальное значение третьего (по счету слева направо) бара тоже будет выше минимального значения первого бара. Таким образом, бар 3 (по счету справа налево, т.е. от 0 бара) является минимальным значением для формирования фрактала. И если восходящее направление канала не изменилось, можно открывать позицию BUY.

Теперь относительно изменения направления канала. Если канал был построен по реперным точкам снизу, то в данном случае направление канала не изменится, т.к. минимальная точка фрактала окажется выше нижней линии коридора.

Если канал был построен по максимальным реперным точкам сверху, то направление канала тоже не изменится. Вопрос по первому варианту заключается в том, какую величину разницы между нижней границей канала и минимумом образованного фрактала около этой границы считать достаточной. Выбор этой величины в основном зависит от размера канала.

Третий вариант движения цены, как правило, ведёт к изменению величины наклона канала, а иногда и к изменению направления канала т.к. фрактал формируется ниже нижней границы канала. В случае, когда коридор был построен по минимальным значениям первых двух реперных точек, а значение сформировавшегося фрактала оказалось ниже первой опорной точки, но выше второй опорной точки, то произойдет коррекция величины наклона коридора, а направление останется восходящим.

Если же минимальное значение бара сформировавшегося фрактала окажется ниже и второй опорной точки, то произойдет изменение направления коридора на нисходящее. Аналогичные рассуждения применимы относительно позиции SELL, но с обратным "знаком". Ниже приведены примеры из торгов по предлагаемому советнику.

В блоках открытия ордеров и отслеживания позиций привожу пример торгового варианта условий открытия и закрытия позиций, протестированных на демо. Результат тестирования:

Полный текст программы приведен ниже:

//+------------------------------------------------------------------+
//|                                             Expert_Ch_v83_2_.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);
  }
//+------------------------------------------------------------------+


Заключение

Думаю, что вопрос о возможности торговли в канале должен иметь положительный ответ. И надеюсь получить критические замечания для дальнейшего совершенствования советника. Надеюсь также, что мой опыт пригодится не только начинающим трейдерам.