English 中文 Español Deutsch 日本語 Português
Автоматический подбор перспективных сигналов

Автоматический подбор перспективных сигналов

MetaTrader 5Интеграция | 21 ноября 2017, 13:51
7 629 12
Alexander Fedosov
Alexander Fedosov

Введение

Автоматическая торговля на финансовых рынках — цель постоянной разработки всё новых и новых торговых роботов, ведь рынок не стоит на месте, а постоянно изменяется. Тем не менее, автоматические советники не могут предвидеть все ситуации на рынке. Поэтому наиболее эффективным способом всё равно остается симбиоз робота и человека, управляющего работой своих автоматических систем и корректирующего ее. Наблюдать за торговлей в таком сочетании можно в сервисе Торговых сигналов. Однако там предлагаются торговые системы и методы торговли с различными степенями риска или динамикой торговли. Инструментов поиска нужного именно вам сигнала в сервисе достаточно много, но как определить ту совокупность параметров, которые бы подходили под конкретно ваш стиль торговли? Удобно было бы определить набор параметров торгового сигнала и их значений, которые отвечали бы требованиям рискованного, умеренного или консервативного трейдинга.

Модель оценки торговых сигналов

Для комплексной оценки торговых сигналов было решено выбрать пять критериев оценки. Каждый из них, в свою очередь, будет давать оценку по 20-балльной системе, где 0 — это рискованный стиль трейдинга, а 20 — консервативный. В сумме каждый торговый сигнал будет оцениваться по 100-балльной системе. В качестве критериев я выбрал следующие характеристики торговых сигналов.

1) Плечо торгового счета.

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

  • 20 баллов присвоим торговому плечу 1:1.
  • 0 баллов присвоим торговому плечу 1:500 и выше.

Для проведения оценки, исходя из заданного значения применяемого торгового плеча, воспользуемся формулой канонического уравнения прямой:

где Xa = 1, Ya = 20, и, соответственно, Xb = 500 и Yb = 0. Таким образом мы получим уравнение прямой, проходящей через две точки. В нашем случае оно отразит зависимость оценки по 20-балльной шкале от текущего значения плеча. 

Еще раз подчеркнем, что полученная зависимость действует в рамках заданного оценочного диапазона от 0 до 20. Это означает, что если используемое торговое плечо будет выше 1:500 (например, 1:1000), то его оценка все равно будет 0.

2) Прирост счета в процентах.

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

  • Отрицательный прирост будет означать, что в данный момент оцениваемый сигнал находится в группе риска, поэтому ему будет присвоена оценка 0.
  • Оценивать будем не абсолютный прирост счета, а его динамику во времени. Для этого прирост счета поделим на время жизни сигнала в неделях.
  • Понятие риска у всех разное. Для кого-то 10% прироста в неделю — это мало, для кого-то — много. Однако определенный диапазон установить всё же необходимо. Поэтому примем 1% прироста в неделю за эталонный прирост консервативного сигнала, а порогом рискованного трейдинга назначим 15% в неделю.

Снова воспользуемся формулой получения уравнения прямой через две точки, где Xa = 1, Ya = 20, и, соответственно Xb = 15 и Yb = 0. Получим следующую зависимость:

3) Максимальная просадка в процентах.

Этот показатель напрямую характеризует риск торговли. Чтобы определить границы, введем следующие правила.

  • Просадка до 20% включительно будет считаться консервативным стилем торговли и оцениваться в 20 баллов.
  • Просадку выше 40% будем считать за рискованный стиль торговли и оценивать в 0 баллов.
  • Интервал 20-40 % максимальной просадки будет оцениваться исходя из уравнения прямой, заданной двумя предыдущими точками.  

В этом уравнении Xa = 20, Ya = 20, и соответственно Xb = 40 и Yb = 0. Получим следующую зависимость:

4) Значение ROI ( Return on Investment) в процентах.

Если окупаемость инвестиций выше 100%, это означает прибыльное использование средств; если ниже 100% — значит, инвестиции применяются неэффективно. Наложим этот показатель степени успешности инвестирования на 20-балльную шкалу.

  • ROI менее 100% оценим в 0 баллов.
  • ROI более 200% примем за 20 баллов.
  • Интервал от 100 до 200 будет оцениваться исходя из уравнения прямой, заданной двумя предыдущими точками.

