Простейшие торговые системы с использованием семафорных индикаторов

Nikolay Kositsin | 13 января, 2012

Введение

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

Подобных индикаторов очень много, но сама суть первоначальной торговой системы, построенной на основе таких индикаторов, абсолютно не меняется. И поэтому возникла идея воплотить её в максимально простом и универсальном виде. Это позволит в дальнейшем использовать полученный результат с минимальными переделками с любыми аналогичными индикаторами.

Рис.1 Семафорный индикатор ASCtrend

Рис.1 Семафорный сигнальный индикатор ASCtrend

Рис.2 Индикатор ASCtrend Торговый сигнал для совершения сделки 

Рис.2 Торговый сигнал для сделки с использованием семафорного сигнального индикатора  ASCtrend


Образцы типичных семафорных, сигнальных индикаторов

На данный момент в Code Base имеется достаточно много подобных индикаторов, в контексте данной статьи я сделаю только несколько ссылок на исходные ресурсы:

Помимо семафорных сигнальных индикаторов существует ещё группа семафорных трендовых индикаторов:

Рис.3 Трендовые сигналы с использованием индикатора Heiken_Ashi_Smoothed 

Рис.3 Семафорный трендовый индикатор

 

Рис.4 Торговый сигнал для сделки с использованием семафорного, трендового индикатора Heiken Ashi Smoothed

Рис.4 Торговый сигнал для сделки с использованием семафорного трендового индикатора Heiken Ashi Smoothed

В торговых системах с использованием таких индикаторов немного меняется код для получения торговых сигналов, но сам код эксперта остаётся практически неизменным. 

Образцы типичных семафорных трендовых индикаторов

В Code Base более чем достаточно таких индикаторов, в контексте данной статьи я сделаю только несколько ссылок на исходные ресурсы:

 

Исходные данные для построения торговой системы: 

  1. Семафорный индикатор с входными параметрами, которые должны присутствовать в эксперте;
  2. Перечень дополнительных, входных, торговых параметров эксперта:
    • доля финансовых ресурсов от депозита в сделке;
    • размер стоплосса и тейкпрофита (при нулевых значениях отложенные ордера не должны использоваться);
    • проскальзывание (максимальная допустимая разница между выставленной ценой сделки и действительной);
    • номер бара, с которого мы будем получать торговые сигналы;
    • разрешения на открытие длинных и коротких позиций;
    • разрешения принудительного закрывания длинных и коротких позиций по сигналам индикатора.

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

Код эксперта, реализующего семафорную торговую систему:

//+------------------------------------------------------------------+
//|                                                 Exp_ASCtrend.mq5 |
//|                             Copyright © 2011,   Nikolay Kositsin | 
//|                              Khabarovsk,   farria@mail.redcom.ru | 
//+------------------------------------------------------------------+
#property copyright "Copyright © 2011, Nikolay Kositsin"
#property link      "farria@mail.redcom.ru"
#property version   "1.00"
//+----------------------------------------------+
//| Входные параметры индикатора эксперта        |
//+----------------------------------------------+
input double MM=-0.1;             //Доля финансовых ресурсов от депозита в сделке, отрицательные значения - размер лота
input int    StopLoss_=1000;      //стоплосс в пунктах
input int    TakeProfit_=2000;    //тейкпрофит в пунктах
input int    Deviation_=10;       //макс. отклонение цены в пунктах
input bool   BuyPosOpen=true;     //Разрешение для входа в лонг
input bool   SellPosOpen=true;    //Разрешение для входа в шорт
input bool   BuyPosClose=true;     //Разрешение для выхода из лонгов
input bool   SellPosClose=true;    //Разрешение для выхода из шортов
//+----------------------------------------------+
//| Входные параметры индикатора ASCtrend        |
//+----------------------------------------------+
input ENUM_TIMEFRAMES InpInd_Timeframe=PERIOD_H1; //таймфрейм индикатора ASCtrend
input int  RISK=4;                                //степень риска
input uint SignalBar=1;                           //номер бара для получения сигнала входа
//+----------------------------------------------+

