Утилита для отбора и навигации на MQL5 и MQL4: повышаем информативность графиков

24 апреля 2019, 16:12
Roman Klymenko
3
1 495

Введение

В предыдущих статьях (Утилита для отбора и навигации на MQL5 и MQL4: добавляем автоматичекий поиск паттернов с показом найденных символов, Утилита для отбора и навигации на MQL5 и MQL4: добавляем вкладки "домашки" и сохраняем графические объекты, Пишем утилиту для отбора и навигации по инструментам на языках MQL5 и MQL4) на данную тему мы уже сделали довольно много. Но все равно этого недостаточно для качественной внутридневной торговли. Сегодня мы добавим новый функционал, который позволит найти качественные точки входа в сделку или точки выхода из нее.

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


Скрыть символы, у которых тренд сегодня отличается от вчерашнего тренда

Входящий параметр: Скрыть, если тренд сегодня != вчера
Область: Настройки фильтрации

Многие гуру трейдинга рекомендуют для внутридневной торговли выбирать только такие акции, по которым тренд сегодняшнего дня совпадает с трендом вчерашнего. Если говорить проще, то если цена акции вчера росла, то и сегодня она должна расти. И, соответственно, торговать в этот день можно только в Long. И наоборот.

Для фильтрации подобных символов используется следующая функция:

/*
Возвращает true, если тренд сегодня не равен тренду вчера
symname - имя символа, для которого проверять
*/
bool check_no_trend(string symname){
   MqlRates rates2[];
   ArraySetAsSeries(rates2, true);
   if(CopyRates(symname, PERIOD_D1, 0, 2, rates2)==2){
      if( rates2[0].close>rates2[0].open && rates2[1].close>rates2[1].open ){
         return false;
      }
      if( rates2[0].close<rates2[0].open && rates2[1].close<rates2[1].open ){
         return false;
      }
   }
   return true;
}


Показывать начало сессии

Входящий параметр: Показать начало дня (на периодах до дня)
Область: Настройки графиков

Начало торговой сессии, это время, когда открываются рынки акций, если мы говорим об акциях. Например, 16:30 для американского фондового рынка, 10:00 для европейского и российского. Если же речь идет о рынке Forex, то за начало торговой сессии мы возьмем полночь.

На низких таймфреймах, например, M5, сразу бывает трудно определить начало текущей сессии. Например, чтобы сразу визуально видеть, растет акция или падает сегодня. Поэтому давайте добавим настройку, которая позволит нам сразу видеть, с какого бара начался текущий торговый день по инструменту.

Функция для отображения начала сессии:

/*
Отображает на графике начало дня
currChart - id графика
day - номер дня (0 - текущий )
*/
void showNdays(long currChart, int day){
   MqlRates rates[];
   ArraySetAsSeries(rates, true);
   if(CopyRates(ChartSymbol(currChart), PERIOD_D1, day, 1, rates)>0){
      ObjectCreate(currChart, exprefix+"_Nday", OBJ_VLINE, 0, rates[0].time, 0);
      ObjectSetInteger(currChart,exprefix+"_Nday",OBJPROP_STYLE,STYLE_DOT);
      ObjectSetInteger(currChart,exprefix+"_Nday",OBJPROP_RAY,true); 
      ObjectSetInteger(currChart,exprefix+"_Nday",OBJPROP_SELECTABLE,false); 
      ObjectSetInteger(currChart,exprefix+"_Nday",OBJPROP_BACK,true); 
   }
}

На графике это выглядит как вертикальная красная линия:

Показывать начало сессии

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

Показывать разделители периодов


Отображать Low и High вчерашнего и позавчерашнего дня

Входящий параметр: Показывать low и high вчерашнего дня и Показывать low и high позавчерашнего дня
Область: Настройки графиков

При работе внутри дня High и Low вчерашнего и позавчерашнего дня являются очень популярными уровнями поддержки и сопротивления. Ситуация, когда цена падает, ударяется в High вчерашнего/позавчерашнего дня и отскакивает, происходит очень часто. По собственным наблюдениям могу сказать, что цена в 70 и более процентах случаев с первого раза не пробивает High/Low как вчерашнего, так и позавчерашнего дня.

Поэтому пренебрегать данными уровнями поддержки и сопротивления никогда не стоит.

Для отображения Low и High выбранного дня используется следующая функция:

