Мастер MQL5 Wizard, который открывается с помощью кнопки New панели инструментов редактора MetaEditor, позволяет сгенерировать код эксперта на основе готовых модулей – сигналов, управления капиталом и трейлинг-стопа.
Модуль сигнала добавляется с помощью кнопки Add.
Файлы модулей сигналов – это включаемые файлы Include (*.mqh), расположенные в папке MQL5\Include\Expert\Signal.
В качестве примера выберем сигнал MACD и сигнал PSAR.
Если посмотреть файлы SignalMACD.mqh и SignalSAR.mqh папки MQL5\Include\Expert\Signal, сигнал MACD имеет 5 моделей прогноза цены:
Модель 0 – «осциллятор имеет необходимое направление» – значимость 10.
Модель 1 – «разворот осциллятора в нужном направлении» – значимость 30.
Модель 2 – «пересечение основной и сигнальной линии» – значимость 80.
Модель 3 – «пересечение главной линией нулевого уровня» – значимость 50.
Модель 4 – «дивергенция осциллятора и цены» – значимость 60.
Модель 5 – «двойная дивергенция осциллятора и цены» – значимость 100.
Сигнал SAR имеет 2 модели прогноза цены:
Модель 0 – «параболик находится на нужной стороне цены» – значимость 40.
Модель 1 – «параболик переключается на другую сторону цены» – значимость 90.
Если модель дает сигнал на падение цены – значимость отрицательная, если на рост цены – значимость положительная.
Итоговый прогноз двух модулей будет рассчитываться по формуле:
Итоговый Прогноз = (Прогноз MACD + Прогноз SAR) /2
Где Прогноз MACD = Вес сигнала MACD * значимость Модели MACD,
Прогноз SAR = Вес сигнала SAR * значимость Модели SAR
В нашем случае мы установили весы сигналов равными единице.
Если итоговый прогноз превысит пороговое значение, эксперт совершит сделку на покупку или продажу.
После определения сигналов эксперта определяется алгоритм сопровождения открытой позиции.
– Сопровождение открытой позиции на фиксированном «расстоянии» (в пунктах) – уровни Stop Loss и Take Profit открытой позиции перемещаются на фиксированное расстояние по движению цены в направлении открытой позиции. Когда цена перемещается в направлении открытой позиции на расстояние, которое превышает количество пунктов, соответствующих уровню Trailing Stop Level, эксперт изменяет значения уровней Stop Loss и Take Profit (если Trailing Profit Level> 0).
– Сопровождение открытой позиции по значениям скользящей средней на предыдущем баре.
– Сопровождение открытой позиции по значениям индикатора Parabolic SAR на предыдущем баре.
Файлы реализации алгоритма сопровождения открытой позиции находятся в папке MQL5\Include\Expert\Trailing.
После определения алгоритма сопровождения открытой позиции устанавливается алгоритм управления капиталом и рисками.
– Торговля с фиксированным лотом.
– Торговля с фиксированным уровнем маржи. Значение лота вычисляется функцией MaxLotCheck, которая возвращает максимально возможный объем торговой операции на основе доли свободной маржи (здесь по умолчанию 10%).
– Торговля с фиксированным уровнем риска. Значение лота вычисляется как отношение доли баланса, выделенной для риска, к StopLoss.
– Торговля минимальным лотом.
– Торговля объемом, определяемым результатами предыдущих сделок. Здесь сначала значение лота вычисляется функцией MaxLotCheck, которая возвращает максимально возможный объем торговой операции на основе доли свободной маржи (здесь по умолчанию 10%). Затем в случае получения убытка лот уменьшается на фактор Decrease Factor (по умолчанию 3).
Если образуется убыток, равный указанному проценту от текущего капитала, производится принудительное закрытие убыточной позиции.
Файлы реализации алгоритма управления капиталом и рисками находятся в папке MQL5\Include\Expert\Money.
После выбора алгоритма управления капиталом и рисками генерируется код эксперта.
Код эксперта основан на использовании экземпляра класса CExpert, файл которого находится в папке MQL5\Include\Expert.
Пороговые значения Signal_ThresholdOpen и Signal_ThresholdClose итогового прогноза сигналов по умолчанию равны 10.
Тестирование этого эксперта с сигналами MACD и PSAR на часовом графике EUR/USD с разными алгоритмами трейлинга и манименеджмента дает отрицательное матожидание выигрыша. Ничего не дает и оптимизация таких параметров, как Trailing_FixedPips_StopLevel, Signal_MACD_Weight и Signal_SAR_Weight. Эксперт остается убыточным.
Попробуем создать эксперт и включить в него все стандартные сигналы мастера MQL5 Wizard.
При тестировании на часовом графике EUR/USD такой эксперт с равными весами сигналов также покажет отрицательное матожидание выигрыша.
Однако при оптимизации весов сигналов, эксперт выйдет в плюс, и будут видны комбинации сигналов со значимыми весами, дающие наилучший результат. Также можно провести оптимизацию пороговых значений прогноза вместе с оптимизацией весов сигналов.
На основании полученных комбинаций сигналов, выводящих советник в прибыль, уже можно строить торговые системы.
Модульная структура эксперта, генерируемого мастером MQL5 Wizard, позволяет включить в советник свой собственный модуль сигналов торговой системы.
В качестве примера рассмотрим создание модуля сигналов на основе торговой системы Сидуса.
В редакторе MetaEditor нажмем кнопку New и в мастере MQL5 Wizard создадим включаемый файл.
Который поместим в папку MQL5\Include\Expert\Signal.
Включаемый файл должен содержать класс, расширяющий класс CExpertSignal. Поэтому в код необходимо включить файл ExpertSignal.mqh класса CExpertSignal.
#include <Expert\ExpertSignal.mqh>
Далее должна присутствовать информация о модуле сигналов, предназначенная для мастера MQL5 Wizard, которая используется для распознавания модуля сигналов мастером MQL5 Wizard при создании эксперта в окне добавления сигналов, а также при генерации самого кода эксперта. Без этой информации мастер MQL5 Wizard просто не добавит сигнал в свой интерфейс, хотя файл сигнала и будет расположен в папке MQL5\Include\Expert\Signal. После создания модуля сигналов редактор MetaEditor необходимо перезагрузить, чтобы сигнал добавился в MQL5 Wizard.
// wizard description start
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
//| Description of the class |
//| Title=Signals of the system «Sidus’ |
//| Type=SignalAdvanced |
//| Name=Sidus |
//| ShortName=MA |
//| Class=CSignalSidus |
//| Page= |
//| Parameter=NumberOpenPosition, int,5,Bars number checked to cross |
//| Parameter=Pattern_0,int,80,Model 0 |
//| Parameter=Pattern_1,int,20,Model 1 |
//| Parameter=Pattern_2,int,80,Model 2 |
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
// wizard description end
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
//| Class CSignalSidus. |
//| Purpose: Class of generator of trade signals based on |
//| the «Sidus’ system. |
//| Is derived from the CExpertSignal class. |
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
Здесь строка Title отображается в списке сигналов окна MQL5 Wizard, строка Page указывает на раздел справки и должна быть пустой, строки Parameter описывают методы установки параметров сигнала и используются при генерации кода советника.
Далее идет объявление параметров и методов класса модуля сигналов.
class CSignalSidus: public CExpertSignal
{
protected:
CiMA m_ma18; // object-indicator
CiMA m_ma28; // object-indicator
CiMA m_ma5; // object-indicator
CiMA m_ma8; // object-indicator
// – - adjusted parameters
int m_numberOpenPosition;
// – - «weights» of market models (0—100)
int m_pattern_0; // model 0 «5 WMA and 8 WMA cross the 18 EMA and the 28 EMA upward or top down» 80
int m_pattern_1; // model 1 «5 WMA crosses the 8 WMA upward or top down» 10
int m_pattern_2; // model 2 «18 EMA crosses the 28 EMA upward or top down» 80
public:
CSignalSidus (void);
~CSignalSidus (void);
// – - methods of setting adjustable parameters
void NumberOpenPosition (int value) {m_numberOpenPosition=value;}
// – - methods of adjusting «weights» of market models
void Pattern_0 (int value) {m_pattern_0=value;}
void Pattern_1 (int value) {m_pattern_1=value;}
void Pattern_2 (int value) {m_pattern_2=value;}
// – - method of verification of settings
virtual bool ValidationSettings (void);
// – - method of creating the indicator and timeseries
virtual bool InitIndicators (CIndicators *indicators);
// – - methods of checking if the market models are formed
virtual int LongCondition (void);
virtual int ShortCondition (void);
protected:
// – - method of initialization of the indicator
bool InitMA (CIndicators *indicators);
};
Здесь объекты m_ma* представляют скользящие средние, используемые системой Сидуса, параметр m_numberOpenPosition представляет количество баров, на которых будет проверяться пересечение скользящих средних.
Для генерации сигналов определим три модели прогноза цены – пересечение скользящих средних 5WMA и 8 WMA скользящих средних 18 ЕМА и 28 ЕМА, пересечение скользящей средней 5WMA скользящую среднюю 8 WMA, пересечение скользящей средней 18 ЕМА скользящую среднюю 28 ЕМА.
Метод ValidationSettings проверяет на корректность параметры сигнала, метод InitIndicators инициализирует объекты m_ma*.
Эксперт получает сигналы модуля с помощью методов CheckOpenLong. CheckOpenShort, CheckCloseLong, CheckCloseShort, CheckReverseLong, CheckReverseShort класса CExpertSignal.
Однако эти методы, в свою очередь, формируют свои возвращаемые значения на основе значений, которые возвращаются методами LongCondition и ShortCondition.
Методы LongCondition и ShortCondition как раз и оперируют моделями прогноза цены. Потому в модуле сигналов достаточно определить эти два метода.
Определим конструктор класса и его методы.
CSignalSidus::CSignalSidus (void): m_numberOpenPosition (5),
m_pattern_0 (80),
m_pattern_1 (10),
m_pattern_2 (80)
{
// – - initialization of protected data
m_used_series=USE_SERIES_OPEN+USE_SERIES_HIGH+USE_SERIES_LOW+USE_SERIES_CLOSE;
}
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
//| Destructor |
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
CSignalSidus::~CSignalSidus (void)
{
}
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
//| Validation settings protected data. |
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
bool CSignalSidus::ValidationSettings (void)
{
// – - validation settings of additional filters
if (!CExpertSignal::ValidationSettings ())
return (false);
// – - ok
return (true);
}
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
//| Create indicators. |
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
bool CSignalSidus::InitIndicators (CIndicators *indicators)
{
// – - check pointer
if (indicators==NULL)
return (false);
// – - initialization of indicators and timeseries of additional filters
if (!CExpertSignal::InitIndicators (indicators))
return (false);
// – - create and initialize MA indicator
if (!InitMA (indicators))
return (false);
// – - ok
return (true);
}
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
//| Initialize MA indicators. |
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
bool CSignalSidus::InitMA (CIndicators *indicators)
{
// – - check pointer
if (indicators==NULL)
return (false);
// – - add object to collection
if(!indicators.Add (GetPointer (m_ma18)))
{
printf (__FUNCTION__+": error adding object»);
return (false);
}
// – - initialize object
if(!m_ma18.Create(m_symbol.Name (),m_period,18,0,MODE_EMA, PRICE_WEIGHTED))
{
printf (__FUNCTION__+": error initializing object»);
return (false);
}
// – - add object to collection
if(!indicators.Add (GetPointer (m_ma28)))
{
printf (__FUNCTION__+": error adding object»);
return (false);
}
// – - initialize object
if(!m_ma28.Create(m_symbol.Name (),m_period,28,0,MODE_EMA, PRICE_WEIGHTED))
{
printf (__FUNCTION__+": error initializing object»);
return (false);
}
// – - add object to collection
if(!indicators.Add (GetPointer (m_ma5)))
{
printf (__FUNCTION__+": error adding object»);
return (false);
}
// – - initialize object
if(!m_ma5.Create(m_symbol.Name (),m_period,5,0,MODE_LWMA, PRICE_WEIGHTED))
{
printf (__FUNCTION__+": error initializing object»);
return (false);
}
// – - add object to collection
if(!indicators.Add (GetPointer (m_ma8)))
{
printf (__FUNCTION__+": error adding object»);
return (false);
}
// – - initialize object
if(!m_ma8.Create(m_symbol.Name (),m_period,8,0,MODE_LWMA, PRICE_WEIGHTED))
{
printf (__FUNCTION__+": error initializing object»);
return (false);
}
// – - ok
return (true);
}
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
//| «Voting» that price will grow. |
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
int CSignalSidus::LongCondition (void)
{
int result=0;
int idx=StartIndex ();
if(m_ma5.Main(idx)>m_ma8.Main(idx)&&m_ma8.Main(idx)>m_ma18.Main(idx)&&m_ma8.Main(idx)>m_ma28.Main (idx)) {
bool flagCross1=false;
bool flagCross2=false;
for (int i= (idx+1);i <m_numberOpenPosition; i++) {
if(m_ma5.Main(i)<m_ma18.Main(i)&&m_ma5.Main(i)<m_ma28.Main (i)) {
flagCross1=true;
}
if(m_ma8.Main(i)<m_ma18.Main(i)&&m_ma8.Main(i)<m_ma28.Main (i)) {
flagCross2=true;
}
}
if (flagCross1==true&&flagCross2==true) {
result=m_pattern_0;
}
}
if(m_ma5.Main(idx)>m_ma8.Main (idx)) {
bool flagCross=false;
for (int i= (idx+1);i <m_numberOpenPosition; i++) {
if(m_ma5.Main(i)<m_ma8.Main (i)) {
flagCross=true;
}
}
if (flagCross==true) {
result=m_pattern_1;
}
}
if(m_ma18.Main(idx)>m_ma28.Main (idx)) {
bool flagCross=false;
for (int i= (idx+1);i <m_numberOpenPosition; i++) {
if(m_ma18.Main(i)<m_ma28.Main (i)) {
flagCross=true;
}
}
if (flagCross==true) {
result=m_pattern_2;
}
}
return result;
}
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
//| «Voting» that price will fall. |
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
int CSignalSidus::ShortCondition (void)
{
int result=0;
int idx =StartIndex ();
if(m_ma5.Main(idx)<m_ma8.Main(idx)&&m_ma8.Main(idx)<m_ma18.Main(idx)&&m_ma8.Main(idx)<m_ma28.Main (idx)) {
bool flagCross1=false;
bool flagCross2=false;
for (int i= (idx+1);i <m_numberOpenPosition; i++) {
if(m_ma5.Main(i)>m_ma18.Main(i)&&m_ma5.Main(i)>m_ma28.Main (i)) {
flagCross1=true;
}
if(m_ma8.Main(i)>m_ma18.Main(i)&&m_ma8.Main(i)>m_ma28.Main (i)) {
flagCross2=true;
}
}
if (flagCross1==true&&flagCross2==true) {
result=m_pattern_0;
}
}
if(m_ma5.Main(idx)<m_ma8.Main (idx)) {
bool flagCross=false;
for (int i= (idx+1);i <m_numberOpenPosition; i++) {
if(m_ma5.Main(i)>m_ma8.Main (i)) {
flagCross=true;
}
}
if (flagCross==true) {
result=m_pattern_1;
}
}
if(m_ma18.Main(idx)<m_ma28.Main (idx)) {
bool flagCross=false;
for (int i= (idx+1);i <m_numberOpenPosition; i++) {
if(m_ma18.Main(i)>m_ma28.Main (i)) {
flagCross=true;
}
}
if (flagCross==true) {
result=m_pattern_2;
}
}
return result;
}
С помощью мастера MQL5 Wizard сгенерируем код эксперта на основе созданного модуля сигналов.
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
//| Include |
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
#include <Expert\Expert.mqh>
// – - available signals
#include <Expert\Signal\SignalSidus.mqh>
// – - available trailing
#include <Expert\Trailing\TrailingFixedPips.mqh>
// – - available money management
#include <Expert\Money\MoneyFixedLot.mqh>
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
//| Inputs |
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
// – - inputs for expert
input string Expert_Title =«EMTSSGeneratedSidus»; // Document name
ulong Expert_MagicNumber =7506; //
bool Expert_EveryTick =false; //
// – - inputs for main signal
input int Signal_ThresholdOpen =20; // 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 =4; // Expiration of pending orders (in bars)
input int Signal_MA_NumberOpenPosition =5; // Sidus (5,80,20,80) Bars number checked to cross
input int Signal_MA_Pattern_0 =80; // Sidus (5,80,20,80) Model 0
input int Signal_MA_Pattern_1 =20; // Sidus (5,80,20,80) Model 1
input int Signal_MA_Pattern_2 =80; // Sidus (5,80,20,80) Model 2
input double Signal_MA_Weight =1.0; // Sidus (5,80,20,80) Weight [0…1.0]
// – - inputs for trailing
input int Trailing_FixedPips_StopLevel =30; // Stop Loss trailing level (in points)
input int Trailing_FixedPips_ProfitLevel=50; // Take Profit trailing level (in points)
// – - inputs for money
input double Money_FixLot_Percent =10.0; // Percent
input double Money_FixLot_Lots =0.1; // Fixed volume
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
//| Global expert object |
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
CExpert ExtExpert;
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
//| Initialization function of the expert |
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
int OnInit ()
{
// – - Initializing expert
if(!ExtExpert.Init (Symbol (),Period (),Expert_EveryTick, Expert_MagicNumber))
{
// – - failed
printf (__FUNCTION__+": error initializing expert»);
ExtExpert.Deinit ();
return (INIT_FAILED);
}
// – - Creating signal
CExpertSignal *signal=new CExpertSignal;
if (signal==NULL)
{
// – - failed
printf (__FUNCTION__+": error creating signal»);
ExtExpert.Deinit ();
return (INIT_FAILED);
}
// – —
ExtExpert.InitSignal (signal);
signal.ThresholdOpen (Signal_ThresholdOpen);
signal.ThresholdClose (Signal_ThresholdClose);
signal.PriceLevel (Signal_PriceLevel);
signal.StopLevel (Signal_StopLevel);
signal. TakeLevel (Signal_TakeLevel);
signal. Expiration (Signal_Expiration);
// – - Creating filter CSignalSidus
CSignalSidus *filter0=new CSignalSidus;
if (filter0==NULL)
{
// – - failed
printf (__FUNCTION__+": error creating filter0»);
ExtExpert.Deinit ();
return (INIT_FAILED);
}
signal.AddFilter (filter0);
// – - Set filter parameters
filter0.NumberOpenPosition (Signal_MA_NumberOpenPosition);
filter0.Pattern_0 (Signal_MA_Pattern_0);
filter0.Pattern_1 (Signal_MA_Pattern_1);
filter0.Pattern_2 (Signal_MA_Pattern_2);
filter0.Weight (Signal_MA_Weight);
// – - Creation of trailing object
CTrailingFixedPips *trailing=new CTrailingFixedPips;
if (trailing==NULL)
{
// – - failed
printf (__FUNCTION__+": error creating trailing»);
ExtExpert.Deinit ();
return (INIT_FAILED);
}
// – - Add trailing to expert (will be deleted automatically))
if(!ExtExpert.InitTrailing (trailing))
{
// – - failed
printf (__FUNCTION__+": error initializing trailing»);
ExtExpert.Deinit ();
return (INIT_FAILED);
}
// – - Set trailing parameters
trailing.StopLevel (Trailing_FixedPips_StopLevel);
trailing.ProfitLevel (Trailing_FixedPips_ProfitLevel);
// – - Creation of money object
CMoneyFixedLot *money=new CMoneyFixedLot;
if (money==NULL)
{
// – - failed
printf (__FUNCTION__+": error creating money»);
ExtExpert.Deinit ();
return (INIT_FAILED);
}
// – - Add money to expert (will be deleted automatically))
if(!ExtExpert.InitMoney (money))
{
// – - failed
printf (__FUNCTION__+": error initializing money»);
ExtExpert.Deinit ();
return (INIT_FAILED);
}
// – - Set money parameters
money.Percent (Money_FixLot_Percent);
money. Lots (Money_FixLot_Lots);
// – - Check all trading objects parameters
if(!ExtExpert.ValidationSettings ())
{
// – - failed
ExtExpert.Deinit ();
return (INIT_FAILED);
}
// – - Tuning of all necessary indicators
if(!ExtExpert.InitIndicators ())
{
// – - failed
printf (__FUNCTION__+": error initializing indicators»);
ExtExpert.Deinit ();
return (INIT_FAILED);
}
// – - ok
return (INIT_SUCCEEDED);
}
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
//| Deinitialization function of the expert |
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
void OnDeinit (const int reason)
{
ExtExpert.Deinit ();
}
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
//| «Tick» event handler function |
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
void OnTick ()
{
ExtExpert. OnTick ();
}
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
//| «Trade» event handler function |
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
void OnTrade ()
{
ExtExpert. OnTrade ();
}
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
//| «Timer» event handler function |
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
void OnTimer ()
{
ExtExpert. OnTimer ();
}
//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +
При оптимизации параметров данного эксперта на часовом графике EUR/USD получим, что эксперт лучше всего работает со следующими значениями параметров:
input int Signal_ThresholdOpen =20; // Signal threshold value to open [0…100]
input int Signal_ThresholdClose =20; // Signal threshold value to close [0…100]
input int Signal_MA_NumberOpenPosition =3; // Sidus (5,80,20,80) Bars number checked to cross
input int Signal_MA_Pattern_0 =60; // Sidus (5,80,20,80) Model 0
input int Signal_MA_Pattern_1 =10; // Sidus (5,80,20,80) Model 1
input int Signal_MA_Pattern_2 =100; // Sidus (5,80,20,80) Model 2
input int Trailing_FixedPips_StopLevel =100; // Stop Loss trailing level (in points)