В этом уравнении Xa = 100, Ya = 0, и соответственно Xb = 200 и Yb = 20. Получим следующую зависимость:

5) Возраст торгового сигнала.

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

  • Возраст сигнала менее 4 недель (количества полных недель в месяце) оценим в 0 баллов.
  • Возраст сигнала более 25 недель (количество полных недель в полугодии) оценим в 20 баллов.
  • Интервал от 4 до 25 недель будет оцениваться из уравнения прямой, заданной двумя предыдущими точками.  


Реализация инструмента оценки

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

  • Таблица доступных сигналов для текущего торгового счета в терминале.
  • Подробная оценка выбранного в таблице торгового сигнала по категориям.
  • Количество доступных торговых сигналов для текущего счета.
  • Визуальная совокупная оценка выбранного в таблице торгового сигнала.


Рис.1а Структура приложения(левая часть)

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


Рис.1б Структура приложения(правая часть)

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

  • Метод CreateWindow() создает окно приложения с заголовком. Есть возможность закрывать его и уменьшать размер.
  • Метод CreateTable() создает таблицу со всеми доступными для текущего торгового счета сигналами.
  • Метод CreateStatusBar() создает статусную строку, отображающую общее количество доступных торговых сигналов.
  • Методы CreatePicture1() и CreatePicture2() создают градиентную шкалу и указатель для шкалы, соответственно.
  • Методы CreateTextLabel() задают отображение подробной информации об оценке выбранного торгового сигнала.
  • Метод CreateButton() создает кнопку подписки на сигнал, выбранный в таблице.
//+------------------------------------------------------------------+
//| Создаёт графический интерфейс программы                          |
//+------------------------------------------------------------------+
bool CProgram::CreateGUI(void)
  {
//--- Создание панели
   if(!CreateWindow("Auto Search Signal"))
      return(false);
//--- Создание таблицы
   if(!CreateTable(7,100))
      return(false);
//--- Статусная строка
   if(!CreateStatusBar(1,26))
      return(false);
//--- Картинки
   if(!CreatePicture1(618,40))
      return(false);
   if(!CreatePicture2(610,80))
      return(false);
//--- Текстовая метка
   if(!CreateTextLabel1(20,20,"Оценка плеча: -"))
      return(false);
   if(!CreateTextLabel2(20,40,"Оценка прироста: -"))
      return(false);
   if(!CreateTextLabel3(20,60,"Оценка просадки: -"))
      return(false);
   if(!CreateTextLabel4(200,20,"Оценка ROI: -"))
      return(false);
   if(!CreateTextLabel5(200,40,"Оценка возраста: -"))
      return(false);
//--- Кнопки с картинкой
   if(!CreateButton(440,40,"Подписаться"))
      return(false);
//--- Завершение создания GUI
   CWndEvents::CompletedGUI();
   return(true);
  }
//+-----------------------------------------------------------------


Следующий метод отвечает за интерактивное взаимодействие: выбор торгового сигнала из списка в таблице и отображение информации о нем. Также он отслеживает событие по нажатию созданной ранее кнопки и подписке на выбранный в таблице сигнал.

//+------------------------------------------------------------------+
//| Обработчик событий                                               |
//+------------------------------------------------------------------+
void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Событие нажатия на пункте списка или таблицы
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_LIST_ITEM)
     {
      int x=610+3*int(m_table.GetValue(9,m_table.SelectedItem()));
      //---
      CreateTextLabel1(20,20,"Оценка плеча: "+IntegerToString(GetLeverageRating(int(m_table.GetValue(2,m_table.SelectedItem())))));
      CreateTextLabel2(20,40,"Оценка прироста: "+IntegerToString(GetGainRating(int(m_table.GetValue(3,m_table.SelectedItem())),int(m_table.GetValue(8,m_table.SelectedItem())))));
      CreateTextLabel3(20,60,"Оценка просадки: "+IntegerToString(GetDrawDownRating(int(m_table.GetValue(5,m_table.SelectedItem())))));
      CreateTextLabel4(200,20,"Оценка ROI: "+IntegerToString(GetROIRating(int(m_table.GetValue(4,m_table.SelectedItem())))));
      CreateTextLabel5(200,40,"Оценка возраста: "+IntegerToString(GetWeeksRating(int(m_table.GetValue(8,m_table.SelectedItem())))));
      CreatePicture2(x,80);
      //---
      m_button.IsLocked(false);
      Update(true);
     }