/*
Отобразить на графике Low и High указанного дня
currChart - id графика
day - номер отображаемого дня
*/
void showHOBeforeDay(long currChart, int day=1){
   MqlRates rates[];
   ArraySetAsSeries(rates, true);
   
   color curColor=clrAquamarine;
   switch(day){
      case 2:
         curColor=clrOlive;
         break;
   }
   
   if(CopyRates(ChartSymbol(currChart), PERIOD_D1, day, 1, rates)>0){
      ObjectCreate(currChart, exprefix+"_beforeday_high_line"+(string) day, OBJ_HLINE, 0, 0, rates[0].high);
      ObjectSetInteger(currChart,exprefix+"_beforeday_high_line"+(string) day,OBJPROP_COLOR,curColor); 
      ObjectSetInteger(currChart,exprefix+"_beforeday_high_line"+(string) day,OBJPROP_SELECTABLE,false); 
      ObjectSetInteger(currChart,exprefix+"_beforeday_high_line"+(string) day,OBJPROP_BACK,true); 
      
      ObjectCreate(currChart, exprefix+"_beforeday_low_line"+(string) day, OBJ_HLINE, 0, 0, rates[0].low);
      ObjectSetInteger(currChart,exprefix+"_beforeday_low_line"+(string) day,OBJPROP_COLOR,curColor); 
      ObjectSetInteger(currChart,exprefix+"_beforeday_low_line"+(string) day,OBJPROP_SELECTABLE,false); 
      ObjectSetInteger(currChart,exprefix+"_beforeday_low_line"+(string) day,OBJPROP_BACK,true); 
   }
}

На графике Low и High вчерашнего дня отображаются светлыми горизонтальными линиями, а позавчерашнего - линиями болотного цвета:

Показывать low и high вчерашнего/позавчерашнего дня


Показывать ближайшие круглые цены

Входящий параметр: Показывать ближайшие круглые цены (период < 1day)
Область: Настройки графиков

Еще одними природными уровнями поддержки/сопротивления считаются круглые цены. То есть, цены инструментов, которые заканчиваются на .00 или .50. Многие трейдеры часто ищут проторговки только возле подобных круглых уровней.

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

Для отображения круглых цен используется следующая функция:

/*
Отобразить на графике 3 ближайших круглых уровня к текущей цене.
currChart - id графика
*/
void showRoundPrice(long currChart){
   string name=ChartSymbol(currChart);
   int tmpPrice;
   
   MqlRates rates[];
   ArraySetAsSeries(rates, true);
   if(CopyRates(name, PERIOD_D1, 0, 1, rates)==1){
      switch((int) SymbolInfoInteger(name, SYMBOL_DIGITS)){
         case 0:
            break;
         case 2:
         case 3:
         case 5:
            tmpPrice=(int) rates[0].close;

            ObjectCreate(currChart, exprefix+"_round_line_00_0", OBJ_HLINE, 0, 0, (double) tmpPrice-1 );
            ObjectSetInteger(currChart,exprefix+"_round_line_00_0",OBJPROP_COLOR,clrCadetBlue); 
            ObjectSetInteger(currChart,exprefix+"_round_line_00_0",OBJPROP_STYLE, STYLE_DASH); 
            ObjectSetInteger(currChart,exprefix+"_round_line_00_0",OBJPROP_SELECTABLE,false); 
            ObjectSetInteger(currChart,exprefix+"_round_line_00_0",OBJPROP_BACK,true); 

            ObjectCreate(currChart, exprefix+"_round_line_05_0", OBJ_HLINE, 0, 0, (double) tmpPrice-0.5 );
            ObjectSetInteger(currChart,exprefix+"_round_line_05_0",OBJPROP_COLOR,clrCadetBlue); 
            ObjectSetInteger(currChart,exprefix+"_round_line_05_0",OBJPROP_STYLE, STYLE_DASH); 
            ObjectSetInteger(currChart,exprefix+"_round_line_05_0",OBJPROP_SELECTABLE,false); 
            ObjectSetInteger(currChart,exprefix+"_round_line_05_0",OBJPROP_BACK,true); 
            
            ObjectCreate(currChart, exprefix+"_round_line_00_1", OBJ_HLINE, 0, 0, (double) tmpPrice );
            ObjectSetInteger(currChart,exprefix+"_round_line_00_1",OBJPROP_COLOR,clrCadetBlue); 
            ObjectSetInteger(currChart,exprefix+"_round_line_00_1",OBJPROP_STYLE, STYLE_DASH); 
            ObjectSetInteger(currChart,exprefix+"_round_line_00_1",OBJPROP_SELECTABLE,false); 
            ObjectSetInteger(currChart,exprefix+"_round_line_00_1",OBJPROP_BACK,true); 

            ObjectCreate(currChart, exprefix+"_round_line_05_1", OBJ_HLINE, 0, 0, (double) tmpPrice+0.5 );
            ObjectSetInteger(currChart,exprefix+"_round_line_05_1",OBJPROP_COLOR,clrCadetBlue); 
            ObjectSetInteger(currChart,exprefix+"_round_line_05_1",OBJPROP_STYLE, STYLE_DASH); 
            ObjectSetInteger(currChart,exprefix+"_round_line_05_1",OBJPROP_SELECTABLE,false); 
            ObjectSetInteger(currChart,exprefix+"_round_line_05_1",OBJPROP_BACK,true); 

            ObjectCreate(currChart, exprefix+"_round_line_00_2", OBJ_HLINE, 0, 0, (double) tmpPrice+1 );
            ObjectSetInteger(currChart,exprefix+"_round_line_00_2",OBJPROP_COLOR,clrCadetBlue); 
            ObjectSetInteger(currChart,exprefix+"_round_line_00_2",OBJPROP_STYLE, STYLE_DASH); 
            ObjectSetInteger(currChart,exprefix+"_round_line_00_2",OBJPROP_SELECTABLE,false); 
            ObjectSetInteger(currChart,exprefix+"_round_line_00_2",OBJPROP_BACK,true); 

            ObjectCreate(currChart, exprefix+"_round_line_05_2", OBJ_HLINE, 0, 0, (double) tmpPrice+1.5 );
            ObjectSetInteger(currChart,exprefix+"_round_line_05_2",OBJPROP_COLOR,clrCadetBlue); 
            ObjectSetInteger(currChart,exprefix+"_round_line_05_2",OBJPROP_STYLE, STYLE_DASH); 
            ObjectSetInteger(currChart,exprefix+"_round_line_05_2",OBJPROP_SELECTABLE,false); 
            ObjectSetInteger(currChart,exprefix+"_round_line_05_2",OBJPROP_BACK,true); 
            
            break;
      }
   }
}