int TimeShiftSec;
//---- Объявление целых переменных для хендлов индикаторов
int InpInd_Handle;
//---- объявление целых переменных начала отсчета данных
int min_rates_total;
//+------------------------------------------------------------------+
//  Торговые алгоритмы                                               | 
//+------------------------------------------------------------------+
#include <TradeAlgorithms.mqh>
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---- получение хендла индикатора ASCtrend
   InpInd_Handle=iCustom(Symbol(),InpInd_Timeframe,"ASCtrend",RISK);
   if(InpInd_Handle==INVALID_HANDLE) Print(" Не удалось получить хендл индикатора ASCtrend");

//---- инициализация переменной для хранения периода графика в секундах  
   TimeShiftSec=PeriodSeconds(InpInd_Timeframe);

//---- инициализация переменных начала отсчёта данных
   min_rates_total=int(3+RISK*2+SignalBar);
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//----
   GlobalVariableDel_(Symbol());
//----
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---- проверка количества баров на достаточность для расчёта
   if(BarsCalculated(InpInd_Handle)<min_rates_total) return;
   
//---- подгрузка истории для нормальной работы функций IsNewBar() и SeriesInfoInteger()  
   LoadHistory(TimeCurrent()-PeriodSeconds(InpInd_Timeframe)-1,Symbol(),InpInd_Timeframe);

//---- Объявление локальных переменных
   double DnValue[1],UpValue[1];
//---- Объявление статических переменных
   static bool Recount=true;
   static bool BUY_Open=false,BUY_Close=false;
   static bool SELL_Open=false,SELL_Close=false;
   static datetime UpSignalTime,DnSignalTime;
   static CIsNewBar NB;

//+----------------------------------------------+
//| Определение сигналов для сделок              |
//+----------------------------------------------+
   if(!SignalBar || NB.IsNewBar(Symbol(),InpInd_Timeframe) || Recount) // проверка на появление нового бара
     {
      //---- обнулим торговые сигналы
      BUY_Open=false;
      SELL_Open=false;
      BUY_Close=false;
      SELL_Close=false;
      Recount=false;

      //---- копируем вновь появившиеся данные в массивы
      if(CopyBuffer(InpInd_Handle,1,SignalBar,1,UpValue)<=0) {Recount=true; return;}
      if(CopyBuffer(InpInd_Handle,0,SignalBar,1,DnValue)<=0) {Recount=true; return;}

      //---- Получим сигналы для покупки
      if(UpValue[0] && UpValue[0]!=EMPTY_VALUE)
        {
         if(BuyPosOpen) BUY_Open=true;
         if(SellPosClose) SELL_Close=true;
         UpSignalTime=datetime(SeriesInfoInteger(Symbol(),InpInd_Timeframe,SERIES_LASTBAR_DATE))+TimeShiftSec;
        }

      //---- Получим сигналы для продажи
      if(DnValue[0] && DnValue[0]!=EMPTY_VALUE)
        {
         if(SellPosOpen) SELL_Open=true;
         if(BuyPosClose) BUY_Close=true;
         DnSignalTime=datetime(SeriesInfoInteger(Symbol(),InpInd_Timeframe,SERIES_LASTBAR_DATE))+TimeShiftSec;
        }

      //---- Поиск последнего направления торговли для получения сигналов для закрывания позиций
      //if(!MQL5InfoInteger(MQL5_TESTING) && !MQL5InfoInteger(MQL5_OPTIMIZATION)) //если режим торговли в тестере "Произвольная задержка" 
      if((BuyPosOpen && BuyPosClose || SellPosOpen && SellPosClose) && (!BUY_Close && !SELL_Close))
        {
         int Bars_=Bars(Symbol(),InpInd_Timeframe);

         for(int bar=int(SignalBar+1); bar<Bars_; bar++)
           {
            if(SellPosClose)
              {
               if(CopyBuffer(InpInd_Handle,1,bar,1,UpValue)<=0) {Recount=true; return;}
               if(UpValue[0]!=0 && UpValue[0]!=EMPTY_VALUE)
                 {
                  SELL_Close=true;
                  break;
                 }
              }

            if(BuyPosClose)
              {
               if(CopyBuffer(InpInd_Handle,0,bar,1,DnValue)<=0) {Recount=true; return;}
               if(DnValue[0]!=0 && DnValue[0]!=EMPTY_VALUE)
                 {
                  BUY_Close=true;
                  break;
                 }
              }
           }
        }
     }