//---
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON)
     {
      //--- Если нажали на кнопку
      if(lparam==m_button.Id())
        {
         SignalSubscribe(long(m_table.GetValue(6,m_table.SelectedItem())));
        }
     }
  }

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

Метод GetTradingSignals() получает всю информацию о доступных сигналах и заносит их в массивы данных:

//+------------------------------------------------------------------+
//| Получение информации по доступным торговым сигналам              |
//+------------------------------------------------------------------+
bool CProgram::GetTradingSignals(void)
  {
//--- запрашиваем общее количество сигналов в базе 
   m_signals_total=SignalBaseTotal();
//---
   ArrayResize(m_name,m_signals_total);
   ArrayResize(m_curr,m_signals_total);
   ArrayResize(m_leverage,m_signals_total);
   ArrayResize(m_gain,m_signals_total);
   ArrayResize(m_roi,m_signals_total);
   ArrayResize(m_max_drawdown,m_signals_total);
   ArrayResize(m_pips,m_signals_total);
   ArrayResize(m_subscr,m_signals_total);
   ArrayResize(m_weeks,m_signals_total);
   ArrayResize(m_rating,m_signals_total);
//--- цикл по всем сигналам 
   for(int i=0;i<m_signals_total;i++)
     {
      //--- выбираем сигнал для дальнейшей работы 
      if(SignalBaseSelect(i))
        {
         //--- получение свойств сигнала 
         m_name[i]=SignalBaseGetString(SIGNAL_BASE_NAME);                                                // имя сигнала
         m_curr[i]=SignalBaseGetString(SIGNAL_BASE_CURRENCY);                                            // валюта сигнала
         m_leverage[i]=SignalBaseGetInteger(SIGNAL_BASE_LEVERAGE);                                       // плечо
         m_gain[i]=SignalBaseGetDouble(SIGNAL_BASE_GAIN);                                                // прирост счета в %
         m_roi[i]=SignalBaseGetDouble(SIGNAL_BASE_ROI);                                                  // ROI
         m_max_drawdown[i]=SignalBaseGetDouble(SIGNAL_BASE_MAX_DRAWDOWN);                                // Максимальная просадка
         m_id[i]=SignalBaseGetInteger(SIGNAL_BASE_ID);                                                   // ID сигнала
         m_subscr[i]=SignalBaseGetInteger(SIGNAL_BASE_SUBSCRIBERS);                                      // количество подписчиков 
         m_weeks[i]=int((TimeCurrent()-SignalBaseGetInteger(SIGNAL_BASE_DATE_PUBLISHED))/3600/24/7);     // возраст сигнала
         //--- получение совокупной оценки
         m_rating[i]=GetLeverageRating(m_leverage[i])+GetGainRating(m_gain[i],m_weeks[i])+GetDrawDownRating(m_max_drawdown[i])+GetROIRating(m_roi[i])+GetWeeksRating(m_weeks[i]);
        }
      else
        {
         PrintFormat("Ошибка выбора сигнала. Код ошибки=%d",GetLastError());
         return(false);
        }
     }
   return (true);
  }
//+------------------------------------------------------------------+

Как видно из листинга выше, в массив m_rating[] добавляются данные совокупной оценки для каждого торгового сигнала. Поэтому рассмотрим методы, используемые в этом расчете. Они являются программной реализацией модели из первого раздела статьи.

//+------------------------------------------------------------------+
//| Оценка торгового плеча                                           |
//+------------------------------------------------------------------+
int CProgram::GetLeverageRating(long leverage)
  {
   int lev_rating=int(-20.0/499.0*double(leverage)+10000.0/499.0);
   lev_rating=(lev_rating>20)?20:lev_rating;
   return lev_rating;
  }