На графике круглые уровни показываются пунктирными горизонтальными линиями:

Показывать ближайшие круглые цены


Показывать закрытие часа

Входящий параметр: Показывать закрытие часа (период < 1hour)
Область: Настройки графиков

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

Функция для выделения закрытий часа на графике:

/*
Показать на графике закрытие 4 последних часов
currChart - id графика
*/
void showCloseHour(long currChart){
   MqlDateTime tmpTime;
   int tmpOffset=0;
   MqlRates rates[];
   ArraySetAsSeries(rates, true);
   if(CopyRates(ChartSymbol(currChart), PERIOD_M30, 0, 9, rates)>0){
      tmpOffset=0;
      TimeToStruct(rates[tmpOffset].time, tmpTime);
      if(tmpTime.min!=0){
         tmpOffset++;
      }
      ObjectCreate(currChart, exprefix+"_hour_0", OBJ_VLINE, 0, rates[tmpOffset].time, 0);
      ObjectSetInteger(currChart,exprefix+"_hour_0",OBJPROP_SELECTABLE,false); 
      ObjectSetInteger(currChart,exprefix+"_hour_0",OBJPROP_STYLE,STYLE_DOT);
      ObjectSetInteger(currChart,exprefix+"_hour_0",OBJPROP_RAY,true); 
      ObjectSetInteger(currChart,exprefix+"_hour_0",OBJPROP_COLOR,clrYellow); 
      ObjectSetInteger(currChart,exprefix+"_hour_0",OBJPROP_BACK,true); 

      tmpOffset++;
      TimeToStruct(rates[tmpOffset].time, tmpTime);
      if(tmpTime.min!=0){
         tmpOffset++;
      }
      ObjectCreate(currChart, exprefix+"_hour_1", OBJ_VLINE, 0, rates[tmpOffset].time, 0);
      ObjectSetInteger(currChart,exprefix+"_hour_1",OBJPROP_SELECTABLE,false); 
      ObjectSetInteger(currChart,exprefix+"_hour_1",OBJPROP_STYLE,STYLE_DOT);
      ObjectSetInteger(currChart,exprefix+"_hour_1",OBJPROP_RAY,true); 
      ObjectSetInteger(currChart,exprefix+"_hour_1",OBJPROP_COLOR,clrYellow); 
      ObjectSetInteger(currChart,exprefix+"_hour_1",OBJPROP_BACK,true); 

      tmpOffset++;
      TimeToStruct(rates[tmpOffset].time, tmpTime);
      if(tmpTime.min!=0){
         tmpOffset++;
      }
      ObjectCreate(currChart, exprefix+"_hour_2", OBJ_VLINE, 0, rates[tmpOffset].time, 0);
      ObjectSetInteger(currChart,exprefix+"_hour_2",OBJPROP_SELECTABLE,false); 
      ObjectSetInteger(currChart,exprefix+"_hour_2",OBJPROP_STYLE,STYLE_DOT);
      ObjectSetInteger(currChart,exprefix+"_hour_2",OBJPROP_RAY,true); 
      ObjectSetInteger(currChart,exprefix+"_hour_2",OBJPROP_COLOR,clrYellow); 
      ObjectSetInteger(currChart,exprefix+"_hour_2",OBJPROP_BACK,true); 

      tmpOffset++;
      TimeToStruct(rates[tmpOffset].time, tmpTime);
      if(tmpTime.min!=0){
         tmpOffset++;
      }
      ObjectCreate(currChart, exprefix+"_hour_3", OBJ_VLINE, 0, rates[tmpOffset].time, 0);
      ObjectSetInteger(currChart,exprefix+"_hour_3",OBJPROP_SELECTABLE,false); 
      ObjectSetInteger(currChart,exprefix+"_hour_3",OBJPROP_STYLE,STYLE_DOT);
      ObjectSetInteger(currChart,exprefix+"_hour_3",OBJPROP_RAY,true); 
      ObjectSetInteger(currChart,exprefix+"_hour_3",OBJPROP_COLOR,clrYellow); 
      ObjectSetInteger(currChart,exprefix+"_hour_3",OBJPROP_BACK,true); 
      
   }
}

