MTF-индикаторы как инструмент технического анализа
Alexander Lasygin | 26 марта, 2019
Введение
Большинство из нас согласны с мнением, что процесс анализа текущей рыночной ситуации начинается с рассмотрения старших периодов графика. Происходит это до тех пор, пока мы не перейдем на тот график, на котором совершаем сделки. Данный вариант анализа является одним из условий успешной торговли и профессиональным подходом к делу. Обычно для этого мы открываем несколько окон или переключаемся между периодами графика, если используем одинаковый набор инструментов. И вот процесс первичного анализа пройден.
Как быть дальше? Оставаться в неведении того, что творится на старших ТФ, либо продолжать «скакать» между окнами и периодами? Хорошо если мы работаем на таймфрейме Н1 и выше, тогда у нас есть время для тщательной оценки. А если это М1-М15? Но нам данная информация нужна, а иногда жизненно необходима. И не там, где-то на другой закладке или после нажатия очередной клавиши, а здесь и сейчас. Особенно это касается MTF стратегий, основанных на одновременной оценке разных TF, таких, как «Волны Вульфа» или «Три экрана Элдера».
Трейдеры, вынужденные поступать подобным образом, находятся в сильном умственном и зрительно напряжении.
Совершая сделки на младших TF, легко упустить выгодные сигналы старших периодов. Все это впоследствии приводит к принятию поспешного решения, раннему закрытию сделки или потере момента разворота рынка. Последствия подобных промахов хорошо известны. В данных
ситуациях нам остаётся уповать на наш опыт или скорость в получении информации.
Но есть решение данной проблемы — это индикатор, получающий информацию с разных ТФ для данного или нескольких торговых инструментов и комплексно выводящий ее на экран, тем самым позволяя оперативно оценивать состояние рынка. Они кроме основной информации могут отображать реальное состояние рыночной тенденции и рекомендовать торговые действия.
Особенности алгоритма
Основным отличием от классических является обработка общей информации со всех временных интервалов или торговых инструментов с последующей передачей ее на текущий. Мультифреймовым может быть индикаторы любого тип (осциллятор, трендовый, объёма и т.д.) либо их комбинация. Они рассчитываются по своему основному алгоритму и передают сведения с учетом временного интервала, указанного в настройках.
Настройка не отличается от их классических собратьев с той лишь разницей, что в нем присутствует параметр (или группа), в которых указывается список временных интервалов, а в некоторых случаях и перечень торговых инструментов, с которых будет поступать информация, необходимая для составления более четкой картины происходящего. Вывод результата может быть осуществлен как в основном окне графика, так и отдельно, а также иметь сигнальный вид, объединяя группы по типу инструмента.
Классификация мультитаймфреймовых индикаторов
Они представлены во всех стандартных классах, многие из них комплексные, то есть совмещают в себе расчетно-справочную информацию с графическими элементами. Можно выделить следующие группы:
1. Информационные – выводят на экран данные и дополнительную информацию без сигналов и графических построений. Классическим примером данного типа можно считать индикатор MultiTimeFrame. Он отражает время закрытия свечи каждого таймфрейма Ask, Bid по выбранным валютным парам, состояние самой свечи (UP, DOWN, DOJI) и объем. Экран индикаторов этого типа наполнен большим объемом полезной информацией, но для торговли мало пригоден – только для просмотра.
Рис. 1. Информационные индикаторы
Данная группа также представлена и вполне самостоятельными инструментами, которые в полной мере можно использовать для принятия торговых решений. Они выводят на экран результаты анализа нескольких стандартных индикаторов без необходимости их установки на график (расчет выполняется автоматически), и сопровождают это торговыми рекомендациями.
Рис. 2. Сигналы информационных индикаторов
Рис. 3. Сигналы информационных индикаторов
2. Графические выводят на экран построения одного и того же инструмента, но на разных ТФ. Вот так выглядит стандартный конверт МА(13) с разных ТФ.
Рис. 4. Графические индикаторы
Еще один тип графического построения представляет группу графиков с разным периодом расчета. Данный подход реализуется из простой математики. То есть Стохастик (5.3.3) на М5 будет иметь параметры(15.3.9) с М15, а с М30 уже другие — (30.3.18).
Рис. 5. Графические индикаторы с разными периодами расчета
Выше упомянутый вариант решения относить к классу MTF надо с некоторой оговоркой.
Подобный подход не всегда возможно реализовать, и в некоторых случаях недостатки данного инструмента столь значительны, что его применение нецелесообразно. Более подробно, когда данный
метод применим, о его достоинствах и недостатках мы коснёмся ниже.
Отдельной
группой можно считать так называемые сигнальные. Для того чтобы разгрузить рабочий экран от графических
построений, он формирует точечные (сигнальные) линии или графические блоки, отражающие направление тренда или другие параметры. Вот так будет выглядеть
стандартный MTF_Coral решенный подобным образом:
Рис. 6. Сигнальные индикаторы
Также мы можем выделить группу, которую можно условна назвать «Окно в окне». Данная группа характеризуется тем, что отражает в одном окне с основным графики других таймфреймов или индикаторов.
Рис. 7. Индикатор типа "Окно в окне"
Рис. 7.1. Индикатор типа "Окно в окне"
Еще один пример решения All_Woodies CCI.
Рис. 7.1. Индикатор типа "Окно в окне" All_Woodies CCI
Отдельно нужно отметить МТФ индикаторы
волатильности. К ним можно отнести MTF Candles.
Рис. 8. Индикатор волатильности MTF Candles
Способы реализации
Выше мы рассмотрели основные типы MTF индикаторов. Сейчас коснемся на простых примерах основные способы реализации линейного варианта, а также проанализируем особенности каждого решения.
Мультипериодные индикаторы. На примере МА рассмотрим задачу: создать вариант с изменением периода расчета для отображения трех разных
ТФ. Зададим основные параметры и наши переменные:
//---- indicator settings #property indicator_chart_window #property indicator_buffers 3 #property indicator_plots 3 #property indicator_type1 DRAW_LINE #property indicator_type2 DRAW_LINE #property indicator_type3 DRAW_LINE #property indicator_color1 Blue #property indicator_color2 Red #property indicator_color3 Lime #property indicator_width1 1 #property indicator_width2 1 #property indicator_width3 1 //---- input parameters input ENUM_TIMEFRAMES tf1 = 1; // Time Frame (1) input ENUM_TIMEFRAMES tf2 = 5; // Time Frame (2) input ENUM_TIMEFRAMES tf3 = 15; // Time Frame (3) input int maPeriod = 13; // MA period input int Shift = 0; // Shift input ENUM_MA_METHOD InpMAMethod = MODE_SMA; // Moving average method input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE; // Applied price //---- indicator buffers double ExtBuf1[]; double ExtBuf2[]; double ExtBuf3[]; //---- handles for moving averages int ExtHandle1; int ExtHandle2; int ExtHandle3; //--- bars minimum for calculation int ExtBarsMinimum; //--- int period1=0; int period2=0; int period3=0;
Теперь инициализируем данные массивов с условием, что ТФ на котором он расположен <= заданных в переменных.
void OnInit() { int timeframe; //---- indicator buffers mapping SetIndexBuffer(0,ExtBuf1,INDICATOR_DATA); SetIndexBuffer(1,ExtBuf2,INDICATOR_DATA); SetIndexBuffer(2,ExtBuf3,INDICATOR_DATA); //--- timeframe =_Period; //--- if(tf1>=timeframe) { period1=maPeriod*(int)MathFloor(tf1/timeframe); PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,period1-1); //sets first bar from what index will be drawn PlotIndexSetInteger(0,PLOT_SHIFT,Shift); //line shifts when drawing PlotIndexSetString(0,PLOT_LABEL,"MA("+string(period1)+")"); //name for DataWindow ExtHandle1=iMA(NULL,0,period1,0,InpMAMethod,InpAppliedPrice); //get MA's handles } //---- if(tf2>=timeframe) { period2=maPeriod*(int)MathFloor(tf2/timeframe); PlotIndexSetInteger(1,PLOT_DRAW_BEGIN,period2-1); //sets first bar from what index will be drawn PlotIndexSetInteger(1,PLOT_SHIFT,Shift); //line shifts when drawing PlotIndexSetString(1,PLOT_LABEL,"MA("+string(period2)+")"); //name for DataWindow ExtHandle2=iMA(NULL,0,period2,0,InpMAMethod,InpAppliedPrice); //get MA's handles } //---- if(tf3>=timeframe) { period3=maPeriod*(int)MathFloor(tf3/timeframe); PlotIndexSetInteger(2,PLOT_DRAW_BEGIN,period3-1); //sets first bar from what index will be drawn PlotIndexSetInteger(2,PLOT_SHIFT,Shift); //line shifts when drawing PlotIndexSetString(2,PLOT_LABEL,"MA("+string(period3)+")"); //name for DataWindow ExtHandle3=iMA(NULL,0,period3,0,InpMAMethod,InpAppliedPrice); //get MA's handles } //--- set accuracy IndicatorSetInteger(INDICATOR_DIGITS,_Digits); //--- bars minimum for calculation int per=MathMax(period3,MathMax(period1,period2)); ExtBarsMinimum=per+Shift; //--- initialization done }
Основной цикл проверки инициализации и расчета:
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[]) { //--- check for rates total if(rates_total<ExtBarsMinimum) return(0); // not enough bars for calculation //--- not all data may be calculated int calculated=BarsCalculated(ExtHandle1); if(calculated<rates_total&&period1!=0) { Print("Not all data of ExtHandle1 is calculated (",calculated,"bars ). Error",GetLastError()); return(0); } calculated=BarsCalculated(ExtHandle2); if(calculated<rates_total&&period2!=0) { Print("Not all data of ExtHandle2 is calculated (",calculated,"bars ). Error",GetLastError()); return(0); } calculated=BarsCalculated(ExtHandle3); if(calculated<rates_total&&period3!=0) { Print("Not all data of ExtHandle3 is calculated (",calculated,"bars ). Error",GetLastError()); return(0); } //--- we can copy not all data int to_copy; if(prev_calculated>rates_total || prev_calculated<0) to_copy=rates_total; else { to_copy=rates_total-prev_calculated; if(prev_calculated>0) to_copy++; } //---- get ma buffers if(IsStopped()) return(0); //Checking for stop flag if(period1!=0) if(CopyBuffer(ExtHandle1,0,0,to_copy,ExtBuf1)<=0) { Print("getting ExtHandle1 is failed! Error",GetLastError()); return(0); } if(IsStopped()) return(0); //Checking for stop flag if(period2!=0) if(CopyBuffer(ExtHandle2,0,0,to_copy,ExtBuf2)<=0) { Print("getting ExtHandle2 is failed! Error",GetLastError()); return(0); } if(IsStopped()) return(0); //Checking for stop flag if(period3!=0) if(CopyBuffer(ExtHandle3,0,0,to_copy,ExtBuf3)<=0) { Print("getting ExtHandle3 is failed! Error",GetLastError()); return(0); } //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+
Посмотрим на полученный нами результат.
Рис. 9. Реализация MTF-индикатора
Мы рассмотрели пример, когда нам необходимо использовать один индикатор на разных ТФ, используя увеличение периода расчета. При незначительной переделке мы можем использовать его и с возможностью задавать периоды для каждой линии самостоятельно. Данный способ написания может показаться не целесообразным. Нет ничего проще чем рассчитать период самому и накинуть несколько индикаторов одновременно. Но возникают случаи, когда данный вариант при всех своих недостатках является оптимальным. К ним можно отнести случай, когда необходимо наблюдать одновременно два (три) ненормированных осциллятора в одном окне. Из-за разброса своей амплитуды данные осцилляторы смещаются относительно центральной линии, что вносит сложность в их интерпретацию. Данный вариант позволяет исключить этот недостаток.
Мультитаймфреймовые индикаторы
В место хорошо нам знакомых по MQL4 функций iClose(), iHigh(), iLow(), iOpen(), iTime(), iVolume() в MQL5 пришли CopyTime(), CopyClose(), CopyHigh(), CopyLow(), CopyOpen(), CopyTime(), CopyVolume(), а функции iCustom, iMA, iCCI, iMACD и т.д. реализуются через CopyBuffer(). Каждая из них имеет свои достоинства и недостатки. В нашем случае мы коснемся только MQL5. Для написания нам может понадобится весь список фреймов от М1 до MN1, это 26 вариантов. А если мы используем несколько торговых символов или инструментов, то это число увеличивается многократно. В большинстве случаев нет необходимости копировать всю историю. Для информационных индикаторов, в большинстве своем, количество баров ограничивается двумя. Поэтому, чтобы не раздувать текст кода до безграничных размеров целесообразно записывать данные команды отдельными функциями и вызывать их многократно.
Для функции тайм серии CopyClose()
функция будет иметь вид:
//+------------------------------------------------------------------+ double _iClose(string symbol,int tf,int index) { if(index < 0) return(-1); double buf[]; ENUM_TIMEFRAMES timeframe=TFMigrate(tf); if(CopyClose(symbol,timeframe, index, 1, buf)>0) return(buf[0]); else return(-1); } //+------------------------------------------------------------------+
Для вызова WPR:
//+------------------------------------------------------------------+ double _iWPR(string symbol, int tf, int period, int shift) { ENUM_TIMEFRAMES timeframe=TFMigrate(tf); int handle=iWPR(symbol,timeframe,period); if(handle<0) { Print("Объект iWPR не создан: Ошибка ",GetLastError()); return(-1); } else return(_CopyBuffer(handle,shift)); } //+------------------------------------------------------------------+ double _CopyBuffer(int handle,int shift) { double buf[]; if(CopyBuffer(handle,0,shift,1,buf)>0) return(buf[0]); return(EMPTY_VALUE); } //+------------------------------------------------------------------+
В тех случаях когда есть несколько линий, функцию _CopyBuffer можно записать в виде:
//+------------------------------------------------------------------+ double _CopyBuffer(int handle,int index,int shift) { double buf[]; switch(index) { case 0: if(CopyBuffer(handle,0,shift,1,buf)>0) return(buf[0]); break; case 1: if(CopyBuffer(handle,1,shift,1,buf)>0) return(buf[0]); break; case 2: if(CopyBuffer(handle,2,shift,1,buf)>0) return(buf[0]); break; case 3: if(CopyBuffer(handle,3,shift,1,buf)>0) return(buf[0]); break; case 4: if(CopyBuffer(handle,4,shift,1,buf)>0) return(buf[0]); break; default: break; } return(EMPTY_VALUE); } //+------------------------------------------------------------------+
а в функция _iWPR изменит строку
return(_CopyBuffer(handle,shift)
на
return(_CopyBuffer(handle,0,shift)
Для обеих случаев функция TFMigrate() будет выглядеть как:
//+------------------------------------------------------------------+ ENUM_TIMEFRAMES TFMigrate(int tf) { switch(tf) { case 0: return(PERIOD_CURRENT); case 1: return(PERIOD_M1); case 5: return(PERIOD_M5); case 15: return(PERIOD_M15); case 30: return(PERIOD_M30); case 60: return(PERIOD_H1); case 240: return(PERIOD_H4); case 1440: return(PERIOD_D1); case 10080: return(PERIOD_W1); case 43200: return(PERIOD_MN1); case 2: return(PERIOD_M2); case 3: return(PERIOD_M3); case 4: return(PERIOD_M4); case 6: return(PERIOD_M6); case 10: return(PERIOD_M10); case 12: return(PERIOD_M12); case 20: return(PERIOD_M20); case 16385: return(PERIOD_H1); case 16386: return(PERIOD_H2); case 16387: return(PERIOD_H3); case 16388: return(PERIOD_H4); case 16390: return(PERIOD_H6); case 16392: return(PERIOD_H8); case 16396: return(PERIOD_H12); case 16408: return(PERIOD_D1); case 32769: return(PERIOD_W1); case 49153: return(PERIOD_MN1); default: return(PERIOD_CURRENT); } } //+------------------------------------------------------------------+
Как мы уже говорили для данного типа чаще всего требуется в расчете ограниченное число элементов (баров). Но иногда желательно рассчитать всю историю. И здесь надо быть внимательным. Надо понимать, что количество баров в истории младшего ТФ будет больше чем старшего. Данный фактор надо учитывать при создании данного инструмента. Самый простой способ — это определить их наименьшее количество и использовать это значение для расчета. Более сложный это определять эту величину для каждого ТФ отдельно. Так же зачастую (особенно в информационных) требуются сведения только после закрытия бара и нет необходимости пересчитывать старшие ТФ на каждом тике младшего. Если учесть данный аспект это значительно сократит энергоемкость данного инструмента, которая достаточно велика из-за своих особенностей.
Написание информационных индикаторов (рис 1, рис 2, рис 3) ни чем не отличается от написания классических, поэтому мы сразу перейдем к рассмотрению более интересных, с моей точки зрения, — к классу графических индикаторов. Если информационным нужна только текущая информация о состояние рынка и нашего набора инструментов, то графические еще и предъявляют требования к построению. Мы все знаем, что для формирования периода М5 необходимо 5 баров периода М1, для М15 три бара М5 и так далее. То есть во время формирования линии на М5 линия с М15 рисуется в течении 3-х баров. Положение линии не фиксируется и изменяется пока свеча М15 не закроется. По этой причине возникает необходимость привязки по времени к открытию свечи. Рассмотрим вариант, как это сделать, на примере все той же МА.
//---- indicator settings #property indicator_chart_window #property indicator_buffers 1 #property indicator_plots 1 #property indicator_type1 DRAW_LINE #property indicator_color1 Blue #property indicator_width1 1 //---- input parameters input ENUM_TIMEFRAMES tf = 5; // Time Frame input int maPeriod = 13; // MA period input int Shift = 0; // Shift input ENUM_MA_METHOD InpMAMethod = MODE_SMA; // Moving average method input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE; // Applied price input int Bars_Calculated = 500; //---- indicator buffers double ExtMA[]; //---- handles for moving averages int MA_Handle; //--- bars minimum for calculation int ExtBarsMinimum; ENUM_TIMEFRAMES _tf; int pf; //--- будем хранить количество значений в индикаторе Moving Average int bars_calculated=0; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { _tf=tf; ENUM_TIMEFRAMES timeframe; int draw_shift=Shift;// начальное значение PLOT_SHIFT int draw_begin=maPeriod;// начальное значение PLOT_DRAW_BEGIN //--- timeframe=_Period; if(_tf<=timeframe)_tf=timeframe;// еcли TF меньше или равен текущему, ставим его PERIOD_CURRENT pf=(int)MathFloor(_tf/timeframe);// расcчитаем коэффициент для PLOT_DRAW_BEGIN, PLOT_SHIFT и кол-ва расчетных баров. draw_begin=maPeriod*pf;// рассчитаем PLOT_DRAW_BEGIN draw_shift=Shift*pf;// рассчитаем PLOT_SHIFT //---- indicator buffers mapping SetIndexBuffer(0,ExtMA,INDICATOR_DATA); //--- PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,draw_begin-pf); //sets first bar from what index will be drawn PlotIndexSetInteger(0,PLOT_SHIFT,draw_shift); //line shifts when drawing PlotIndexSetString(0,PLOT_LABEL,"MA("+string(tf)+" "+string(maPeriod)+")");//name for DataWindow //--- MA_Handle=iMA(NULL,_tf,maPeriod,0,InpMAMethod,InpAppliedPrice); //get MA's handles if(MA_Handle==INVALID_HANDLE) { Print("getting MA Handle is failed! Error",GetLastError()); return(INIT_FAILED); } //--- set accuracy IndicatorSetInteger(INDICATOR_DIGITS,_Digits); //--- bars minimum for calculation ExtBarsMinimum=draw_begin+draw_shift;// рассчитаем минимальное необходимое кол-во баров для расчета //--- initialization done 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[]) { //--- check for rates total if(rates_total<ExtBarsMinimum) return(0); // not enough bars for calculation int limit; //--- индексация элементов в массивах как в таймсериях ArraySetAsSeries(time,true); ArraySetAsSeries(ExtMA,true); //--- detect start position //--- расчеты необходимого количества копируемых данных //--- и стартового номера limit для цикла пересчета баров if(prev_calculated>rates_total || prev_calculated<=0|| calculated!=bars_calculated)// проверка на первый старт расчета индикатора { limit=rates_total-ExtBarsMinimum-1; // стартовый номер для расчета всех баров } else { limit=(rates_total-prev_calculated)+pf+1; // стартовый номер для расчета новых баров } if(Bars_Calculated!=0) limit=MathMin(Bars_Calculated,limit);
Мы не будем выполнять поиск бара по номеру (iBarShift()), а сразу будем копировать значения по времени.
//--- main cycle for(int i=limit;i>=0 && !IsStopped();i--) { ExtMA[i]=_CopyBuffer(MA_Handle,time[i]); } //--- bars_calculated=calculated; Для этого будем использовать выше упомянутую функцию. //+--------- CopyBuffer MA Handle ----------------------------------+ double _CopyBuffer(int handle,datetime start_time) { double buf[]; if(CopyBuffer(handle,0,start_time,1,buf)>0) return(buf[0]); return(EMPTY_VALUE); } //+-------------------- END -----------------------------------------+
Наш результат будет выглядеть так:
Рис. 10. Линия MTF-индикатора
Данный метод с успехом можно использовать для всех типов линейных индикаторов. Основной недостаток хорошо заметен из рисунка — это пресловутые ступеньки. Если для МА это даже в некотором роде достоинство, более четко определены уровни поддержки-сопротивления, то для осцилляторов, в работе с которыми мы используем паттерны, это сильно затруднит нам задачу их выявления и построения. А для таких как WPR, CCI данное решение вообще неприемлемо т.к. вид линии изменится до неузнаваемости.
Для решения этой проблемы достаточно производить расчет последнего бара с учетом весовых коэффициентов. Для универсальности добавим глобальную переменную Interpolate, которая позволит использовать оба решения.
input bool Interpolate = true; //--- main cycle for(int i=limit;i>=0 && !IsStopped();i--) { int n; datetime t=time[i]; ExtMA[i]=_CopyBuffer(MA_Handle,t); if(!Interpolate) continue; //--- datetime times= _iTime(t); for(n = 1; i+n<rates_total && time[i+n]>= times; n++) continue; double factor=1.0/n; for(int k=1; k<n; k++) ExtMA[i+k]=k*factor*ExtMA[i+n]+(1.0-k*factor)*ExtMA[i]; } //---
В данном варианте возникла необходимость добавить функцию _iTime, которая будет определять номер бара исходя из времени его открытия для текущего графика.
//+------------------------------------------------------------------+ datetime _iTime(datetime start_time) { if(start_time < 0) return(-1); datetime Arr[]; if(CopyTime(NULL,_tf, start_time, 1, Arr)>0) return(Arr[0]); else return(-1); } //+------------------------------------------------------------------+
Теперь наша линия приобрела более привычный для нас вид.
Рис. 11. Линия MTF-индикатора с _iTime
При кажущейся нецелесообразности написания таких сложных и энергоёмких систем, они имеют преимущества, а иногда и незаменимы. В случаях где используется классическое усреднение (МА, Alligator и т.д.), при увеличении периода расчета наблюдается некоторое запаздывание по сравнению с MTF версией. Особенно это заметно при малых периодах предполагаемого значения.
Рис. 12. Запаздывание в MTF-индикаторе MA
Рис. 13. Запаздывание в MTF-индикаторе Stochastic
Если для простых индикаторов, таких как МА и Alligator, это может быть и не столь существенно, то для тех, которые представляют сложную систему из двух и более МА, таких как MACD, AO и т.д., это может иметь существенное значение. Тем более что выше упомянутый АО или АС и им подобные вообще не имеют возможности изменять период усреднения. И для индикаторов, линия которых не сглаживается (WPR, CCI и т.д.), банальным увеличением периода расчета добиться сколь либо достойного результата довольно сложно, они сильно зашумлены.
Рис. 14 MTF-индикатор WRP
Рис. 15 MTF-индикатор CCI
Из рисунков 14-15 хорошо видно, что их с успехом можно использовать и как сглаживающий для тех случаев, когда подобная возможность не предусмотрена в алгоритме.
Данный тип, помимо своей непосредственной функции, может выполнять и сугубо практичную — они способны компенсировать недостатки тестера стратегий MetaTrader 5 в режиме визуализации. При создании MTF советников для торговли или анализа эффективности подобного типа стратегий мы сталкиваемся с тем что не можем одновременно наблюдать положение индикаторов с различных ТФ на экране, а по итогам тестирования получаем набор закладок в зависимости от количества используемых периодов. Воспользуемся для примера советником по стратегии «Три экрана Элдера» из материала «РЕЦЕПТЫ MQL5 - РАЗРАБОТКА СХЕМЫ ДЛЯ ТОРГОВОЙ СИСТЕМЫ ТИПА "ТРИ ЭКРАНА ЭЛДЕРА"» автора Anatoli Kazharski. Напомним, как звучит эта стратегия в классическом варианте: первый таймфрейм – самый крупный, например, недельный, дневной или 4-часовой. С помощью него определяют основной тренд. Второй таймфрейм отличается от первого на 1 или 2 порядка. С его помощью определяем окончание коррекции. Третий таймфрейм отличается еще на один порядок. По нему выявляем выгодную точку входа.
В первом окне, обычно это М30-W1, размещаем MACD (12,26,1) и EMA с периодом 13. Второй экран, М5-D1 соответственно, у нас расположился Стохастик (Stochastic Oscillator) (5,3,3). Третий экран может быть от M1 до H4, используем его для выставления Stop-ордеров в направлении основного тренда.
Рис. 16. Три экрана Элдера
Автор
несколько отошел от данного
варианта, но концепция «Три
экрана» сохранена.
Во время и по окончанию тестирования мы наблюдаем подобную картину:
Рис. 17. Тестирование стратегии "Три экрана Элдера"
Данный
вариант не позволяет нам в полной мере
проанализировать работу советника
(стратегии).
Напишем свою версию
советника с использованием наших инструментов более приближенную к классической
версии стратегии. Создание советников на базе данных индикаторов ничем не отличается от работ с их классическими собратьями.
Основной код будет выглядеть так.
#define EXPERT_MAGIC 1234502 //--- #include <Trade\Trade.mqh> #include <Trade\SymbolInfo.mqh> #include <Trade\PositionInfo.mqh> #include <Trade\AccountInfo.mqh> #include <Trade\OrderInfo.mqh> //+------------------------------------------------------------------+ enum compTF { A, //m1-m5-m15 B, //m5-m15-h1 C, //m15-h1-h4 E //h1-h4-d1 }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ input string s0="//--- input parameters Lots+Trailing ---//"; input double InpLots =0.1; // Lots input int InpTakeProfit =150; // Take Profit (in pips) input int InpStopLoss =60; // StopLoss (in pips) input int InpLevel_S =20; // Level Signal input compTF InpTFreg =2; input string s1="//--- input parameters MA ---//"; input int Signal_MA_PeriodMA =21; // Period of averaging input string s2="//--- input parameters MACD ---//"; input int Signal_MACD_PeriodFast =12; // Period of fast EMA input int Signal_MACD_PeriodSlow =26; // Period of slow EMA input string s3="//--- input parameters Stochastic ---//"; input int Signal_Stoch_PeriodK =5; // K-period input int Signal_Stoch_PeriodD =3; // D-period input string s4="//--- input parameters Trailing ---//"; //--- inputs for trailing input int InpTrailingStop =25; // Trailing Stop Level (in pips) input int InpOffset =5; // Distance from the price (in pips) //--- int ExtTimeOut=10; // время в секундах между торговыми операциями int barsCalculated=3; datetime t=0; datetime time[]; //+------------------------------------------------------------------+ //| AC Sample expert class | //+------------------------------------------------------------------+ class CSampleExpert { protected: double m_adjusted_point; // значение на 3 или 5 знаков CTrade m_trade; // торговый объект CSymbolInfo m_symbol; // информация о символе CPositionInfo m_position; // торговая позиция CAccountInfo m_account; // информация об аккаунте COrderInfo m_order; // информация об ордере //--- indicators ENUM_TIMEFRAMES mas_tf[3]; // массив набора timeframes int handle_MACD; // MACD indicator handle int handle_MA; // MA indicator handle int handle_Stochastic; // Stochastic indicator handle //--- indicator buffers double macd_buff[]; // MACD indicator main buffer double ma_buff[]; // MA indicator main buffer double stoch_buff[]; // Stochastic indicator main buffer //--- double close[]; double open[]; double low[]; double high[]; //--- данные индикатора для обработки double macd_ind_0; double macd_ind_1; double ma_ind; double stoch_ind_0; double stoch_ind_1; int level_stoch; //--- данные trailing stop для обработки double m_traling_stop; double m_take_profit; double m_stop_losse; public: CSampleExpert(void); ~CSampleExpert(void); bool Init(void); void Deinit(void); bool Processing(void); protected: bool InitCheckParameters(const int digits_adjust); bool Copy(void); // bool InitIndicators(void); bool LongModified(void); bool ShortModified(void); bool LongOpened(void); // проверяем условия Long bool ShortOpened(void); // проверяем условия Short bool OpenSellStop(void); // ставим ордер SELLSTOP bool OpenBuyStop(void); // ставим ордер BUYSTOP bool OrderModifySellStop(void); // изменяем ордер SELLSTOP bool OrderModifyBuyStop(void); // изменяем ордер BUYSTOP bool DellSellStop(void); // удаляем ордер SELLSTOP bool DellBuyStop(void); // удаляем ордер BUYSTOP }; //--- global expert CSampleExpert ExtExpert; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CSampleExpert::CSampleExpert(void) : m_adjusted_point(0), handle_MACD(INVALID_HANDLE), handle_Stochastic(INVALID_HANDLE), handle_MA(INVALID_HANDLE), macd_ind_0(0), macd_ind_1(0), stoch_ind_0(0), stoch_ind_1(0), ma_ind(0), m_traling_stop(0), m_take_profit(0) { ArraySetAsSeries(macd_buff,true); } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CSampleExpert::~CSampleExpert(void) { } //+------------------------------------------------------------------+ //| Инициализация и проверка входных параметров | //+------------------------------------------------------------------+ bool CSampleExpert::Init(void) { //--- инициализировать общую информацию m_symbol.Name(Symbol()); // symbol m_trade.SetExpertMagicNumber(EXPERT_MAGIC); // magic m_trade.SetMarginMode(); m_trade.SetTypeFillingBySymbol(Symbol()); //--- tuning for 3 or 5 digits int digits_adjust=1; if(m_symbol.Digits()==3 || m_symbol.Digits()==5) digits_adjust=10; m_adjusted_point=m_symbol.Point()*digits_adjust; //--- установить отклонение по умолчанию для торговли m_traling_stop =InpTrailingStop*m_adjusted_point; m_take_profit =InpTakeProfit*m_adjusted_point; m_stop_losse =InpStopLoss*m_adjusted_point; //--- set default deviation for trading in adjusted points m_trade.SetDeviationInPoints(3*digits_adjust); //--- int x=InpTFreg; switch(x) { case 0: {mas_tf[0]=PERIOD_M1;mas_tf[1]=PERIOD_M5;mas_tf[2]=PERIOD_M15;} break; case 1: {mas_tf[0]=PERIOD_M5;mas_tf[1]=PERIOD_M15;mas_tf[2]=PERIOD_H1;} break; case 2: {mas_tf[0]=PERIOD_M15;mas_tf[1]=PERIOD_H1;mas_tf[2]=PERIOD_H4;} break; case 3: {mas_tf[0]=PERIOD_H1;mas_tf[1]=PERIOD_H4;mas_tf[2]=PERIOD_D1;} break; } //--- if(!InitCheckParameters(digits_adjust)) return(false); if(!InitIndicators()) return(false); //--- результат return(true); } //+------------------------------------------------------------------+ //| Проверка входных параметров | //+------------------------------------------------------------------+ bool CSampleExpert::InitCheckParameters(const int digits_adjust) { //--- проверка исходных данных if(InpTakeProfit*digits_adjust<m_symbol.StopsLevel()) { printf("Take Profit должен быть больше, чем %d",m_symbol.StopsLevel()); return(false); } if(InpTrailingStop*digits_adjust<m_symbol.StopsLevel()) { printf("Trailing Stop должен быть больше, чем %d",m_symbol.StopsLevel()); return(false); } //--- проверить правильную сумму лота if(InpLots<m_symbol.LotsMin() || InpLots>m_symbol.LotsMax()) { printf("Lots должен быть в диапазоне от %f to %f",m_symbol.LotsMin(),m_symbol.LotsMax()); return(false); } if(MathAbs(InpLots/m_symbol.LotsStep()-MathRound(InpLots/m_symbol.LotsStep()))>1.0E-10) { printf("Сумма не соответствует шагу лота %f",m_symbol.LotsStep()); return(false); } //--- warning if(InpTakeProfit<=InpTrailingStop) printf("Warning: Trailing Stop должен быть меньше, чем Take Profit"); //--- результат return(true); } //+------------------------------------------------------------------+ //| Инициализация индикаторов | //+------------------------------------------------------------------+ bool CSampleExpert::InitIndicators(void) { //--- создать индикатор MACD if(handle_MACD==INVALID_HANDLE) { //--- handle_MACD=iCustom(NULL,0,"MTF\\Oscillators\\MTF_MACD",mas_tf[2],Signal_MACD_PeriodFast,Signal_MACD_PeriodSlow); //--- if(handle_MACD==INVALID_HANDLE) { printf("Ошибка при создании MACD индикатора"); return(false); } } //--- создать индикатор MA if(handle_MA==INVALID_HANDLE) { //--- handle_MA=iCustom(NULL,0,"MTF\\Trend\\MA_MultiTF",mas_tf[2],Signal_MA_PeriodMA,0,MODE_EMA,PRICE_CLOSE); //--- if(handle_MA==INVALID_HANDLE) { printf("Ошибка при создании MA индикатора"); return(false); } } //--- создать индикатор Stochastic if(handle_Stochastic==INVALID_HANDLE) { //--- handle_Stochastic=iCustom(NULL,0,"MTF\\Oscillators\\MTF_Stochastic",mas_tf[1],Signal_Stoch_PeriodK,Signal_Stoch_PeriodD); //--- if(handle_Stochastic==INVALID_HANDLE) { printf("Ошибка при создании Stochastic индикатора"); return(false); } } //--- результат return(true); } //+------------------------------------------------------------------+ //| Измененяем длинную позицию | //+------------------------------------------------------------------+ bool CSampleExpert::LongModified(void) { bool res=false; //--- проверить трейлинг-стоп if(InpTrailingStop>0) { if(m_symbol.Bid()-m_position.PriceOpen()>m_adjusted_point*InpTrailingStop) { double sl=NormalizeDouble(m_symbol.Bid()-m_traling_stop,m_symbol.Digits()); double tp=m_position.TakeProfit(); if(m_position.StopLoss()<sl || m_position.StopLoss()==0.0) { //--- изменить позицию if(m_trade.PositionModify(Symbol(),sl,tp)) printf("Long position by %s to be modified",Symbol()); else { printf("Error modifying position by %s : '%s'",Symbol(),m_trade.ResultComment()); printf("Modify parameters : SL=%f,TP=%f",sl,tp); } //--- изменен и должен выйти из эксперта res=true; } } } //--- результат return(res); } //+------------------------------------------------------------------+ //| Изменяем короткую позицию | //+------------------------------------------------------------------+ bool CSampleExpert::ShortModified(void) { bool res=false; //--- проверить трейлинг-стоп if(InpTrailingStop>0) { if((m_position.PriceOpen()-m_symbol.Ask())>(m_adjusted_point*InpTrailingStop)) { double sl=NormalizeDouble(m_symbol.Ask()+m_traling_stop,m_symbol.Digits()); double tp=m_position.TakeProfit(); if(m_position.StopLoss()>sl || m_position.StopLoss()==0.0) { //--- изменить позицию if(m_trade.PositionModify(Symbol(),sl,tp)) printf("Short position by %s to be modified",Symbol()); else { printf("Error modifying position by %s : '%s'",Symbol(),m_trade.ResultComment()); printf("Modify parameters : SL=%f,TP=%f",sl,tp); } //--- изменен и должен выйти из эксперта res=true; } } } //--- результат return(res); } //+------------------------------------------------------------------+ //| Проверить условия открытия длинной позиции | //+------------------------------------------------------------------+ bool CSampleExpert::LongOpened(void) { bool res=false; level_stoch=InpLevel_S; //--- проверяем возможность длинной позиции (BUY) if(stoch_ind_1<level_stoch && stoch_ind_0>level_stoch && macd_ind_1>macd_ind_0 && ma_ind<close[1] && ma_ind<open[1])//&& ma_ind<close[1] && ma_ind<open[1] { //--- выйти из эксперта res=true; } //--- результат return(res); } //+------------------------------------------------------------------+ //| открыть Buy Stop позицию | //+------------------------------------------------------------------+ bool CSampleExpert::OpenBuyStop(void) { bool res=false; double tp=0,sl=0; //--- if(LongOpened()) { res=true; //-- объявление и инициализация запроса и результата MqlTradeRequest request={0}; MqlTradeResult result={0}; //--- параметры для установки отложенного ордера request.action =TRADE_ACTION_PENDING; // тип торговой операции request.symbol =Symbol(); // символ request.deviation=5; // допустимое отклонение от цены request.volume =InpLots; // объем в лот request.magic =EXPERT_MAGIC; // MagicNumber ордера double offset=InpOffset; // отступ от текущей цены для установки ордера, в пунктах double price; // цена срабатывания ордера double point=SymbolInfoDouble(_Symbol,SYMBOL_POINT); // размер пункта int digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); // кол-во знаков после запятой (точность) //--- тип операции request.type=ORDER_TYPE_BUY_STOP; // тип ордера price=high[1]+offset*m_adjusted_point; // цена для открытия request.price=NormalizeDouble(price,digits); // нормализованная цена открытия tp=price+m_take_profit; sl=price-m_stop_losse; request.sl =NormalizeDouble(sl,_Digits); // заносим новое значение Buy Loss в структуру request.tp =NormalizeDouble(tp,_Digits); // заносим новое значение Take Profit в структуру //--- отправка запроса if(!OrderSend(request,result)) {res=false;printf("OrderSend error %d",GetLastError());} // если отправить запрос не удалось, вывести код ошибки //--- информация об операции printf("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order); //--- выйти из эксперта } //--- результат return(res); } //+------------------------------------------------------------------+ //| Проверить условия открытия короткой позиции | //+------------------------------------------------------------------+ bool CSampleExpert::ShortOpened(void) { bool res=false; level_stoch=100-InpLevel_S; //--- проверить возможность короткой позиции (SELL) if(stoch_ind_1>level_stoch && stoch_ind_0<level_stoch && macd_ind_1<macd_ind_0 && ma_ind>close[1] && ma_ind>open[1]) { //--- выйти из эксперта res=true; } //--- результат return(res); } //+------------------------------------------------------------------+ //| открыть Sell Stop позицию | //+------------------------------------------------------------------+ bool CSampleExpert::OpenSellStop(void) { bool res=false; double tp=0,sl=0; //--- if(ShortOpened()) { res=true; //-- объявление и инициализация запроса и результата MqlTradeRequest request={0}; MqlTradeResult result={0}; //--- параметры для установки отложенного ордера request.action =TRADE_ACTION_PENDING; // тип торговой операции request.symbol =Symbol(); // символ request.deviation=5; // допустимое отклонение от цены request.volume=InpLots; // объем в лот request.magic=EXPERT_MAGIC; // MagicNumber ордера int offset=InpOffset; // отступ от текущей цены для установки ордера, в пунктах double price; // цена срабатывания ордера int digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); // кол-во знаков после запятой (точность) request.type=ORDER_TYPE_SELL_STOP; // тип ордера price=low[1]-offset*m_adjusted_point; // цена для открытия request.price=NormalizeDouble(price,digits); // нормализованная цена открытия tp=price-m_take_profit; sl=price+m_stop_losse; request.sl =NormalizeDouble(sl,_Digits); // заносим новое значение StopLoss в структуру request.tp =NormalizeDouble(tp,_Digits); // заносим новое значение TakeProfit в структуру //--- отправка запроса if(!OrderSend(request,result)) {res=false;printf("OrderSend error %d",GetLastError());} // если отправить запрос не удалось, вывести код ошибки //--- информация об операции printf("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order); //--- выйти из эксперта } //--- результат return(res); } //+------------------------------------------------------------------+ //| основная функция возвращает true, если какая-либо позиция | //| обрабатывается | //+------------------------------------------------------------------+ bool CSampleExpert::Processing(void) { // MqlDateTime dt; //--- частота обновления if(!m_symbol.RefreshRates()) return(false); //--- индикаторы обновления if(BarsCalculated(handle_Stochastic)<barsCalculated) return(false); if(CopyBuffer(handle_Stochastic,0,0,barsCalculated,stoch_buff)!=barsCalculated) return(false); //--- if(BarsCalculated(handle_MACD)<barsCalculated) return(false); if(CopyBuffer(handle_MACD,0,0,barsCalculated,macd_buff)!=barsCalculated) return(false); //--- if(BarsCalculated(handle_MA)<barsCalculated) return(false); if(CopyBuffer(handle_MA,0,0,barsCalculated,ma_buff)!=barsCalculated) return(false); //--- if(!Copy())return(false); //--- для упрощения кодирования и ускорения доступа //--- данные помещаются во внутренние переменные macd_ind_0 = macd_buff[1]; macd_ind_1 = macd_buff[0]; ma_ind = ma_buff[1]; stoch_ind_0 = stoch_buff[1]; stoch_ind_1 = stoch_buff[0]; //--- важно выйти из него правильно ... //--- сначала проверьте, существует ли позиция - попробуйте выбрать ее bool bord=false,sord=false; ulong ticket; //+--------- есть открытые позиции и traling stop включен -----------+ if(m_position.Select(Symbol()) && PositionsTotal()>0 && m_traling_stop!=0) { if(m_position.PositionType()==POSITION_TYPE_BUY) { bord=true; //--- изменить длинную позицию if(LongModified()) return(true); } else { sord=true; //---изменить короткую позицию if(ShortModified()) return(true); } } //+----- торговля STOP ордерами ------------------------------------+ // в этом цикле поочередно перебираем все установленные отложенные ордера for(int i=0;i<OrdersTotal();i++) { // выбираем каждый из ордеров, получаем его тикет ticket=OrderGetTicket(i); // обслуживаем ордера Buy Stop if(m_order.OrderType()==ORDER_TYPE_BUY_STOP) { // устанавливаем флаг, индицирующий то, что присутствует ордер Buy Stop bord=true; //--- важно правильно войти на рынок,передвигаем ордер если надо if(bord)OrderModifyBuyStop(); // изменяем ордер BUYSTOP } // обслуживаем ордера Sell Stop if(m_order.OrderType()==ORDER_TYPE_SELL_STOP) { // устанавливаем флаг, индицирующий то, что присутствует ордер Sell Stop sord=true; //--- важно правильно войти на рынок,передвигаем ордер если надо if(sord)OrderModifySellStop(); // изменяем ордер SELLSTOP } } //--- если нет ордеров выставим ------------------------------------+ if(!sord)if(OpenSellStop()){sord=true;return(true);} if(!bord)if(OpenBuyStop()){bord=true;return(true);} //--- выход без обработки позиции return(false); } //+------------------------------------------------------------------+ //|Изменим параметры ранее установленного торгового ордера Sell Stop | //+------------------------------------------------------------------+ bool CSampleExpert::OrderModifySellStop(void) { bool res=true; ulong ticket; double tp=0,sl=0; double offset=InpOffset; // отступ от текущей цены для установки ордера, в пунктах double price; // цена срабатывания ордера int digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); // кол-во знаков после запятой (точность) // объявление и инициализация запроса и результата MqlTradeRequest request={0}; MqlTradeResult result={0}; int total=OrdersTotal(); // количество установленных отложенных ордеров //--- перебор всех установленных отложенных ордеров for(int i=total-1; i>=0; i--) { // выбираем каждый из ордеров, получаем его тикет ticket=OrderGetTicket(i); // обслуживаем ордера Sell Stop if(m_order.OrderType()==ORDER_TYPE_SELL_STOP) { ulong magic=OrderGetInteger(ORDER_MAGIC); // MagicNumber ордера //--- если MagicNumber совпадает if(magic==EXPERT_MAGIC) { price=low[1]-offset*m_adjusted_point; // цена для открытия if(price>m_order.PriceOpen()) // проверим условия if(low[1]>low[2]) // проверим условия { request.action=TRADE_ACTION_MODIFY; // тип торговой операции request.order = OrderGetTicket(i); // тикет ордера request.symbol =Symbol(); // символ request.deviation=InpOffset; // допустимое отклонение от цены price=low[1]-offset*m_adjusted_point; // цена для открытия request.price=NormalizeDouble(price,digits); // нормализованная цена открытия tp=price-m_take_profit; sl=price+m_stop_losse; request.sl =NormalizeDouble(sl,_Digits); // заносим новое значение StopLoss в структуру request.tp =NormalizeDouble(tp,_Digits); // заносим новое значение TakeProfit в структуру //--- отправка запроса if(!OrderSend(request,result)) { // устанавливаем флаг, индицирующий то, что ордер Sell Stop не удален res=true; printf("OrderSend error %d",GetLastError()); } // если отправить запрос не удалось, вывести код ошибки //--- информация об операции printf("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order); } } } } return(res); } //+------------------------------------------------------------------+ //|Изменим параметры ранее установленного торгового ордера Buy Stop | //+------------------------------------------------------------------+ bool CSampleExpert::OrderModifyBuyStop(void) { bool res=true; ulong ticket; double tp=0,sl=0; double offset=InpOffset; // отступ от текущей цены для установки ордера, в пунктах double price; // цена срабатывания ордера int digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); // кол-во знаков после запятой (точность) //-- объявление и инициализация запроса и результата MqlTradeRequest request={0}; MqlTradeResult result={0}; int total=OrdersTotal(); // количество установленных отложенных ордеров //--- перебор всех установленных отложенных ордеров for(int i=total-1; i>=0; i--) { // выбираем каждый из ордеров, получаем его тикет ticket=OrderGetTicket(i); // обслуживаем ордера Buy Stop if(m_order.OrderType()==ORDER_TYPE_BUY_STOP) { ulong magic=OrderGetInteger(ORDER_MAGIC); // MagicNumber ордера //--- если MagicNumber совпадает if(magic==EXPERT_MAGIC) { price=high[1]+offset*m_adjusted_point; // цена для открытия if(price<m_order.PriceOpen()) // проверим условия { request.action=TRADE_ACTION_MODIFY; // тип торговой операции request.symbol =Symbol(); // символ request.action=TRADE_ACTION_MODIFY; // тип торговой операции request.order = OrderGetTicket(i); // тикет ордера request.symbol =Symbol(); // символ request.deviation=InpOffset; // допустимое отклонение от цены //--- установка уровня цены, тейк-профит и стоп-лосс ордера request.price=NormalizeDouble(price,digits); // нормализованная цена открытия tp=price+m_take_profit; sl=price-m_stop_losse; request.sl =NormalizeDouble(sl,_Digits); // заносим новое значение Stop Loss в структуру request.tp =NormalizeDouble(tp,_Digits); // заносим новое значение Take Profit в структуру //--- отправка запроса if(!OrderSend(request,result)) { // устанавливаем флаг, индицирующий то, что ордер Buy Stop не удален res=true; printf("OrderSend error %d",GetLastError()); } // если отправить запрос не удалось, вывести код ошибки //--- информация об операции printf("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order); } } } } return(res); } //+------------------------------------------------------------------+ //| Функция инициализации эксперта | //+------------------------------------------------------------------+ int OnInit(void) { //--- создать все необходимые объекты if(!ExtExpert.Init()) return(INIT_FAILED); //--- ok return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| функция обработки тиков | //+------------------------------------------------------------------+ void OnTick(void) { static datetime limit_time=0; // последнее время обработки торговли + таймаут //--- не обрабатывать, если тайм-аут if(TimeCurrent()>=limit_time) { //--- проверить данные if(Bars(Symbol(),Period())>barsCalculated) { //--- изменить предельное время на тайм-аут в секундах, если обработано if(ExtExpert.Processing()) limit_time=TimeCurrent()+ExtTimeOut; } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CSampleExpert::Copy(void) { //--- сколько копируем int copied=3; //--- ArrayResize(high,copied); ArrayResize(low,copied); ArrayResize(close,copied); ArrayResize(open,copied); //+------ Задаем направление индексации массива ---------------------+ ArraySetAsSeries(high,true); ArraySetAsSeries(low,true); ArraySetAsSeries(close,true); ArraySetAsSeries(open,true); //--- копируем цены баров if(CopyHigh(NULL,mas_tf[0],0,copied,high)<0) {printf("No copied High",GetLastError());return(false);} //--- if(CopyLow(NULL,mas_tf[0],0,copied,low)<0) {printf("No copied Low",GetLastError());return(false);} //--- if(CopyClose(NULL,mas_tf[2],0,copied,close)<0) {printf("No copied Close",GetLastError());return(false);} //--- if(CopyOpen(NULL,mas_tf[2],0,copied,open)<0) {printf("No copied Open",GetLastError());return(false);} //--- return(true); } //+------------------------------------------------------------------+
Применение
подобных инструментов дает другую картину:
Рис. 18. Тестирование советника с нашими MTF-инструментами
Заключение
Несмотря на то, что авторы программных модулей MQL5 не предусматривают возможность создания подобных алгоритмов напрямую,
данные индикаторы имеют право на жизнь. В некоторых случаях, таких как возможность
одновременного анализа состояния рынка на разных TF,
повышение эффективности работы в тестере стратегий, повышение качества сглаживания, они незаменимы.
Данные варианты кода не лишены недостатков, что создает большое поле для деятельности в рамках языка программирования MQL.
Прикрепленные файлы.
Имя | Тип | Путь |
---|---|---|
MA_MultiPeriod | Trend | MQL5\Indicators\MA_MultiPeriod.mq5 |
MA_MultiTF | Trend | MQL5\Indicators\MTF\Trend\MA_MultiTF.mq5 |
MTF_BB | Trend | MQL5\Indicators\MTF\Trend\MTF_BB.mq5 |
MTF_Envelopes | Trend | MQL5\Indicators\MTF\Trend\MTF_Envelopes.mq5 |
MTF_ParabolicSAR | Trend | MQL5\Indicators\MTF\Trend\MTF_ParabolicSAR.mq5 |
MTF_StdDev | Trend | MQL5\Indicators\MTF\Trend\MTF_StdDev.mq5 |
MTF_CCI | Oscillators | MQL5\Indicators\MTF\Oscillators\MTF_CCI.mq5 |
MTF_Force_Index | Oscillators | MQL5\Indicators\MTF\Oscillators\MTF_Force_Index.mq5 |
MTF_MACD | Oscillators | MQL5\Indicators\MTF\Oscillators\MTF_MACD.mq5 |
MTF_Momentum | Oscillators | MQL5\Indicators\MTF\Oscillators\MTF_Momentum.mq5 |
MTF_RSI | Oscillators | MQL5\Indicators\MTF\Oscillators\MTF_RSI.mq5 |
MTF_RVI | Oscillators | MQL5\Indicators\MTF\Oscillators\MTF_RVI.mq5 |
MTF_Stochastic | Oscillators | MQL5\Indicators\MTF\Oscillators\MTF_Stochastic.mq5 |
MTF_WPR | Oscillators | MQL5\Indicators\MTF\Oscillators\MTF_WPR.mq5 |
Triple Screen Trading System 1.0 | Expert |