Книга: Язык программирования MQL5: Продвинутое использование торговой платформы MetaTrader 5
Назад: Создание эксперта с помощью мастера MQL5 Wizard
Дальше: Генетические алгоритмы

Создание индикатора на основе модулей торговых сигналов эксперта

В качестве примера создадим индикатор на основе двух модулей сигналов SignalMA и SignalMACD, файлы которых находятся в каталоге MQL5\Include\Expert\Signal.

В мастере MQL5 Wizard создадим основу индикатора, выбрав вариант Custom Indicator.

Теперь нам нужно указать свойства индикатора, а также определить функции OnInit и OnCalculate.

При создании индикатора на основе модулей торговых сигналов эксперта будем опираться на код сгенерированного с помощью MQL5 Wizard эксперта.

При работе такого эксперта, в функции OnTick, вызывается функция OnTick класса CExpert.

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

Так как функция Refresh является защищенной, а нам нужно обновлять данные модулей торговых сигналов в нашей функции OnCalculate, создадим свой класс CExpertInd, расширяющий класс CExpert.

#include <Expert\Expert.mqh>



class CExpertInd: public CExpert

{

public:

virtual bool RefreshInd (void);

};



//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +

//| Refreshing data for processing |

//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +

bool CExpertInd::RefreshInd (void)

{

MqlDateTime time;

// – - refresh rates

if(!m_symbol.RefreshRates ())

return (false);

// – - check need processing

TimeToStruct (m_symbol. Time (),time);

if (m_period_flags!=WRONG_VALUE && m_period_flags!=0)

if ((m_period_flags&TimeframesFlags (time)) ==0)

return (false);

m_last_tick_time=time;

// – - refresh indicators

m_indicators.Refresh ();

// – - ok

return (true);

}

Посмотрев на классы CSignalMA и CSignalMACD, мы увидим, что функции LongCondition и ShortCondition, дающие рыночные модели, вызываются на текущем баре. Нам же нужно вызывать эти функции на всей истории символа. Кроме того, нам нужна функция BarsCalculated, возвращающая количество рассчитанных значений в модуле сигналов. Поэтому создадим классы CSignalMAInd и CSignalMACDInd, расширяющие классы CSignalMA и CSignalMACD.

#include <Expert\Signal\SignalMA.mqh>

class CSignalMAInd: public CSignalMA

{

public:

virtual int BarsCalculatedInd ();

virtual int LongConditionInd (int ind);

virtual int ShortConditionInd (int ind);

};



//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +

//| Refresh indicators. |

//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +

int CSignalMAInd:: BarsCalculatedInd () {

return m_ma.BarsCalculated ();

}



//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +

//| «Voting» that price will grow. |

//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +

int CSignalMAInd::LongConditionInd (int idx)

{



int result=0;



// – - analyze positional relationship of the close price and the indicator at the first analyzed bar

if (DiffCloseMA (idx) <0.0)

{

// – - the close price is below the indicator

if (IS_PATTERN_USAGE (1) && DiffOpenMA (idx)> 0.0 && DiffMA (idx)> 0.0)

{

// – - the open price is above the indicator (i.e. there was an intersection), but the indicator is directed upwards

result=m_pattern_1;

// – - consider that this is an unformed «piercing» and suggest to enter the market at the current price

m_base_price=0.0;

}

}

else

{

// – - the close price is above the indicator (the indicator has no objections to buying)

if (IS_PATTERN_USAGE (0))

result=m_pattern_0;

// – - if the indicator is directed upwards

if (DiffMA (idx)> 0.0)

{

if (DiffOpenMA (idx) <0.0)

{

// – - if the model 2 is used

if (IS_PATTERN_USAGE (2))

{

// – - the open price is below the indicator (i.e. there was an intersection)

result=m_pattern_2;

// – - suggest to enter the market at the «roll back»

m_base_price=m_symbol.NormalizePrice (MA (idx));

}

}

else

{

// – - if the model 3 is used and the open price is above the indicator

if (IS_PATTERN_USAGE (3) && DiffLowMA (idx) <0.0)

{

// – - the low price is below the indicator

result=m_pattern_3;

// – - consider that this is a formed «piercing» and suggest to enter the market at the current price

m_base_price=0.0;

}

}

}

}

// – - return the result

return (result);

}