//+----------------------------------------------+
//| Совершение сделок                            |
//+----------------------------------------------+
//---- Закрываем лонг
   BuyPositionClose(BUY_Close,Symbol(),Deviation_);

//---- Закрываем шорт   
   SellPositionClose(SELL_Close,Symbol(),Deviation_);

//---- Открываем лонг
   BuyPositionOpen(BUY_Open,Symbol(),UpSignalTime,MM,0,Deviation_,StopLoss_,TakeProfit_);

//---- Открываем шорт
   SellPositionOpen(SELL_Open,Symbol(),DnSignalTime,MM,0,Deviation_,StopLoss_,TakeProfit_);
//----
  }
//+------------------------------------------------------------------+
Сам код для реализации подобной идеи получился достаточно простым и понятным, но некоторые детали всё-таки уточним.

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

Функция IsNewBar(), необходимая для определения момента прихода нового бара, реализована в виде класса, размещённого в файле TradeAlgorithms.mqh. При такой реализации можно без проблем использовать любое количество таких функций в коде, задав для каждой из которых свою переменную типа  static CIsNewBar.  

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

Блок "Поиск последнего направления торговли для получения сигналов для закрывания позиций" в функции  OnTick() необходим для выработки сигналов закрывания позиций на барах, на которых отсутствуют торговые сигналы.  При нормальной  работе эксперта потребности в них нет, а вот если возникают обрывы Интернет-соединений, то вполне возможна ситуация, когда возникший торговый сигнал будет пропущен. Открывать торговую позицию задним числом едва ли целесообразно, а вот закрыть всё-таки не помешает.

Использование торговой системы с другими семафорными сигнальными индикаторами

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

  1. Вписать во входные параметры эксперта все необходимые параметры нового индикатора, заменив ими данные предыдущего индикатора;
  2. Изменить код получения хендла этого индикатора в блоке OnInit();
  3. Определить номера для индикаторных буферов, в которых лежат торговые сигналы для покупки и продажи из кода индикатора и вписать их соответственно в блоке OnTick() в вызовы функции CopyBuffer(). В данном случае у нас используется нулевой и первый буферы индикатора;
  4. Изменить инициализацию переменной начала отсчёта данных (min_rates_total) в эксперте в соответствии с кодом индикатора;
  5. Изменить код блока  "Поиск последнего направления торговли для получения сигналов для закрывания позиций " в функции  OnTick() в соответствии с кодом индикатора. 

Использование торговой системы с другими семафорными трендовыми индикаторами

При использовании этой торговой системы с семафорным трендовым индикатором код эксперта в блоке определения сигналов для сделок функции OnTick() немного изменился. Например, для эксперта  на основе индикатора  FiboCandles этот код будет таким:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---- проверка количества баров на достаточность для расчёта
   if(BarsCalculated(InpInd_Handle)<min_rates_total) return;
   
//---- подгрузка истории для нормальной работы функций IsNewBar() и SeriesInfoInteger()  
   LoadHistory(TimeCurrent()-PeriodSeconds(InpInd_Timeframe)-1,Symbol(),InpInd_Timeframe);

//---- Объявление локальных переменных
   double TrendValue[2];
