Всем привет!
В этом уроке мы научимся подгружать данные из классических осцилляторов терминала MT4 и учитывать их для создания собственного индикатора.
Создаем индикатор осциллятор
Для экспорта данных я решил взять в работу три стандартных индикатора-осциллятора:
- RSI (Relative Strength index) — индекс относительной силы
- WPR (Williams Percent Range) — процентный диапазон Вильямса
- Stochastic — стохастический осциллятор
Все они имеют четкие границы построение от 0 до 100. В случае с WPR это от 0 до -100. Т.е. все имеют предел, который нельзя превысить. Тут у меня зародилась идея: что если взять все эти три показателя для каждой свечи на графике, сложить и разделить на три. Тогда мы получим среднее значение всех данных. Я далеко не математик и корректность этих действий для будущей торговли предсказать не могу. Но! Так как цель этого урока — практика написания кода, то почему бы и нет? Поехали.
Создаем индикатор. Для сохранения данных трех встроенных и одного пользовательского индикатора нам нужно 4 отдельных буфера. Поэтому в параметрах отображения индикатора создадим их, чтобы не прописывать свойства вручную. Наш индикатор должен быть в отдельном окне, иметь минимум 0 и максимум 100. Первые три буфера нам нужны просто для отрисовки, поэтому сделаем их бледно серым цветом, а четвертый — голубым.
Созданный конструктором MetaEditor код будет иметь вид:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
#property copyright "Copyright (c) DaVinci FX Group" #property link "https://www.davinci-fx.com/" #property version "1.00" #property strict #property indicator_separate_window #property indicator_minimum 0 #property indicator_maximum 100 #property indicator_buffers 4 #property indicator_plots 4 //--- plot RSI #property indicator_label1 "RSI" #property indicator_type1 DRAW_LINE #property indicator_color1 clrGray #property indicator_style1 STYLE_SOLID #property indicator_width1 1 //--- plot WPR #property indicator_label2 "WPR" #property indicator_type2 DRAW_LINE #property indicator_color2 clrGray #property indicator_style2 STYLE_SOLID #property indicator_width2 1 //--- plot Stochastic #property indicator_label3 "Stochastic" #property indicator_type3 DRAW_LINE #property indicator_color3 clrGray #property indicator_style3 STYLE_SOLID #property indicator_width3 1 //--- plot Sum #property indicator_label4 "Sum" #property indicator_type4 DRAW_LINE #property indicator_color4 clrDeepSkyBlue #property indicator_style4 STYLE_SOLID #property indicator_width4 1 //--- indicator buffers double RSIBuffer[]; double WPRBuffer[]; double StochasticBuffer[]; double SumBuffer[]; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { SetIndexBuffer(0,RSIBuffer); SetIndexBuffer(1,WPRBuffer); SetIndexBuffer(2,StochasticBuffer); SetIndexBuffer(3,SumBuffer); return(INIT_SUCCEEDED); } |
Теперь зададим внешние переменные для настройки каждого из индикаторов. Чтобы ограничить количество свечей, для которых мы будем стоить линии, введем параметр BarsForAnalyze. Далее для индикаторов RSI и WPR нужна только одна настройка — период, тогда как Стохастик более жадный на точность и ему нужно задать четыре настройки: период линии K, период линии D, замедление и метод усреднения.
0 1 2 3 4 5 6 7 8 9 |
extern int BarsForAnalyze = 500; extern string _1 = "RSI Indicator"; // > > > extern int RSI_Period = 14; extern string _2 = "CCI Indicator"; // > > > extern int WPR_Period = 14; extern string _3 = "Stochastic Indicator"; // > > > extern int StoKPeriod = 14; extern int StoDPeriod = 3; extern int SlowingPeriod = 3; extern ENUM_MA_METHOD StoMethod = MODE_SMA; |
Переходим в функцию обработки событий OnCalculate. Как и в предыдущих уроках нам нужно выполнить условие, при котором на первом тике после установки индикатора у нас произойдет анализ всех свечей на истории с последующим построением линий индикатора, а на всех последующих тиках будут обновляться данные только для [0] свечи. В этом нам помогут параметры функции rates_total и prev_calculated.
0 1 |
int limit = rates_total-prev_calculated; limit = MathMin(limit,BarsForAnalyze); |
Второй строчкой кода мы ограничиваем анализ баров до указанного в настройках параметра BarsForAnalyze. Далее запускаем оператор цикла for, чтобы пробежаться по всем доступным свечам, начиная с последней указанной до самой новой открытой.
0 1 2 |
for(int i=limit; i>=0; i--) { } |
Теперь в теле цикла необходимо получить данные по каждому из осцилляторов. Благодаря тому, что эти индикаторы уже являются встроенными в терминал МТ4, то их программный код расчета показателей переписывать в наш индикатор не нужно. Для этого есть функции их подгрузки: iRSI, iWPR и iStochastic. Буква i в начале обозначает, что это индикатор. Всего для импорта доступно 37 технических индикатора.
Начнем с RSI. Создадим переменную типа double, которая будет хранить данные индикатора для выбранной свечи:
0 |
double rsi = iRSI(NULL,0,RSI_Period,PRICE_CLOSE,i); |
Перечень параметров функции по порядку:
- Имя символа. Константа NULL обозначает текущий символ.
- Таймфрейм. 0 значит, что должен использоваться текущий ТФ, на котором открыт график.
- Период индикатора. Берем из внешних настроек.
- Тип цены для расчета. По классике берем цену закрытия PRICE_CLOSE
- Сдвиг. Номер бара, для которого необходимо рассчитать показания.
Соответственно для каждой итерации цикла будет рассчитано значение для определенной свечи по порядку, начиная с самой поздней, заканчивая текущей.
Второй индикатор это WPR. Основное отличие только в том, что тут не учитывается тип цены. Код импорта данных выглядит так:
0 |
double wpr = iWPR(NULL,0,WPR_Period,i); |
Осталось импортировать данные стохастика.
0 |
double stoch = iStochastic(NULL,0,StoKPeriod,StoDPeriod,SlowingPeriod,StoMethod,0,MODE_MAIN,i); |
Параметров у этой функции больше, но все интуитивно понятно. Мы перечисляем K, D периоды, замедление, метод усреднения из внешних настроек, а также параметр выбора цены для расчета и индекс линии индикатора. Нас интересует основная линия MODE_MAIN.
Если вы запутались в параметрах этих индикаторов, просто установите каретку на наименование функции и нажмите F1. Появится подробная справка по языку MQL4 с открытой страницей описания выбранной функции.
Теперь полученные значения, сохраненные в переменных необходимо передать в объявленные массивы, чтобы кривые осцилляторов отобразились в подвальном окне индикатора.
0 1 2 |
RSIBuffer[i] = rsi; WPRBuffer[i] = 100+wpr; StochasticBuffer[i] = stoch; |
Единственное замечание тут связано с тем, что у индикатора WPR данные идут от 0 до -100, т.е. нам нужно «отзеркалить» единицы измерений, поэтому прибавляем число 100 к значению переменной wpr.
Осталось создать четвертый буфер данных, который будет суммировать результаты трех индикаторов и делить их на три. Полученное значение необходимо округлить до точности измерения текущего символа.
0 |
SumBuffer[i] = NormalizeDouble((RSIBuffer[i]+WPRBuffer[i]+StochasticBuffer[i])/3,Digits); |
В итоге код будет выглядеть следующим образом:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
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 limit = rates_total-prev_calculated; limit = MathMin(limit,BarsForAnalyze); for(int i=limit; i>=0; i--) { double rsi = iRSI(NULL,0,RSI_Period,PRICE_CLOSE,i); double wpr = iWPR(NULL,0,WPR_Period,i); double stoch = iStochastic(NULL,0,StoKPeriod,StoDPeriod,SlowingPeriod,StoMethod,0,MODE_MAIN,i); RSIBuffer[i] = rsi; WPRBuffer[i] = 100+wpr; StochasticBuffer[i] = stoch; SumBuffer[i] = NormalizeDouble((RSIBuffer[i]+WPRBuffer[i]+StochasticBuffer[i])/3,Digits); } return(rates_total); } |
Установим индикатор на график и посмотрим, что получилось.
Как и предполагалось, у нас получилось 4 кривых линии. Голубая линия выглядит невыразительно на фоне серых. Изменим ее толщину в свойствах indicator_width4.
0 |
#property indicator_width4 3 |
Не хватает только горизонтальных линий перекупленности/перепроданности. Давайте зададим количество линий INDICATOR_LEVELS равное двум и их значения равными 20 и 80 в функции обработки событий OnInit.
0 1 2 |
IndicatorSetInteger(INDICATOR_LEVELS,2); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,20); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,80); |
Посмотрим, что получилось
Для проверки корректности всех указанных данных в настройках можно вручную установить все три индикатора по очереди на график и сравнить их показания как визуально:
так и с помощью окна данных. Все должно сойтись, кроме данных по WPR, к ним прибавлено число 100.
Давайте добавим последний штрих. Предположим, что данный индикатор должен быть сигнальным на открытие сделок для пользователей, которые предпочитают ручную торговлю. Для этого он должен стрелочками указывать где именно должен осуществляться вход в покупки и продажи. Предположим, по классике что линия перекупленности находится на отметке 80 и когда индикатор выше, либо равен ей, то пришло время продавать. В свою очень при значении индикатора ниже линии 20 — время покупать.
Давайте запишем пользовательскую функцию создания графического объекта типа «Стрелка».
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
bool ArrowCreate(const long chart_ID=0, // ID графика const string name="Arrow", // имя стрелки const int sub_window=0, // номер подокна datetime time=0, // время точки привязки double price=0, // цена точки привязки const uchar arrow_code=252, // код стрелки const ENUM_ARROW_ANCHOR anchor=ANCHOR_BOTTOM, // положение точки привязки const color clr=clrRed, // цвет стрелки const int width=3) // размер стрелки { ResetLastError(); if(ObjectFind(chart_ID,name) == -1) { if(!ObjectCreate(chart_ID,name,OBJ_ARROW,sub_window,time,price)) { Print(__FUNCTION__,": не удалось создать стрелку! Код ошибки = ",GetLastError()); return(false); } } ObjectSetInteger(chart_ID,name,OBJPROP_ARROWCODE,arrow_code); ObjectSetInteger(chart_ID,name,OBJPROP_ANCHOR,anchor); ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr); ObjectSetInteger(chart_ID,name,OBJPROP_WIDTH,width); return(true); } |
Добавляем следующие строки в самый низ цикла for.
0 1 2 |
int wnd=ChartWindowFind(); if(SumBuffer[i] <= 20) ArrowCreate(0,"DavinciB "+IntegerToString(i),wnd,Time[i],SumBuffer[i]-1,241,ANCHOR_TOP,clrGreen,4); if(SumBuffer[i] >= 80) ArrowCreate(0,"DavinciS "+IntegerToString(i),wnd,Time[i],SumBuffer[i]+1,242,ANCHOR_BOTTOM,clrRed,4); |
В первом условном операторе if проверяется условие, когда значение индикатора ниже или равно 20, во втором выше или равно 80. При результате true создается графический объект с параметрами:
- ID графика. В нашем случае 0.
- Уникальное имя объекта. Для уникальности прибавляем к имени номер итерации.
- Номер подокна индикатора. Сохранен в переменную wnd.
- Время на графике для построения стрелки.
- Значение индикатора для стрелки с отступом в единицу, чтобы избежать наложения на линию.
- Код графического объекта в виде шрифта Wingdings, для стрелок это 241 и 242
- Привязка к стрелке снизу или сверху
- Цвет графического объекта
- Толщина объекта.
Все остальные параметры были мою удалены из функции за ненадобностью. В итоге окончательная версия индикатора приняла следующий вид:
Заключение
Мы создали очень простой, компактный код всего на 140 строк. Научились импортировать данные стандартных индикаторов терминала MT4 и писать свой индикатор в отдельном окне.
В свое время я создавал для нашего сайта по подобному сценарию мультивалютный индикатор Multi Stochastic. По ссылке можно изучить его метод работы и при желании повторить это в вашем коде.
Остается последний вопрос. Можно ли использовать написанный нами индикатор в торговле? Скорее да, чем нет. Нужно проверить его эффективность на истории котировок и получить положительную статистику. Это можно сделать вручную, либо написать простенький советник, что мы научимся делать в дальнейших уроках. Но так или иначе, торговать только по сигналам одного индикатора некорректно. Для создания полноценной торговой системы нужно куда больше данных, чем предполагаемые точки входа/выхода по одному осциллятору.
Если наш урок вам был полезен, подпишитесь на рассылку и поделитесь им с друзьями.
[download url=»http://www.davinci-fx.com/wp-content/uploads/2021/01/2.7-RSIWPRStoch-indicator.rar» title=»Скачать код урока»]
7 комментариев. Оставить новый
Здравствуйте! Спасибо большое за урок, только благодаря ему понял, как объединять и выводить стрелки. Но мучает еще один вопрос)) Как сделать, чтобы при пересечении линий стрелки показывались в главном окне терминала, а не в подвале?
В настройках функции ObjectCreate есть параметр, отвечающих за окно, в котором будет отображение. Главное окно имеет номер 0. Т.е. нужно в настройках пользовательской функции ArrowCreate изменить номер окна и цену отображения
Благодарности моей нет предела. Всех благ, мил человек.
Вот зараза)) я то думал, добавив Ваши 2 строки в код индикатора, будет и в подвале и в главном окне, а не. Почему-то либо отображение либо там, либо там. Дублировать, в двух окнах, почему-то не хочет
Вы создаете два элемента с одним и тем же именем, один в итоге заменяет второй. Нужно оставить тот код, что был и добавить две новые строчки из прошлого сообщения с уникальным именем.
А если только касание уровня 80, по идее должно быть SumBuffer[i] == 80, но не работает)
Цена не может быть именно на уровне 80, это большая редкость, чтобы так совпало. Поэтому ничего и не отображается. В данном случае нужно оставить, как есть знак больше или меньше какого-либо уровня.