//+------------------------------------------------------------------+
//| Оценка прироста                                                  |
//+------------------------------------------------------------------+
int CProgram::GetGainRating(double gain,int weeks)
  {
   weeks=(weeks==0)?1:weeks;
   int gain_rating=int(-10*(gain/double(weeks)/7.0)+150.0/7.0);
   gain_rating=(gain_rating>20)?20:gain_rating;
   gain_rating=(gain_rating<0)?0:gain_rating;
   gain_rating=(gain<0)?0:gain_rating;
   return gain_rating;
  }
//+------------------------------------------------------------------+
//| Оценка максимальной просадки                                     |
//+------------------------------------------------------------------+
int CProgram::GetDrawDownRating(double max_drawdown)
  {
   int drawdn_rating=int(-max_drawdown+40);
   drawdn_rating=(drawdn_rating>20)?20:drawdn_rating;
   drawdn_rating=(drawdn_rating<0)?0:drawdn_rating;
   return drawdn_rating;
  }
//+------------------------------------------------------------------+
//| Оценка ROI                                                       |
//+------------------------------------------------------------------+
int CProgram::GetROIRating(double roi)
  {
   int roi_rating=int(0.2*roi-20);
   roi_rating=(roi_rating>20)?20:roi_rating;
   roi_rating=(roi_rating<0)?0:roi_rating;
   return roi_rating;
  }
//+------------------------------------------------------------------+
//| Оценка возраста торгового сигнала                                |
//+------------------------------------------------------------------+
int CProgram::GetWeeksRating(int weeks)
  {
   int age_rating=int(20.0*double(weeks)/21.0-80.0/21.0);
   age_rating=(age_rating>20)?20:age_rating;
   age_rating=(age_rating<0)?0:age_rating;
   return age_rating;
  }

Далее все полученные  данные заносятся в таблицу методом InitializingTable() и визуально отображаются методом CreateTable().

//+------------------------------------------------------------------+
//| Инициализация таблицы                                            |
//+------------------------------------------------------------------+
void CProgram::InitializingTable(void)
  {
//---
   string columns[10]=
     {
      "Имя сигнала",
      "Валюта счета",
      "Плечо торг. счета",
      "Прирост счета, %",
      "ROI",
      "Макс. просадка",
      "ID сигнала",
      "Кол-во подписчиков",
      "Возраст, нед.",
      "Оценка"
     };
//---
   for(int c=0; c<COLUMNS1_TOTAL; c++)
     {
      //--- Установим названия заголовков
      m_table.SetHeaderText(c,columns[c]);
      //---
      for(int r=0; r<m_signals_total; r++)
        {
         if(c==0)
            m_table.SetValue(c,r,m_name[r]);
         else if(c==1)
            m_table.SetValue(c,r,m_curr[r]);
         else if(c==2)
            m_table.SetValue(c,r,IntegerToString(m_leverage[r]));
         else if(c==3)
            m_table.SetValue(c,r,DoubleToString(m_gain[r],2));
         else if(c==4)
            m_table.SetValue(c,r,DoubleToString(m_roi[r],2));
         else if(c==5)
            m_table.SetValue(c,r,DoubleToString(m_max_drawdown[r],2));
         else if(c==6)
            m_table.SetValue(c,r,IntegerToString(m_id[r]));
         else if(c==7)
            m_table.SetValue(c,r,IntegerToString(m_subscr[r]));
         else if(c==8)
            m_table.SetValue(c,r,IntegerToString(m_weeks[r]));
         else if(c==9)
            m_table.SetValue(c,r,IntegerToString(m_rating[r]));
        }
     }
  }