//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +

//| «Voting» that price will fall. |

//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +

int CSignalMAInd::ShortConditionInd (int idx)

{

int result=0;



// – - analyze positional relationship of the close price and the indicator at the first analyzed bar

if (DiffCloseMA (idx)> 0.0)

{

// – - the close price is above the indicator

if (IS_PATTERN_USAGE (1) && DiffOpenMA (idx) <0.0 && DiffMA (idx) <0.0)

{

// – - the open price is below the indicator (i.e. there was an intersection), but the indicator is directed downwards

result=m_pattern_1;

// – - consider that this is an unformed «piercing» and suggest to enter the market at the current price

m_base_price=0.0;

}

}

else

{

// – - the close price is below the indicator (the indicator has no objections to buying)

if (IS_PATTERN_USAGE (0))

result=m_pattern_0;

// – - the indicator is directed downwards

if (DiffMA (idx) <0.0)

{

if (DiffOpenMA (idx)> 0.0)

{

// – - if the model 2 is used

if (IS_PATTERN_USAGE (2))

{

// – - the open price is above the indicator (i.e. there was an intersection)

result=m_pattern_2;

// – - suggest to enter the market at the «roll back»

m_base_price=m_symbol.NormalizePrice (MA (idx));

}

}

else

{

// – - if the model 3 is used and the open price is below the indicator

if (IS_PATTERN_USAGE (3) && DiffHighMA (idx)> 0.0)

{

// – - the high price is above the indicator

result=m_pattern_3;

// – - consider that this is a formed «piercing» and suggest to enter the market at the current price

m_base_price=0.0;

}

}

}

}

// – - return the result

return (result);

}

//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +

#include <Expert\Signal\SignalMACD.mqh>



class CSignalMACDInd: public CSignalMACD

{

public:

virtual int BarsCalculatedInd ();

virtual int LongConditionInd (int ind);

virtual int ShortConditionInd (int ind);



};

//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +

//| Refresh indicators. |

//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +

int CSignalMACDInd:: BarsCalculatedInd () {

return m_MACD.BarsCalculated ();

}



//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +

//| «Voting» that price will grow. |

//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +

int CSignalMACDInd::LongConditionInd (int idx)

{

int result=0;



// – - check direction of the main line

if (DiffMain (idx)> 0.0)

{

// – - the main line is directed upwards, and it confirms the possibility of price growth

if (IS_PATTERN_USAGE (0))

result=m_pattern_0; // «confirming» signal number 0

// – - if the model 1 is used, look for a reverse of the main line

if (IS_PATTERN_USAGE (1) && DiffMain (idx+1) <0.0)

result=m_pattern_1; // signal number 1

// – - if the model 2 is used, look for an intersection of the main and signal line

if (IS_PATTERN_USAGE (2) && State (idx)> 0.0 && State (idx+1) <0.0)

result=m_pattern_2; // signal number 2

// – - if the model 3 is used, look for an intersection of the main line and the zero level

if (IS_PATTERN_USAGE (3) && Main (idx)> 0.0 && Main (idx+1) <0.0)

result=m_pattern_3; // signal number 3

// – - if the models 4 or 5 are used and the main line turned upwards below the zero level, look for divergences

if ((IS_PATTERN_USAGE (4) || IS_PATTERN_USAGE (5)) && Main (idx) <0.0)

{

// – - perform the extended analysis of the oscillator state

ExtState (idx);

// – - if the model 4 is used, look for the «divergence» signal

if (IS_PATTERN_USAGE (4) && CompareMaps (1,1)) // 0000 0001b

result=m_pattern_4; // signal number 4

// – - if the model 5 is used, look for the «double divergence» signal

if (IS_PATTERN_USAGE (5) && CompareMaps (0x11,2)) // 0001 0001b

return (m_pattern_5); // signal number 5

}

}

// – - return the result

return (result);

}