На графике закрытие последних 4 часов отображается в виде вертикальных желтых линий:

Показывать закрытия часа


Показывать максимальную и минимальную цену за год

Входящий параметр: Показывать макс./мин. цену за год
Область: Настройки графиков

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

Для отображения граничных цен за год используется следующая функция:

/*
Отобразить на графике максимальную и минимальную цену за последний год.
currChart - id графика.
*/
void showMaxPrice(long currChart){
   MqlRates rates[];
   ArraySetAsSeries(rates, true);
   if(CopyRates(ChartSymbol(currChart), PERIOD_MN1, 0, 12, rates)==12){
      double maxPrice=0;
      double minPrice=0;
      
      
      for(int j=0; j<12; j++){
         if( maxPrice==0 || maxPrice<rates[j].high ){
            maxPrice=rates[j].high;
         }
         if( minPrice==0 || minPrice>rates[j].low ){
            minPrice=rates[j].low;
         }
      }
      
      ObjectCreate(currChart, exprefix+"_maxprice_line", OBJ_HLINE, 0, 0, maxPrice);
      ObjectSetInteger(currChart,exprefix+"_maxprice_line",OBJPROP_COLOR,clrMagenta); 
      ObjectSetInteger(currChart,exprefix+"_maxprice_line",OBJPROP_SELECTABLE,false); 
      ObjectSetInteger(currChart,exprefix+"_maxprice_line",OBJPROP_BACK,true); 

      ObjectCreate(currChart, exprefix+"_minprice_line", OBJ_HLINE, 0, 0, minPrice);
      ObjectSetInteger(currChart,exprefix+"_minprice_line",OBJPROP_COLOR,clrMagenta); 
      ObjectSetInteger(currChart,exprefix+"_minprice_line",OBJPROP_SELECTABLE,false); 
      ObjectSetInteger(currChart,exprefix+"_minprice_line",OBJPROP_BACK,true); 
      
      
   }
}

Максимальные и минимальные цены за последние 12 месяцев отображаются фиолетовыми горизонтальными линиями:

Показывать максимальную/минимальную цену за год


Показывать спред при открытии графика

Входящий параметр: Показывать спред при открытии графика
Область: Настройки графиков

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

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

Спред возвращает следующая небольшая функция:

/*
Возвращает строку с текущим спредом для указанного символа.
symname - имя символа
*/
string getmespread_symbol(string symname){
   double curSpread=SymbolInfoDouble(symname, SYMBOL_ASK)-SymbolInfoDouble(symname, SYMBOL_BID);
   return "Spread: "+(string) DoubleToString(curSpread, (int) SymbolInfoInteger(symname, SYMBOL_DIGITS))+" "+SymbolInfoString(symname, SYMBOL_CURRENCY_PROFIT)+" ("+DoubleToString(curSpread/SymbolInfoDouble(symname, SYMBOL_POINT), 0)+" p)";
}

Спред выводится в комментарии к открытому графику, среди другой дополнительной информации:

Показывать спред

Применить к графику шаблон с именем

Входящий параметр: Применить к графику шаблон с именем
Область: Настройки графиков

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

Но «быстро» —  понятие растяжимое. Чтобы применить шаблон к графику, нужно в контекстном меню графика выбрать пункт Шаблон/«Имя шаблона». Но что, если какой-то индикатор, например, скользящая средняя, для вас является важным в принятии решения, и вы хотите видеть его на каждом открываемом графике. А открываете вы сотни графиков различных инструментов в день. Что же, по каждому из них кликать мышкой?

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

Чтобы применить шаблон к графику, достаточно воспользоваться командной ChartApplyTemplate, одинаково работающей и в MQL5, и в MQL4:

      ChartApplyTemplate(curChartID[ArraySize(curChartID)-1], allApplyTemplate+".tpl");


Показывать ATR по Герчику

Входящий параметр: Показывать ATR по Герчику
Область: Настройки графиков

ATR, то есть среднее дневное движение по инструменту за определенный период — важный параметр при определении запаса хода по инструменту. Используется он для разных целей.

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