//+------------------------------------------------------------------+
//| Создаёт нарисованную таблицу                                     |
//+------------------------------------------------------------------+
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\arrow_up.bmp"
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\arrow_down.bmp"
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\circle_gray.bmp"
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\calendar.bmp"
//---
bool CProgram::CreateTable(const int x_gap,const int y_gap)
  {
#define COLUMNS1_TOTAL 10
//--- Сохраним указатель на главный элемент
   m_table.MainPointer(m_window);
//--- Массив ширины столбцов
   int width[COLUMNS1_TOTAL];
   ::ArrayInitialize(width,110);
   width[1]=80;
   width[2]=100;
   width[4]=90;
   width[8]=85;
   width[9]=90;
//--- Массив отступа текста в столбцах по оси X
   int text_x_offset[COLUMNS1_TOTAL];
   ::ArrayInitialize(text_x_offset,7);
//--- Массив выравнивания текста в столбцах
   ENUM_ALIGN_MODE align[COLUMNS1_TOTAL];
   ::ArrayInitialize(align,ALIGN_CENTER);
//---
   GetTradingSignals();
//--- Свойства
   m_table.XSize(1000);
   m_table.YSize(470);
   m_table.CellYSize(20);
   m_table.TableSize(COLUMNS1_TOTAL,m_signals_total);
   m_table.TextAlign(align);
   m_table.ColumnsWidth(width);
   m_table.TextXOffset(text_x_offset);
   m_table.LabelXGap(5);
   m_table.LabelYGap(4);
   m_table.IconXGap(7);
   m_table.IconYGap(4);
   m_table.MinColumnWidth(0);
   m_table.ShowHeaders(true);
   m_table.IsSortMode(true);
   m_table.LightsHover(true);
   m_table.SelectableRow(true);
   m_table.IsWithoutDeselect(true);
   m_table.ColumnResizeMode(true);
   m_table.IsZebraFormatRows(clrWhiteSmoke);
   m_table.AutoXResizeMode(true);
   m_table.AutoXResizeRightOffset(7);
   m_table.AutoYResizeBottomOffset(28);
   m_table.HeadersColor(clrSkyBlue);
   m_table.DataType(2,TYPE_INT);
   m_table.DataType(3,TYPE_FLOAT);
   m_table.DataType(4,TYPE_FLOAT);
   m_table.DataType(5,TYPE_FLOAT);
   m_table.DataType(6,TYPE_INT);
   m_table.DataType(7,TYPE_INT);
   m_table.DataType(8,TYPE_INT);
   m_table.DataType(9,TYPE_INT);

//--- Заполним таблицу данными
   InitializingTable();
//--- Создадим элемент управления
   if(!m_table.CreateTable(x_gap,y_gap))
      return(false);
//--- Добавим объект в общий массив групп объектов
   CWndContainer::AddToElementsArray(0,m_table);
   m_table.SortData(9);
   m_table.SortData(9);
   return(true);
  }

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

Последний элемент, на который стоит обратить внимание, — это визуальное отображение совокупной оценки в виде градиентной шкалы и указателя на ней. За это отвечают два метода, листинги которых представлены ниже. Изменение положения указателя на шкале уже было отмечено в методе OnEvent() чуть выше.

//+------------------------------------------------------------------+
//| Создаёт градиентную шкалу                                        |
//+------------------------------------------------------------------+
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp64\\000.bmp"
#resource "\\Images\\EasyAndFastGUI\\Controls\\ArrowUp_blue.bmp"
//---
bool CProgram::CreatePicture1(const int x_gap,const int y_gap)
  {
//--- Сохраним указатель на главный элемент
   m_picture1.MainPointer(m_window);
//--- Свойства
   m_picture1.XSize(300);
   m_picture1.YSize(40);
   m_picture1.IconFile("Images\\EasyAndFastGUI\\Icons\\bmp64\\000.bmp");
//--- Создание кнопки
   if(!m_picture1.CreatePicture(x_gap,y_gap))
      return(false);
//--- Добавим указатель на элемент в базу
   CWndContainer::AddToElementsArray(0,m_picture1);
   return(true);
  }