//---- Объявление статических переменных
   static bool Recount=true;
   static bool BUY_Open=false,BUY_Close=false;
   static bool SELL_Open=false,SELL_Close=false;
   static datetime UpSignalTime,DnSignalTime;
   static CIsNewBar NB;

//+----------------------------------------------+
//| Определение сигналов для сделок              |
//+----------------------------------------------+
   if(!SignalBar || NB.IsNewBar(Symbol(),InpInd_Timeframe) || Recount) // проверка на появление нового бара
     {
      //---- обнулим торговые сигналы
      BUY_Open=false;
      SELL_Open=false;
      BUY_Close=false;
      SELL_Close=false;
      Recount=false;

      //---- копируем вновь появившиеся данные в массивы
      if(CopyBuffer(InpInd_Handle,4,SignalBar,2,TrendValue)<=0) {Recount=true; return;}

      //---- Получим сигналы для покупки
      if(TrendValue[0]==1 && TrendValue[1]==0)
        {
         if(BuyPosOpen) BUY_Open=true;
         if(SellPosClose)SELL_Close=true;
         UpSignalTime=datetime(SeriesInfoInteger(Symbol(),InpInd_Timeframe,SERIES_LASTBAR_DATE))+TimeShiftSec;
        }

      //---- Получим сигналы для продажи
      if(TrendValue[0]==0 && TrendValue[1]==1)
        {
         if(SellPosOpen) SELL_Open=true;
         if(BuyPosClose) BUY_Close=true;
         DnSignalTime=datetime(SeriesInfoInteger(Symbol(),InpInd_Timeframe,SERIES_LASTBAR_DATE))+TimeShiftSec;
        }

      //---- Поиск последнего направления торговли для получения сигналов для закрывания позиций
      //if(!MQL5InfoInteger(MQL5_TESTING) && !MQL5InfoInteger(MQL5_OPTIMIZATION)) //если режим торговли в тестере "Произвольная задержка" 
        {
         if(SellPosOpen && SellPosClose  &&  TrendValue[1]==0) SELL_Close=true;
         if(BuyPosOpen  &&  BuyPosClose  &&  TrendValue[1]==1) BUY_Close=true;
        }
     }

//+----------------------------------------------+
//| Совершение сделок                            |
//+----------------------------------------------+
//---- Закрываем лонг
   BuyPositionClose(BUY_Close,Symbol(),Deviation_);

//---- Закрываем шорт   
   SellPositionClose(SELL_Close,Symbol(),Deviation_);

//---- Открываем лонг
   BuyPositionOpen(BUY_Open,Symbol(),UpSignalTime,MM,0,Deviation_,StopLoss_,TakeProfit_);

//---- Открываем шорт
   SellPositionOpen(SELL_Open,Symbol(),DnSignalTime,MM,0,Deviation_,StopLoss_,TakeProfit_);
//----
  }

В данном случае торговые сигналы достаются из всего одного индикаторного буфера, являющимся цветовым (содержащим индексы цвета), данные в котором могут принимать всего два значения: 0 - для растущего рынка и 1 - для падающего. Код блока "Поиск последнего направления торговли для получения сигналов для закрывания позиций " в этом примере максимально упростился по той причине, что направление тренда на любом баре можно непосредственно достать из соответствующей ячейки индикаторного буфера. 

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

 

Тестирование торговой системы

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

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

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

Итак, тестируем эксперта с параметрами по умолчанию на паре EUR/USD за период с начала года и до начала декабря:

 

Рис.5 Результат тестирования эксперта Exp_ASCtrend с параметрами по умолчанию на EUR/USD H1 

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

 

Рис.6 Результат тестирования эксперта Exp_ASCtrend после оптимизации с лучшими параметрами на EUR/USD H1 

Сам процесс оптимизации этой торговой системы никаких особенностей не имеет, так что для уточнения деталей я сделаю только ссылку на  статью, посвящённую подробностям этого процесса: "MQL5: Руководство по тестированию и оптимизации советников".

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