Также считается, что если инструмент уже прошел более 80% своего дневного ATR в одну сторону, то открывать сделки в ту же сторону нет смысла и работать можно только в противоположном направлении.

ATR по Герчику отличается от обычного ATR только тем, что в расчет не берутся паранормальные бары. То есть, бары, которые больше средних в 2 раза или же менее 0.3 от среднестатистических баров.

Как правило, ATR считается за 5 или 3 дня.

За вывод ATR по Герчику отвечает следующая функция:

/*
Вернуть строку с ATR по Герчику для указанного символа:
symname - имя символа
*/
string getmeatr_symbol(string symname){
   string msg="";
   MqlRates rates[];
   ArraySetAsSeries(rates, true);
   double atr=0;
   double pre_atr=0;
   int curDigits=(int) SymbolInfoInteger(symname, SYMBOL_DIGITS);
   string currencyS=SymbolInfoString(symname, SYMBOL_CURRENCY_PROFIT);
   int count=0;
   int copied=CopyRates(symname, PERIOD_D1, 0, 8, rates);
   if(copied>1){
      for(int j=1; j<copied; j++){
         pre_atr+=rates[j].high-rates[j].low;
      }
      pre_atr/=(copied-1);
   }
   if(pre_atr>0){
      for(int j=1; j<copied; j++){
         if( rates[j].high-rates[j].low > pre_atr*2 ) continue;
         if( rates[j].high-rates[j].low < pre_atr*0.3 ) continue;
               
         if( ++count > 5 ){
            break;
         }
               
         atr+=rates[j].high-rates[j].low;
      }
      if( count > 5 ){
         count=5;
      }
      atr= NormalizeDouble(atr/count, curDigits );
   }
   if(atr>0){
      StringAdd(msg, "ATR: "+(string) DoubleToString(atr, curDigits)+" "+currencyS+", today: "+DoubleToString(rates[0].high-rates[0].low, curDigits)+" "+currencyS+" ("+(string) (int) (((rates[0].high-rates[0].low)/atr)*100) +"%)");
   }
   
   return msg;
}

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


Выводим направления по символам-поводырям

Входящий параметр: Вывод направления по символу #N
Область: Вывод доп. информации

Судя из материала, уже опубликованного в данной статье, внутридневая торговля — это весьма сложная штука. Столько всего нужно держать в голове и за стольким следить. И, как оказывается, это еще не все.

Многие трейдеры рекомендуют перед началом торговли просматривать текущее направление по так называемым символам-поводырям.

Например, если вы торгуете на американском фондовом рынке, то следить нужно:

  • за основными американскими индексами, ну, хотя бы за Dow Jones или SP500;
  • за одним из индексов остальных стран: Китая, Европы, Японии;
  • за крупными по капитализации компаниями в разных секторах рынка: JPMorgan в банковском секторе, Apple в сфере технологий, ExxonMobil в сфере энергетики, IBM и некоторыми другими.

Также можно следить за состоянием котировок нефти, золота, доллара.

Лучше всего, если все символы-поводыри будут идти в одну сторону. Как вы, наверное, догадались, торговать лучше только в ту сторону, в которую идут символы поводыри.

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

Направление по указанному символу возвращает следующая функция:

/*
Вернуть строку с направлением движения цены по указанному символу:
symname - имя символа
show - выводить ли в строке метку с названием символа
*/
string getmeinfo_symbol(string symname, bool show=true){
   MqlRates rates2[];
   ArraySetAsSeries(rates2, true);
   string msg="";

   if(CopyRates(symname, PERIOD_D1, 0, 1, rates2)>0){
      if(show){
         StringAdd(msg, (string) symname+": ");
      }
      StringAdd(msg, "D1 ");
      if( rates2[0].close > rates2[0].open ){
         StringAdd(msg, "+"+DoubleToString(((rates2[0].close-rates2[0].open)/rates2[0].close)*100, 2) +"% (+"+DoubleToString(rates2[0].close-rates2[0].open, (int) SymbolInfoInteger(symname, SYMBOL_DIGITS))+" "+SymbolInfoString(symname, SYMBOL_CURRENCY_PROFIT)+")");
      }else{
         if( rates2[0].close < rates2[0].open ){
            StringAdd(msg, "-"+DoubleToString(((rates2[0].open-rates2[0].close)/rates2[0].close)*100, 2) +"% (-"+DoubleToString(rates2[0].open-rates2[0].close, (int) SymbolInfoInteger(symname, SYMBOL_DIGITS))+" "+SymbolInfoString(symname, SYMBOL_CURRENCY_PROFIT)+")");
         }else{
            StringAdd(msg, "0%");
         }
      }
   }
   if(CopyRates(symname, PERIOD_H1, 0, 1, rates2)>0){
      StringAdd(msg, ", H1 ");
      if( rates2[0].close > rates2[0].open ){
         StringAdd(msg, "+"+DoubleToString(((rates2[0].close-rates2[0].open)/rates2[0].close)*100, 2)+"% (+"+DoubleToString(rates2[0].close-rates2[0].open, (int) SymbolInfoInteger(symname, SYMBOL_DIGITS))+" "+SymbolInfoString(symname, SYMBOL_CURRENCY_PROFIT)+")");
      }else{
         if( rates2[0].close < rates2[0].open ){
            StringAdd(msg, "-"+DoubleToString(((rates2[0].open-rates2[0].close)/rates2[0].close)*100, 2)+"% (-"+DoubleToString(rates2[0].open-rates2[0].close, (int) SymbolInfoInteger(symname, SYMBOL_DIGITS))+" "+SymbolInfoString(symname, SYMBOL_CURRENCY_PROFIT)+")");
         }else{
            StringAdd(msg, "0%");
         }
      }
   }
   
   return msg;
}