//+------------------------------------------------------------------+
//| Создает указатель для шкалы                                      |
//+------------------------------------------------------------------+
bool CProgram::CreatePicture2(const int x_gap,const int y_gap)
  {
//--- Сохраним указатель на главный элемент
   m_picture2.MainPointer(m_window);
//--- Свойства
   m_picture2.XSize(16);
   m_picture2.YSize(16);
   m_picture2.IconFile("Images\\EasyAndFastGUI\\Controls\\ArrowUp_blue.bmp");
//--- Создание кнопки
   if(!m_picture2.CreatePicture(x_gap,y_gap))
      return(false);
//--- Добавим указатель на элемент в базу
   CWndContainer::AddToElementsArray(0,m_picture2);
   return(true);
  }


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

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

Поэтому визуально разделим разработанную нами выше систему оценки сигналов на три категории (рис.2).

  • Первая категория. Красная зона. Перспективность торговых сигналов в этой зоне оценки заключается в возможной высокой прибыльности, но сопряжено с высокими рисками.
  • Вторая категория. Желтая зона. Сигналы в этой зоне перспективны за счет хороших показателей прибыльности при средних рисках.
  • Третья категория. Зеленая зона. Перспективность заключается в повышенной надежности сигнала, но показатели прибыльности невысоки.

Рис.2 Разбивка на категории градиентной шкалы оценки торговых сигналов

Чтобы запрограммировать эту категоризацию, добавим в метод InitializingTable()следующие строчки:

//+------------------------------------------------------------------+
//| Инициализация таблицы                                            |
//+------------------------------------------------------------------+
void CProgram::InitializingTable(void)
  {
//---
   string columns[10]=
     {
      "Имя сигнала",
      "Валюта счета",
      "Плечо торг. счета",
      "Прирост счета, %",
      "ROI",
      "Макс. просадка",
      "ID сигнала",
      "Кол-во подписчиков",
      "Возраст, нед.",
      "Оценка"
     };
//---
   for(int c=0; c<COLUMNS1_TOTAL; c++)
     {
      //--- Установим названия заголовков
      m_table.SetHeaderText(c,columns[c]);

      //---
      for(int r=0; r<m_signals_total; r++)
        {
         if(c==0)
           {
            m_table.SetValue(c,r,m_name[r]);
            if(m_rating[r]<=30)
               m_table.TextColor(c,r,clrCrimson);
            else if(m_rating[r]>30 && m_rating[r]<=66)
               m_table.TextColor(c,r,clrOrange);
            else if(m_rating[r]>66)
               m_table.TextColor(c,r,clrForestGreen);
           }
         else if(c==1)
            m_table.SetValue(c,r,m_curr[r]);
         else if(c==2)
            m_table.SetValue(c,r,IntegerToString(m_leverage[r]));
         else if(c==3)
            m_table.SetValue(c,r,DoubleToString(m_gain[r],2));
         else if(c==4)
            m_table.SetValue(c,r,DoubleToString(m_roi[r],2));
         else if(c==5)
            m_table.SetValue(c,r,DoubleToString(m_max_drawdown[r],2));
         else if(c==6)
            m_table.SetValue(c,r,IntegerToString(m_id[r]));
         else if(c==7)
            m_table.SetValue(c,r,IntegerToString(m_subscr[r]));
         else if(c==8)
            m_table.SetValue(c,r,IntegerToString(m_weeks[r]));
         else if(c==9)
            m_table.SetValue(c,r,IntegerToString(m_rating[r]));
        }
     }
  }

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

Рис.3 Цветовая индикация торговых сигналов

Можно сгруппировать сигналы по категориям, отсортировав таблицу по последнему столбцу. По умолчанию эта сортировка включена. Последнее очевидное действие — реализовать возможность подписаться на сигнал. За это отвечает метод CreateButton() :

//+------------------------------------------------------------------+
//| Создаёт кнопку с картинкой                                       |
//+------------------------------------------------------------------+
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\start.bmp"
//---
bool CProgram::CreateButton(const int x_gap,const int y_gap,const string text)
  {
//--- Сохраним указатель на главный элемент
   m_button.MainPointer(m_window);
//--- Свойства
   m_button.XSize(120);
   m_button.YSize(22);
   m_button.IconXGap(3);
   m_button.IconYGap(3);
   m_button.FontSize(10);
   m_button.IsCenterText(true);
   m_button.IconFile("Images\\EasyAndFastGUI\\Icons\\bmp16\\start.bmp");
   m_button.IsLocked(true);
//--- Создадим элемент управления
   if(!m_button.CreateButton(text,x_gap,y_gap))
      return(false);
//--- Добавим указатель на элемент в базу
   CWndContainer::AddToElementsArray(0,m_button);
   return(true);
  }
