В качестве примера создадим индикатор на основе двух модулей сигналов 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 индикатора мы сначала обновляем все данные, а затем получаем взвешенные сигналы модулей и сравниваем их с пороговым значением. В итоге получаем прогноз на увеличение или уменьшение цены.