Направления выводятся в комментарии к графику, на котором запущена утилита. Выглядит это следующим образом.

Выводим направления по символам-поводырям

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


Автообновление вкладок утилиты

Входящий параметр: Обновлять открытую вкладку каждые, секунд
Область: Вывод доп. информации

Если вы хотите, чтобы содержимое открытой в данный момент вкладки обновлялось автоматически, а не только по нажатию клавиши R, тогда просто задайте количество секунд, через которое должно происходить обновление. Значение данного параметра более 0 говорит утилите о том, что если с момента вывода вкладки прошло больше указанного количества секунд, то нужно обновить данную вкладку. То есть, это не значит, что вкладка будет обновляться строго каждые N секунд. Скорее, это значит что примерно через N плюс еще одна-две секунды она будет обновлена.

Код, реализующий данный функционал, располагается внутри функции OnTimer, и довольно прост:

   // обновить содержимое вкладки через, секунд
   if( autoUpdateAfter>0 && TimeCurrent()-lastUpd >= autoUpdateAfter ){
      start_symbols();
      lastUpd=TimeCurrent();
   }

Естественно, что в начале работы утилиты, в функции OnInit, мы проинициализировали значение переменной lastUpd текущим временем.

Выводим финансовую информацию по акции

Входящий параметр: Показывать данные о компании
Область: Внешние данные

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

Давайте попробуем сделать, чтобы так и было. Чтобы сильно не усложнять, выводить показатели мы будем на вкладке Эксперты терминала, сразу как только график какой-либо акции был открыт с помощью нашей утилиты.

Сами финансовые показатели не будут браться с потолка. Вначале я хотел "зашить" их непосредственно в советнике, но ошибка нехватки памяти не позволила этого сделать. Поэтому все финансовые показатели хранятся отдельно, в папке finder, приложенной к данной статье. Чтобы данный функционал работал, вам нужно скачать архив с данной папкой, распаковать его, и поместить папку finder внутрь папки Files вашего терминала.

Чтобы найти саму папку Files, в окне MetaTrader раскройте меню Файл, и кликните по пункту Открыть каталог данных. После чего в открывшемся окне проводника перейдите в папку MQL4 или MQL5.

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

Но вернемся к нашей функции вывода данных. Данная функция чистит тикер компании от "лишнего", что может добавлять к нему ваш брокер. А именно, от префикса #, а также от суффиксов .us и .eu, которые добавляют некоторые брокеры. Если ваш брокер добавляет к тикеру что-то другое, то вам придется добавить в код утилиты свои фильтры, удаляющие эти символы.

Если по тикеру нужной информации в папке finder не найдено, то выполняется поиск по названию компании.

void printTicker(string ticker, string name){
   bool isYes=false;
   int filehandle;
   
   // чистим тикер от лишних суффиксов и префиксов
   // а также переводим его в нижний регистр
   StringToLower(ticker);
   StringReplace(ticker, "#", "");
   StringReplace(ticker, ".us", "");
   StringReplace(ticker, ".eu", "");
   
   // переводим в нижний регистр название компании
   StringToLower(name);
   
   ResetLastError(); 
   // если в папке finder есть информация о данной компании, то выводим ее
   if( FileIsExist("finder\\"+ticker+".dat") ){
      isYes=true;
      filehandle=FileOpen("finder\\"+ticker+".dat",FILE_READ|FILE_TXT); 
      if(filehandle!=INVALID_HANDLE){ 
         int str_size; 
         string str; 
         Print("----------------");
         while(!FileIsEnding(filehandle)){ 
            str_size=FileReadInteger(filehandle,INT_VALUE); 
            str=FileReadString(filehandle,str_size); 
            Print(str);
         }
         FileClose(filehandle); 
      }
   }else if( FileIsExist("finder\\"+name+".dat") ){
      isYes=true;
      filehandle=FileOpen("finder\\"+name+".dat",FILE_READ|FILE_TXT); 
      if(filehandle!=INVALID_HANDLE){ 
         int str_size; 
         string str; 
         Print("----------------");
         while(!FileIsEnding(filehandle)){ 
            str_size=FileReadInteger(filehandle,INT_VALUE); 
            str=FileReadString(filehandle,str_size); 
            Print(str);
        }
         FileClose(filehandle); 
      }
   }
   
   // иначе пишем, что информация не найдена
   if(!isYes){
      Print("Нет данных по символу");
   }
}