//+------------------------------------------------------------------+

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

1. Для работы с торговыми сигналами в терминале MetaTrader 5 вы должны быть авторизованы в нем под свои mql5-аккаунтом на странице настроек:

Рис.4 Авторизация в терминале

2. При запуске приложения нужно включить опцию Разрешить изменение настроек сигналов. Без этого подписка на торговый сигнал с помощью разработанного приложения будет выдавать ошибку 4014 ("Системная функция не разрешена для вызова").

Рис.5 Необходимая настройка при запуске приложения

3. И последнее, что может повлиять на правильную работу — файл signals.dat. Если данные некорректно отображаются в таблице или вообще отсутствуют, следует найти этот файл по адресу C:\Users\Имя компьютера\AppData\Roaming\MetaQuotes\Terminal\..\bases\signals и удалить его. Затем перезапустите терминал и откройте в нем вкладку Сигналы, как показано на рис.6. Новый файл сгенерируется автоматически, после этого можно будет запустить и использовать приложение.

Рис.6 Вкладка сигналы в терминале MetaTrader 5

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


Заключение

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

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

Программы, используемые в статье:

#
 Имя
Тип
Описание
1
AutoSearch.mq5 Эксперт
 Приложение для автоматического подбора перспективных сигналов
2
Program.mqh Библиотека  Класс для создания приложения
3
MainWindow.mqh Библиотека  Набор методов, используемых для создания приложения


Прикрепленные файлы |
MQL5.zip (1631.66 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (12)
Andrey F. Zelinsky
Andrey F. Zelinsky | 21 нояб. 2017 в 14:33

Качественные оценки крайне сложно выводить.

Как пример важности качественной оценки: три волка и стадо овец -- кого больше?

Andrey F. Zelinsky
Andrey F. Zelinsky | 21 нояб. 2017 в 14:45

Долгое время наблюдаю за продуктами и продвижением продуктов некоего Механика.

Его продукты в ТОПе, пользуются супер-популярностью. По каждому продукту Механик демонстрирует сигнал на реальном счёте.

НО.

Все его продукты -- это исключительно илан-мартин-торговля. Большинство его сигналов живёт не больше 10 недель.

Понятно, что по количественным параметрам -- его продукты топовые -- есть ажиотаж и этот ажиотаж поддерживается самой МК-Маркет-системой (хроника сайта, ТОП, реклама в баннере второй строчкой на каждой странице сайта).

Но если сделать качественную оценку -- то его продукты должны быть далеко в конце ТОПа.

Недавно в сигналах МК ввели показатель "надёжность" -- этот показатель опять же строится исключительно по количественным показателям -- именно поэтому по форуму есть нарекание на его адекватность и объективность.

[Удален] | 21 нояб. 2017 в 14:57
Andrey F. Zelinsky:

Долгое время наблюдаю за продуктами и продвижением продуктов некоего Механика.

Его продукты в ТОПе, пользуются супер-популярностью. По каждому продукту Механик демонстрирует сигнал на реальном счёте.

НО.

Все его продукты -- это исключительно илан-мартин-торговля. Большинство его сигналов живёт не больше 10 недель.

Понятно, что по количественным параметрам -- его продукты топовые -- есть ажиотаж и этот ажиотаж поддерживается самой МК-Маркет-системой (хроника сайта, ТОП, реклама в баннере второй строчкой на каждой странице сайта).

Но если сделать качественную оценку -- то его продукты должны быть далеко в конце ТОПа.

Недавно в сигналах МК ввели показатель "надёжность" -- этот показатель опять же строится исключительно по количественным показателям -- именно поэтому по форуму есть нарекание на его адекватность и объективность.


У него всегда новый продукт и сразу бесплатный... это ладно, но самое главное ведь почему у меня, например, тоже скачивают бесплатный продукт 1000 раз, но пишут 1-2 отзыва, а у него сразу 20-30, вот вопрос ведь. Потом продукт становится платным, а рейтинг уже ведь есть и притягивает всё новых пользователей

Sergey Vradiy
Sergey Vradiy | 12 дек. 2017 в 17:39

Вот давайте сразу определимся с риск-менеджментом. Тут поступило предложение считать максимальную просадку в 20% достойной высшего балла. Не соглашусь, и вот почему. Нужно учесть, при каких обстоятельствах возникла такая просадка. Она может возникнуть на консолидации при запредельном плече, но может возникнуть и на форс-мажоре типа Brexit. Это, как говорится, 2 большие разницы. В 1 случае имеем явный авантюризм, во 2-м - грамотная работа с капиталом. У меня идея другая. Максимальный лот никогда не должен выходить за пределы запаса прочности в случае максимальной годовой волатильности. Это означает, что если пара может двигаться на 3000 пунктов в год, такое движение должно быть учтено в максимальном лоте. Ещё проще для бронетанковых войск. Если при форс-мажоре пара может пройти 30 фигур, то это сразу закладываем в риск. Открываемся по этой паре так, чтобы выдержать просадку 3000 пунктов. Мы не знаем, когда и как произойдёт разворот. Поэтому не будем ломать голову и сразу заложим в риск вероятность ЛЮБОЙ просадки.  

Stan Baftalovskiy
Stan Baftalovskiy | 5 мар. 2018 в 17:24
fxsaber:

Получается, что предложен кастомный GUI, но не критерии выбора.

Разработчиков давно просят дать через Signal-функции доступ к торговой истории Сигнала. Сам терминал такой доступ имеет, когда высвечивает историю в виде объектов на чартах. А через MQL этого нет.

Без этого написать что-то толковое почти невозможно. Разве только GUI, что статья и показывает.

Полностью согласен - статья просто демонстрирует, что ничего стоящего из SignalBaseGetхххх получить невозможно.

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


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

1) нужна функция SignalBaseGetHistory, которая позволит автоматически загрузить с сервера тот файл с историей в формате csv, которая доступна для скачивания через вэб-интерфейс,