Например, на графике EUR/USD H12 в 2011 году с января и по май месяц действовал восходящий тренд, обнаружить который на этапе его раннего возникновения не составляло особого труда:

Рис.7 График EUR/USD H12 январь/май 2011

Рис.7 График EUR/USD H12 январь/май 2011

Было бы интересно протестировать эксперта на этом интервале времени с параметрами по умолчанию, включив при этом возможность делать только покупки и задействовав при этом только 5% средств от депозита (MM=0.05). Вот результат теста эксперта с такими параметрами на часовом графике:

 

Рис.8 Результат тестирования эксперта Exp_ASCtrend с параметрами по умолчанию на EUR/USD H1 за январь/май 2011 (только лонги, MM=0,05)

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

 

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

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

Предположим, что коллекция торговых модулей для семафорных, сигнальных систем уже есть (MySignals.zip), и хотелось бы сделать аналогичный модуль для какого-нибудь конкретного индикатора. Пусть им будет индикатор BykovTrendSignal.mq5, представляющий собой типичный семафорный сигнальный индикатор. Прежде всего, для этого индикатора нам следовало бы найти максимально близкий аналог индикатора из этой коллекции (Indicators.zip). Методом визуального анализа определяем, что максимально похожим для него будет самый первый индикатор из этой статьи -  ASCtrend.  Торговый модуль именно этого индикатора мы и используем для переделки. 

Сам индикатор в плане его использования в требуемом программном коде представляет собой название - "BykovTrend",  имеет набор входных параметров:

//+----------------------------------------------+
//| Входные параметры индикатора                 |
//+----------------------------------------------+
input int RISK=3;
input int SSP=9;
//+----------------------------------------------+

И нам нужны номера индикаторных буферов, в которых находятся сигналы для совершения сделок. В данном случае это: 0 - для сигналов продажи и 1 - для сигналов покупки.

Теперь, когда мы знаем, какой модуль нам использовать для переделки, мы копируем данный модуль в папке "\MQL5\Include\Expert\Signal\MySignals\" с именем файла BykovTrendSignal.mqh, и после этого открываем его в редакторе MetaEditor. В использованном коде присутствует регулярно встречающееся выражение "ASCtrend" (имя прежнего индикатора), которое нужно заменить на имя нового индикатора "BykovTrend". Для этого одновременно нажимаем две клавиши клавиатуры - "Ctrl" и "H" и производим необходимую замену:

 

Рис.9 Замена названия индикатора в коде торгового модуля 

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

Сначала следовало бы произвести изменения в закомментированном блоке описания класса торговых сигналов для Мастера MQL5:

//+------------------------------------------------------------------+
//| Description of the class                                         |
//| Title=Сигналы, основанные на индикаторе BykovTrend               |
//| Type=SignalAdvanced                                              |
//| Name=BykovTrend                                                  |
//| Class=CBykovTrendSignal                                          |
//| Page=                                                            |
//| Parameter=BuyPosOpen,bool,true,Разрешение для входа в лонг       |
//| Parameter=SellPosOpen,bool,true,Разрешение для входа в шорт      |
//| Parameter=BuyPosClose,bool,true,Разрешение для выхода из лонгов  |
//| Parameter=SellPosClose,bool,true,Разрешение для выхода из шортов |
//| Parameter=Ind_Timeframe,ENUM_TIMEFRAMES,PERIOD_H1,Таймфрейм      |
//| Parameter=RISK,int,4,Степень риска                               |
//| Parameter=SSP,int,9,SSP                                          |
//| Parameter=SignalBar,uint,1,Номер бара для сигнала входа          |
//+------------------------------------------------------------------+
// wizard description end
//+------------------------------------------------------------------+
//| Класс CBykovTrendSignal.                                         |
//| Назначение: Класс генератора торговых сигналов по значениям      |
//| индикатора BykovTrend https://www.mql5.com/ru/code/497/.    |
//|             Является производным от класса CExpertSignal.        |
//+------------------------------------------------------------------+

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