В результате, при открытии графика любой акции с помощью данной утилиты, на вкладке Эксперты будет выводиться информация по данной компании. Примерно, в таком виде:

Финансовая информация по компании


Показывать прибыль за сегодня

Входящий параметр: Показывать прибыль за сегодня
Область: Вывод доп. информации

И напоследок давайте добавим на график показатель, который позволит нам вовремя остановиться в своей дневной торговле. То есть, количество прибыли или убытка, полученное за день. А также общее количество сделок за день, количество прибыльных из них и количество убыточных (выводится на графике именно в такой последовательности).

Для MQL4 получить текущую прибыль можно пользовательской функцией getmetoday_profit(), код которой представлен ниже. А вот для MQL5 этой функции недостаточно.

В MQL5 мы сначала проверяем, нужно ли обновить сведения о закрытых позициях. Для этого используется объявленная глобально переменная needHistory. В стандартной функции OnInit() данной переменной присваивается значение true. Поэтому при первом запуске функции getmetoday_profit() происходит подгрузка информации о сделках из истории. После чего значение переменной needHistory становится равным false.

Далее мы используем стандартную функцию MQL5 OnTrade(), чтобы заново подгрузить историю по сделкам всякий раз, когда у нас закрывается какая-либо открытая позиция.

В результате у нас получится следующий код:

#ifdef __MQL5__ 
   void OnTrade(){
      if(orders_total==PositionsTotal()){
         return;
      }
      if(orders_total>PositionsTotal()){
         needHistory=true;
      }
      orders_total=(ushort) PositionsTotal();
   }
#endif 
/*
Возвращает строку с размером прибыли/убытка за сегодня.
*/
string getmetoday_profit(){
   string msg="";
   double curProfit=0;
   int tradeAll=0;
   int tradeMinus=0;
   int tradePlus=0;
   
   MqlDateTime curD;
   TimeCurrent(curD);
   curD.hour=0;
   curD.min=0;
   curD.sec=0;
   
   #ifdef __MQL5__ 
      if( needHistory ){
         HistorySelect(StructToTime(curD),TimeCurrent()); 
      }
      uint totalHistory=HistoryDealsTotal();
      double currHistory=0;
      ulong ticket_history;
      for(uint j=0;j<totalHistory;j++){
         if((ticket_history=HistoryDealGetTicket(j))>0 ){
            double profitHistory=HistoryDealGetDouble(ticket_history,DEAL_PROFIT);
            profitHistory+=HistoryDealGetDouble(ticket_history,DEAL_COMMISSION);
            profitHistory+=HistoryDealGetDouble(ticket_history,DEAL_SWAP);
            if(profitHistory!=0){
               currHistory+=profitHistory;
            }
         }
      }
         
   #else 
      int cntMyPos=OrdersHistoryTotal();
      if(cntMyPos>0){
         for(int ti=cntMyPos-1; ti>=0; ti--){
            if(OrderSelect(ti,SELECT_BY_POS,MODE_HISTORY)==false) continue;
            
            if( OrderCloseTime()<StructToTime(curD) ){ continue; }
            
            double tmpProfit=OrderProfit();
            tmpProfit+=OrderSwap();
            tmpProfit+=OrderCommission();
            
            tradeAll++;
            if(tmpProfit>0){
               tradePlus++;
            }else if(tmpProfit<0){
               tradeMinus++;
            }
            curProfit+=tmpProfit;
         }
      }
   #endif 
   
   if(tradeAll>0){
      StringAdd(msg, "Today: "+DoubleToString(curProfit, 2)+" "+AccountInfoString(ACCOUNT_CURRENCY)+" ("+(string) tradeAll+";+"+(string) tradePlus+";-"+(string) tradeMinus+")");
   }
   
   return msg;
}

Прибыль также выводится на графике, на котором запущена утилита. Выглядит это следующим образом:

Отображение прибыли за сегодня

Обратите внимание, что прибыль за день не пересчитывается автоматически. Чтобы информация о текущей прибыли обновилась, нужно перейти на одну из вкладок, отличную от открытой в данный момент, или нажать клавишу R.


Заключение

Скорее всего это последняя статья из данной серии. Надеюсь, получившаяся в итоге утилита будет вам полезна.

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

В завершение давайте подведем итоги и вкратце рассмотрим тот функционал, который мы реализовали на протяжении данной серии статей.