//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +

//| «Voting» that price will fall. |

//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +

int CSignalMACDInd::ShortConditionInd (int idx)

{

int result=0;



// – - check direction of the main line

if (DiffMain (idx) <0.0)

{

// – - main line is directed downwards, confirming a possibility of falling of price

if (IS_PATTERN_USAGE (0))

result=m_pattern_0; // «confirming» signal number 0

// – - if the model 1 is used, look for a reverse of the main line

if (IS_PATTERN_USAGE (1) && DiffMain (idx+1)> 0.0)

result=m_pattern_1; // signal number 1

// – - if the model 2 is used, look for an intersection of the main and signal line

if (IS_PATTERN_USAGE (2) && State (idx) <0.0 && State (idx+1)> 0.0)

result=m_pattern_2; // signal number 2

// – - if the model 3 is used, look for an intersection of the main line and the zero level

if (IS_PATTERN_USAGE (3) && Main (idx) <0.0 && Main (idx+1)> 0.0)

result=m_pattern_3; // signal number 3

// – - if the models 4 or 5 are used and the main line turned downwards above the zero level, look for divergences

if ((IS_PATTERN_USAGE (4) || IS_PATTERN_USAGE (5)) && Main (idx)> 0.0)

{

// – - perform the extended analysis of the oscillator state

ExtState (idx);

// – - if the model 4 is used, look for the «divergence» signal

if (IS_PATTERN_USAGE (4) && CompareMaps (1,1)) // 0000 0001b

result=m_pattern_4; // signal number 4

// – - if the model 5 is used, look for the «double divergence» signal

if (IS_PATTERN_USAGE (5) && CompareMaps (0x11,2)) // 0001 0001b

return (m_pattern_5); // signal number 5

}

}

// – - return the result

return (result);

}

//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +

Теперь можно приступить к коду нашего индикатора.

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

#property indicator_chart_window



#include <Expert\ExpertInd.mqh>

#include <Expert\Signal\SignalMACDInd.mqh>

#include <Expert\Signal\SignalMAInd.mqh>



#property indicator_buffers 2

#property indicator_plots 1

#property indicator_type1 DRAW_COLOR_LINE

#property indicator_color1 clrBlack, clrRed, clrLawnGreen

#property indicator_style1 STYLE_SOLID

#property indicator_width1 2



double InBuffer [];

double ColorBuffer [];

int bars_calculated=0;



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_MACD_PeriodFast =12; // MACD (12,24,9,PRICE_CLOSE) Period of fast EMA

input int Signal_MACD_PeriodSlow =24; // MACD (12,24,9,PRICE_CLOSE) Period of slow EMA

input int Signal_MACD_PeriodSignal =9; // MACD (12,24,9,PRICE_CLOSE) Period of averaging of difference

input ENUM_APPLIED_PRICE Signal_MACD_Applied =PRICE_CLOSE; // MACD (12,24,9,PRICE_CLOSE) Prices series

input double Signal_MACD_Weight =1.0; // MACD (12,24,9,PRICE_CLOSE) Weight [0…1.0]

input int Signal_MA_PeriodMA =12; // Moving Average (12,0,…) Period of averaging

input int Signal_MA_Shift =0; // Moving Average (12,0,…) Time shift

input ENUM_MA_METHOD Signal_MA_Method =MODE_SMA; // Moving Average (12,0,…) Method of averaging

input ENUM_APPLIED_PRICE Signal_MA_Applied =PRICE_CLOSE; // Moving Average (12,0,…) Prices series

input double Signal_MA_Weight =1.0; // Moving Average (12,0,…) Weight [0…1.0]