//| Parameter=SSP,int,9,SSP                                          |

И сделана замена ссылки на индикатор в код базе:

//| Назначение: Класс генератора торговых сигналов по значениям      |
//| индикатора BykovTrend https://www.mql5.com/ru/code/497/.    |

Теперь всё, что касается изменений входных переменных, необходимо отразить в самом описании класса торговых сигналов CBykovTrendSignal. У нас появляется строка объявления новой, глобальной переменной класса m_SSP в параметрах настройки:

   uint              m_SSP;              //SSP

 и строка объявления нового метода установки параметров настройки SSP():

   void               SSP(uint value)                         { m_SSP=value;              } 

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

Переходим к конструктору класса CBykovTrendSignal::CBykovTrendSignal(). В этом блоке следует добавить инициализацию новой переменной:

   m_SSP=4;

В блоке проверки параметров настройки CBykovTrendSignal::ValidationSettings() необходимо сделать проверку новой переменной на корректность:

   if(m_SSP<=0)
     {
      printf(__FUNCTION__+": SSP должен быть больше нуля");
      return(false);
     }

После этого можно переходить в блок инициализации индикатора BykovTrend -  CBykovTrendSignal::InitBykovTrend(). Новый индикатор имеет другое количество входных переменных и поэтому размерность  для объявляемого массива входных параметров будет другая:

//--- задание параметров индикатора
   MqlParam parameters[3];

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

Теперь следует проинициализировать новую ячейку массива входных параметров, указав при этом тип переменной, которая будет в ней храниться:

   parameters[2].type=TYPE_INT;
   parameters[2].integer_value=m_SSP;

 После этого в этом блоке изменить количество входных переменных на 3 в самом вызове для инициализации индикатора:

//--- инициализация объекта   
   if(!m_indicator.Create(m_symbol.Name(),m_Ind_Timeframe,IND_CUSTOM,3,parameters))

Количество индикаторных буферов в индикаторе остаётся прежним и равным двум, так что в строке инициализации количества индикаторных буферов в нашем случае ничего менять нет необходимости: 

//--- количество буферов
   if(!m_indicator.NumBuffers(2))  return(false);

Индикаторы  ASCtrend и BykovTrend имеют по два индикаторных буфера, причём назначение этих буферов абсолютно одинаковое.  Нулевой буфер предназначен для хранения сигналов на продажу, а буфер с индексом 1 -  для хранения сигналов на покупку.  Так что в блоках функций для подачи торговых сигналов CBykovTrendSignal::LongCondition() и CBykovTrendSignal::ShortCondition() ничего менять не нужно и работу по переделке модуля торговых сигналов можно считать законченной.

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

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

Напоследок мне бы хотелось остановиться на ещё одном моменте особенностей написания модулей торговых сигналов. Иногда в качестве типов для входных переменных модуля код исходного индикатора содержит пользовательские перечисления, например для индикатора Candles_Smoothed типом переменной MA_SMethod оказывается пользовательское перечисление Smooth_Method:

//+-----------------------------------+
//|  объявление перечислений          |
//+-----------------------------------+
enum Smooth_Method
  {
   MODE_SMA_,  // SMA
   MODE_EMA_,  // EMA
   MODE_SMMA_, // SMMA
   MODE_LWMA_, // LWMA
   MODE_JJMA,  // JJMA
   MODE_JurX,  // JurX
   MODE_ParMA, // ParMA
   MODE_T3,    // T3
   MODE_VIDYA, // VIDYA
   MODE_AMA,   // AMA
  }; */
//+----------------------------------------------+
//| Входные параметры индикатора                 |
//+----------------------------------------------+
input Smooth_Method MA_SMethod=MODE_LWMA; // Метод усреднения
input int MA_Length=30;                   // Глубина усреднения                    
input int MA_Phase=100;                   // Параметр усреднения
                                          // для JJMA изменяющийся в пределах -100 ... +100,
                                          // для VIDIA это период CMO, для AMA это период медленной скользящей