Итак, после запуска утилиты перед нами появится список инструментов, которые удовлетворяют нашим условиям. Над данным списком мы можем видеть кнопки вкладок: All, LONG, SHORT, Range, а также вкладки автофильтрации. По умолчанию открыта вкладка All:

График с запущенной на нем утилитой

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

Кстати о настройках утилиты. Понять функционал нашей утилиты будет проще, если рассмотреть эти настройки.

Список инструментов, которые выводятся на вкладке All, зависит от настроек из группы Настройки фильтрации:

Группа настроек Настройки фильтрации

Если нажать на кнопку какого-либо инструмента, которую вывела утилита, то перед вами откроется график данного инструмента с дополнительной информацией о нем:

График инструмента, при открытии его с помощью утилиты

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

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

Фиолетовая линия указывает на максимум или минимум года.

Красная линия — это уровень, который я ранее провел вручную. Утилита может сохранять и восстанавливать все уровни, которые вы ранее проводили на данном инструменте.

Как уже было сказано ранее в данной статье, линия болотного цвета — это цена закрытия/открытия позавчерашнего дня. А последняя изображенная на скриншоте линия — это цена закрытия/открытия вчерашнего дня.

Какие линии и какую информацию выводить на графике инструмента, определяют настройки из группы Настройки графиков:

Настройки из группы Настройки графиков

Таймфрейм и масштаб, который используется для отображения графиков, также можно настроить. Делается это отдельно для вкладки All, вкладок домашки, а также отдельно для некоторых вкладок автофильтрации.

Таймфрейм для вкладки All настраивается в группе Вывод. доп. информации:

Настройки из группы Вывод доп. информации

По умолчанию для всех кнопок инструментов на вкладке All используется таймфрейм 1 день. Для примера изменим его на 15 минут:

График инструмента с периодом 15 минут

Мы это сделали для того, чтобы увидеть еще несколько линий на графике, которые может выводить утилита. Так как данные линии выводятся только на периодах менее 1 дня.

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

Прерывистая горизонтальная линия указывает на круглые уровни. То есть, цены, которые оканчиваются на .00 и .50 для двухзначных котировок. Или, как в нашем примере, на 0.x1000 или 0.xx500 для 4 и 5-значных.

Но вернемся к настройкам. Вкладки автофильтрации также имеют свои настройки. Глобальные из них расположены в группе Дополнительные вкладки. А также для некоторых вкладок есть отдельные группы с настройками:

Настройки вкладок автофильтрации

И последние группы настроек: Открытие графика в новом окне и Внешние данные.

Группы настроек Открытие графика в новом окне и Внешние данные

Настройки группы Открытие графика в новом окне влияют на период и масштаб графиков, которые открываются при нажатии кнопки New windows.

Если вкратце, то это все. Более подробно весь функционал утилиты рассмотрен в предыдущих статьях данной серии.


Прикрепленные файлы |
finder.zip (8456.89 KB)
_finder.ex5 (354.46 KB)
_finder.mq5 (260.34 KB)
_finder.ex4 (230.82 KB)
_finder.mq4 (260.34 KB)
Denis Vyatkin
Denis Vyatkin | 27 апр 2019 в 09:32

На мой взгляд очень интересная утилита,пока начал её тестить.  Спасибо

Denis Vyatkin
Denis Vyatkin | 29 апр 2019 в 03:00

что то не корректно работает в мт4. в мт5 все норм

DJTrend
DJTrend | 10 май 2019 в 23:45
Роман, добрый день. Возникла проблема с отображением на мониторе с большим разрешением ( 4К). Как можно решить эту проблему?
Веб-скрапинг данных о доходности облигаций Веб-скрапинг данных о доходности облигаций

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

Библиотека для простого и быстрого создания программ для MetaTrader (Часть V): Классы и коллекция торговых событий, отправка событий в программу Библиотека для простого и быстрого создания программ для MetaTrader (Часть V): Классы и коллекция торговых событий, отправка событий в программу

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

Библиотека для простого и быстрого создания программ для MetaTrader (Часть VI): События на счёте с типом неттинг Библиотека для простого и быстрого создания программ для MetaTrader (Часть VI): События на счёте с типом неттинг

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

ZUP - зигзаг универсальный с паттернами Песавенто: Графический интерфейс. Дополнения и изменения. Вилы Эндрюса в ZUP ZUP - зигзаг универсальный с паттернами Песавенто: Графический интерфейс. Дополнения и изменения. Вилы Эндрюса в ZUP

В версии 153 редактирование почти всех параметров ZUP можно осуществлять через графический интерфейс. В статье дано описание последних изменений в графическом интерфейсе ZUP. Описаны также основные элементы вил Эндрюса в ZUP для использования этого инструмента при анализе рыночной ситуации.