2) для функции SignalBaseGetDouble нужен параметр для получения текущей просадки;

3) для функции SignalBaseGetDouble нужен параметр для получения % алготрейдинга;

4) для функции SignalBaseGetDouble нужен параметр для получения Общей прибыли в валюте счета;

5) для функции SignalBaseGetDouble нужен параметр для получения Общей прибыли в пипсах;

6) для функции SignalBaseGetDouble нужен параметр для получения Среднего времени удержания позиции;

7) для функции SignalBaseGetDouble нужен параметр для получения % прибыльных трейдов.

Торговля по уровням ДиНаполи Торговля по уровням ДиНаполи
В статье рассматривается один из вариантов практической реализации советника для торговли по уровням ДиНаполи при помощи стандартных инструментов MQL5. Протестированы результаты его работы и сделаны выводы.
Торговая стратегия 'Momentum Pinball' Торговая стратегия 'Momentum Pinball'
В этой статье продолжается тема написания кода к торговым системам, описанным в книге Линды Рашке и Лоуренса Коннорса "Биржевые секреты. Высокоэффективные стратегии краткосрочной торговли". На этот раз исследуется система 'Momentum Pinball': описано создание двух индикаторов, торгового робота и сигнального блока по ней.
Создаем новую торговую стратегию с использованием технологии разложения входов на индикаторы Создаем новую торговую стратегию с использованием технологии разложения входов на индикаторы
В статье предложена технология, с помощью которой каждый желающий сможет создать свою уникальную торговую стратегию, собрав индивидуальный набор индикаторов, и разработать собственные сигналы для входа в рынок.
Ночная торговля в азиатскую сессию: как оставаться в прибыли Ночная торговля в азиатскую сессию: как оставаться в прибыли
В статье рассматривается понятие ночной торговли, стратегии торговли, их реализация на MQL5. Проведено тестирование и сделаны выводы.