//+----------------------------------------------+

В такой ситуации такие входные переменные и всё что с ними связано в модуле торговых сигналов (Candles_SmoothedSignal.mqh) следует преобразовать в переменные  типа int или uint. А для удобства использования этих входных переменных в уже сгенерированном коде готового эксперта произвести обратную процедуру объявления пользовательских перечислений до входных параметров эксперта и замену типов необходимых, входных переменных (эксперт ExpM_Candles_Smoothed.):

//+------------------------------------------------------------------+
//|  объявление перечислений                                         |
//+------------------------------------------------------------------+
enum Smooth_Method
  {
   MODE_SMA_,  // SMA
   MODE_EMA_,  // EMA
   MODE_SMMA_, // SMMA
   MODE_LWMA_, // LWMA
   MODE_JJMA,  // JJMA
   MODE_JurX,  // JurX
   MODE_ParMA, // ParMA
   MODE_T3,    // T3
   MODE_VIDYA, // VIDYA
   MODE_AMA,   // AMA
  };
//+------------------------------------------------------------------+
//| Inputs                                                           |
//+------------------------------------------------------------------+
//--- inputs for expert
input string          Expert_Title         ="Candles_Smoothed"; // Document name
ulong                 Expert_MagicNumber   =29976;              // 
bool                  Expert_EveryTick     =false;              // 
//--- inputs for main signal
input int             Signal_ThresholdOpen =40;                 // Signal threshold value to open [0...100]
input int             Signal_ThresholdClose=20;                 // Signal threshold value to close [0...100]
input double          Signal_PriceLevel    =0.0;                // Price level to execute a deal
input double          Signal_StopLevel     =50.0;               // Stop Loss level (in points)
input double          Signal_TakeLevel     =50.0;               // Take Profit level (in points)
input int             Signal_Expiration    =1;                  // Expiration of pending orders (in bars)
input bool            Signal__BuyPosOpen   =true;               // Candles_Smoothed() Разрешение для входа в лонг
input bool            Signal__SellPosOpen  =true;               // Candles_Smoothed() Разрешение для входа в шорт
input bool            Signal__BuyPosClose  =true;               // Candles_Smoothed() Разрешение для выхода из лонгов
input bool            Signal__SellPosClose =true;               // Candles_Smoothed() Разрешение для выхода из шортов
input ENUM_TIMEFRAMES Signal__Ind_Timeframe=PERIOD_H1;            // Candles_Smoothed() Таймфрейм
input Smooth_Method   Signal__MA_SMethod   =4;                  // Candles_Smoothed() Метод усреднения (1 - 10)
input uint            Signal__MA_Length    =30;                 // Candles_Smoothed() Глубина усреднения
input uint            Signal__MA_Phase     =100;                // Candles_Smoothed() Параметр усреднения
input uint            Signal__SignalBar    =1;                  // Candles_Smoothed() Номер бара для сигнала входа
input double          Signal__Weight       =1.0;                // Candles_Smoothed() Weight [0...1.0]
//--- inputs for money
input double          Money_FixLot_Percent =10.0;               // Percent
input double          Money_FixLot_Lots    =0.1;                // Fixed volume

В данном случае это сделано с входной переменной Signal__MA_SMethod.  

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

Заключение

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

Для тех, кто выбрал для себя в качестве основы для построения своей торговой системы возможности  генератора торговых стратегий, все предложенные эксперты дополнительно представлены торговыми модулями. Эти модули расположены в архиве MySignals.zip, а сгенерированные на их основе торговые системы вы найдете в архиве  Expertsez.zip. Индикаторы, используемые в экспертах, расположены в архиве Indicators.zip. Пути для распаковки файлов:

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

Файл  SmoothAlgorithms.mqh  необходим для компиляции  некоторых  индикаторов  из архива  Indicators.zip, а файл TradeAlgorithms.mqh  для компиляции всех экспертов из архива Experts.zip.