CExpertInd ExtExpert;

CSignalMAInd *filter0 = new CSignalMAInd;

CSignalMACDInd *filter1 = new CSignalMACDInd;



//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +

//| Custom indicator initialization function |

//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +

int OnInit ()

{



// – - Initializing expert

if(!ExtExpert.Init (Symbol (),Period (),true,100))

{

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



filter0.PeriodMA (Signal_MA_PeriodMA);

filter0.Shift (Signal_MA_Shift);

filter0.Method (Signal_MA_Method);

filter0.Applied (Signal_MA_Applied);



filter1.PeriodFast (Signal_MACD_PeriodFast);

filter1.PeriodSlow (Signal_MACD_PeriodSlow);

filter1.PeriodSignal (Signal_MACD_PeriodSignal);

filter1.Applied (Signal_MACD_Applied);



signal.AddFilter (filter0);

signal.AddFilter (filter1);

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

// – - indicator buffers mapping

SetIndexBuffer (0,InBuffer, INDICATOR_DATA);

SetIndexBuffer (1,ColorBuffer, INDICATOR_COLOR_INDEX);



ArraySetAsSeries (InBuffer, true);

ArraySetAsSeries (ColorBuffer, true);



// – —

return (INIT_SUCCEEDED);

}

//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +

//| Custom indicator iteration function |

//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +

int OnCalculate (const int rates_total,

const int prev_calculated,

const datetime &time [],

const double &open [],

const double &high [],

const double &low [],

const double &close [],

const long &tick_volume [],

const long &volume [],

const int &spread [])

{

// – - количество копируемых значений из индикатора

int values_to_copy;

// – - узнаем количество рассчитанных значений в индикаторе

int calculated=MathMin(filter0.BarsCalculatedInd (), filter1.BarsCalculatedInd ());

if (calculated <=0)

{

PrintFormat («BarsCalculated () вернул %d, код ошибки %d», calculated, GetLastError ());

return (0);

}

// – - если это первый запуск вычислений нашего индикатора или изменилось количество значений в индикаторе

// – - или если необходимо рассчитать индикатор для двух или более баров (значит что-то изменилось в истории)

if (prev_calculated==0 || calculated!=bars_calculated || rates_total> prev_calculated+1)

{

// – - если массив больше, чем значений в индикаторе на паре symbol/period, то копируем не все

// – - в противном случае копировать будем меньше, чем размер индикаторных буферов

if (calculated> rates_total) values_to_copy=rates_total;

else values_to_copy=calculated;

}

else

{

// – - значит наш индикатор рассчитывается не в первый раз и с момента последнего вызова OnCalculate ())

// – - для расчета добавилось не более одного бара

values_to_copy= (rates_total-prev_calculated) +1;

}



bars_calculated=calculated;



ArraySetAsSeries (open, true);



ExtExpert.RefreshInd ();



if (values_to_copy> 1)

{



for (int i=0; i <values_to_copy; i++) {



ColorBuffer [i] =0;

InBuffer [i] =open [i];



double result0=Signal_MA_Weight*(filter0.LongConditionInd(i)-filter0.ShortConditionInd (i));

double result1=Signal_MACD_Weight*(filter1.LongConditionInd(i)-filter1.ShortConditionInd (i));

double result= (result0+result1) /2;



if (result> =Signal_ThresholdOpen)

{

ColorBuffer [i] =2;

}



if (-result> =Signal_ThresholdOpen) {

ColorBuffer [i] =1;

}

}

}

// – - return value of prev_calculated for next call

return (rates_total);

}

//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +

Здесь мы взяли входные параметры и код инициализации из кода сгенерированного эксперта.

Так как модули сигналов работают с данными как с таймсериями, применим функцию ArraySetAsSeries к буферам нашего индикатора.

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

Назад: Создание эксперта с помощью мастера MQL5 Wizard
Дальше: Генетические алгоритмы