Содержание
Всем доброго времени суток. На прошлых уроках по программированию советников на языке MQL4 мы использовали данные классических индикаторов MA, Stochastic, MACD и т.д., непосредственно встроенных в торговый терминал Meta Trader 4. В данном случае функция импорта данных уже встроена в редактор Meta Editor и разобраться в ней не составит труда. Но что делать, если вы нашли сторонний индикатор, который удовлетворяет вашим требованиям, и вы жаждете попробовать его в своем советнике?
В этом уроке мы разберем, как использовать функцию iCustom на примере двух рандомных индикаторов и импортируем их данные в наш простенький советник. Почему двух? Потому что один будет с открытым кодом, а второй с закрытым.
Стоит сразу уточнить, что индикаторы, используемые в примере, не являются рекомендацией для применения в ваших советниках. Они были выбраны мною наобум с единственной целью — продемонстрировать работу функции импорта iCustom.
Основы импорта данных индикаторов
Большинство индикаторов являются трендовыми, осцилляторами, стрелочниками и т.д., т.е. содержат в себе какую- то числовую информацию, на основе которой трейдер принимает решение об открытии, либо закрытии своих позиций. Данная информация храниться в массивах, называемых буферами данных, или индексами. В зависимости от того, сколько индикатор содержит в себе расчетных данных и зависит количество буферов. В MT4 оно ограничено восьмью.
Возьмем, к примеру классический индикатор Bollinger Bands. Он отображает на графике три линии — среднюю цену по МА и канал, построенный сверху и снизу от нее.
Соответственно, каждая из трех линий имеем свое значение на всех свечах, участвующих в расчетах. Чтобы узнать это значение, нужно открыть «Окно Данных» во вкладке «Вид», либо нажать Ctrl+D.
В этом окне отображаются OHLC цены текущей свечи, на которую наведена мышь, а также данные всех индикаторов, что установлены на этом графике. В нашем случае это BB и тремя буферами: Band SMA (средняя линия), Bands Upper (верхняя линия) и Bands Lower (нижняя линия).
Нужно заметить, что отсчет нумерации буферов для импорта начинается не с единицы, а с нуля. Соответственно в настройках импорта для получения значений нижней линии мы будем использовать второй буфер данных.
Стоит заметить, что не все индикаторы хранят свою информацию в буфере данных. Это могут быть информационные панели, текстовые или графические индикаторы с отображением линий по определенным ценовым значениям. Также, может быть такое, что автор индикатора просто не захотел использовать буферы для расчета значений. В таком случае нужно понимать, что вы не сможете импортировать данные такого индикатора в своей советник, если у вас только нет открытого кода и вы не возьметесь переписывать его.
Создаем простенькую основу советника
Это уже пятый урок на тему написания советников, поэтому учить как создать новый советник я, извольте, не буду. После создания шаблона добавляем в него встроенную библиотеку расшифровки ошибок
0 1 2 3 4 5 6 7 8 9 10 |
//+------------------------------------------------------------------+ //| 3.4 User indicator's EA | //| Copyright (c) DaVinci FX Group | //| https://www.davinci-fx.com/ | //+------------------------------------------------------------------+ #property copyright "Copyright (c) DaVinci FX Group" #property link "https://www.davinci-fx.com/" #property version "1.00" #property strict #include <..\Libraries\stdlib.mq4> //библиотека для расшифровки ошибок |
Так как мы попробуем импортировать данные двух индикаторов, то нам нужен в коде переключатель, чтобы выбрать какой из индюков в данный момент будет использоваться для торговли. То есть у нас получится советник по двум несвязанным системам. Для этого воспользуемся типом перечисления enum. Пишем ниже следующий код:
0 1 2 3 4 5 6 |
enum ind { SSRC = 1, TMA_Fair = 2, }; extern string s1 = "<== Indicators settings ==>"; //> > > extern ind IndicatorType = 1; |
Соответственно SSRC это первый индикатор, TMA Fair — второй. В окне настройки параметров вы уже сами выберете какой из индикаторов использовать. Далее создаем стандартный перечень внешних параметров extern для советника: СЛ, ТП, проскальзывание, магик и т.д.
0 1 2 3 4 5 6 |
extern string s0 = "<== General Settings ==>"; //> > > extern double Lot = 0.01; extern int Slippage = 5; extern double StopLoss = 20; extern double TakeProfit = 20; extern string Comments = "DaVinci EA"; extern int MagicNumber = 123123; |
Для сохранения данных индикаторов в памяти советника введем несколько переменных глобального уровня. Данные индикаторов мы будем получать только один раз в свечу, после ее закрытия. Для этого добавим уже известную нам с прошлых уроков переменную Update_Time.
0 1 2 3 |
double ssrc1 = 0, ssrc2 = 0; double tma_high = 0, tma_low = 0; datetime Update_Time = 0; |
Добавляем функцию обработки событий OnInit с переводом значений в пунктах в 4 или 5 знак. Функцию OnDeinit в нашем примере мы не используем.
0 1 2 3 4 5 6 7 8 9 10 11 12 |
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { if (Digits == 3 || Digits == 5) { Slippage *= 10; StopLoss *= 10; TakeProfit *= 10; } return(INIT_SUCCEEDED); } |
В функцию OnTick не забываем добавлять проверку на ошибки. Пока что она будет иметь следующий вид:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { if(Update_Time != iTime(NULL,0,0)) { //обновлять данные раз в период //...Будущий код Update_Time = iTime(NULL,0,0); } int Error = GetLastError(); //поиск ошибок по завершению тика if(Error != 0) Print("OnTick() Error ",Error,": ",ErrorDescription(Error)); } |
Для полноценной работы советника в него нужно добавить функцию-счетчик уже открытых сделок, чтобы запретить открытие новых ордеров, пока в рынке есть существующие позиции.
0 1 2 3 4 5 6 7 8 9 10 |
int CountOrder() { //Счетчик ордеров советника в рынке int orders=0; for(int i=OrdersTotal()-1;i>=0;i--){ if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)==false) continue; if(OrderSymbol() !=_Symbol || OrderMagicNumber() != MagicNumber) continue; if(OrderType() > 1) continue; orders++; } return orders; } |
Ну и в конце подготовительного этапа мы добавляем самую основную пользовательскую функцию открытия и модификации ордеров. Расписывать ее я не буду, если что-то непонятно, посмотрите мой первый урок по написанию советников.
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 |
bool OpenTrade(int OP_Type) { //Функция открытия и модификации ордеров double price = (OP_Type == OP_BUY ? Ask : Bid); color col_type = (OP_Type == OP_BUY ? clrBlue : clrRed); string op_str = (OP_Type == OP_BUY ? "на покупку" : "на продажу"); Print("Открываем ордер " + op_str); int ticket = OrderSend(Symbol(), OP_Type, Lot, price, Slippage, 0, 0, Comments, MagicNumber, 0, col_type); if(ticket > 0) { //Если ордер был открыт //выделяем ордер и производим его модификацию if(OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES)) { double SL = 0, TP = 0; if(OP_Type == OP_BUY) { SL = OrderOpenPrice()-StopLoss*Point; TP = OrderOpenPrice()+TakeProfit*Point; } else { SL = OrderOpenPrice()+StopLoss*Point; TP = OrderOpenPrice()-TakeProfit*Point; } Print("Модифицируем ордер " + op_str); for(int n = 0; n<5; n++) { //цикл на случай, если ордер не был модифицирован. if(!OrderModify(ticket, OrderOpenPrice(), SL, TP, 0, clrNONE)) { int Error = GetLastError(); Print("Ошибка модификации ордера ",Error,": ",ErrorDescription(Error)); Sleep(3000); //сон на 3 секунды RefreshRates(); //обновление данных в предопределенных переменных } else break; //успешная модификация, остановка цикла } } } else { //если ордер не открыт - уведомление об ошибке int Error = GetLastError(); Print("Ошибка открытия ордера ",Error,": ",ErrorDescription(Error)); return(false); } return(true); } |
Импорта данных индикатора SSRC
Данный индикатор достался мне с открытым кодом, что в разы ускоряет его присоединение к коду. Во-первых, вам не нужно вручную прописывать все его внешние настройки, а просто взять и скопировать их. Во-вторых, вы можете сами решить, какие настройки стоит выводить как внешние, а какие нет.
Сама программа представляет собой подвальный индикатор осциллятор и имеет только один индекс кривой зеленой линии:
Открываем его код:
Мы видим четыре внешних параметра. Лично я не буду разбираться за что каждый из них отвечает, пусть за меня это сделает оптимизация. Поэтому просто вставляем их все во внешние переменные нашего советника.
Вы можете прописать только несколько переменных индикатора для настройки советника, а остальные, известные вам данные уже подставить в виде числового значения в саму функцию iCustom. Я же в данном примере использую все предложенные индикатором настройки.
0 1 2 3 4 |
extern string s2 = "<= SSRC Settings =>"; //> > > extern int SnakeRange = 3; extern int FilterPeriod = 21; extern double MartFiltr = 2; extern int PriceConst = 6; |
Переходим к функции OnTick.
iCustom
Для того чтобы не запутаться в настройках функции iCustom существует справка. Вам просто нужно прописать эту функцию в код, поставить на нее каретку и нажать F1. Но мы все равно рассмотрим список ее параметром:
- symbol — имя символа, для которого нужно импортировать данные. Текущий символ обозначается как NULL.
- timeframe — период импортированного индикатора. Текущий период обозначается нулем 0.
- name — полное наименование индикатора, как он назван автором. По умолчанию путь к нему указывается следующий: …MQL4\Indicators. Если вы используете дополнительную папку для индикатора, то перед его именем нужно подставить наименование папки: «Папка\\имя_прибыльного_индикатора».
- … — перечень абсолютно всех параметров, что есть у данного индикатора по внешних переменных. Всех должны идти строго по очереди.
- mode — источник данных. Это номер буфера (индекса) индикатора, которые отображается в «Окне Данных». Счет начинается с нуля.
- shift — сдвиг по номеру свечи справа на лево. Т.е. текущая свеча будет равна 0, первая закрытая будет со значением 1, вторая закрытая — 2 и т.д.
По нашей, только что придуманной стратегии торговли, для входа в покупки нам нужно чтобы индикатор на [1] свече был выше -0.75 и на [2] свече ниже -0.75. Т.е. линия начала выходить из зоны перепроданности. Продажи зеркально.
Для этой немудреной ТС нам нужны данные индикатора для двух свечей, т.е. импорт нужно произвести два раза. Прописываем импорт iCustom, если переменная IndicatorType из настроен равна SSRC.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
void OnTick() { if(Update_Time != iTime(NULL,0,0)) { //обновлять данные раз в период //импортируем данные первого индикатора ssrc if(IndicatorType == 1) { ssrc1 = iCustom(NULL,0,"ssrc",SnakeRange,FilterPeriod,MartFiltr,PriceConst,0,1); ssrc2 = iCustom(NULL,0,"ssrc",SnakeRange,FilterPeriod,MartFiltr,PriceConst,0,2); Print("SSRC: [1]:", DoubleToString(ssrc1,4), ", [2]:", DoubleToString(ssrc2,4)); } //... Будущий код второго индикатора Update_Time = iTime(NULL,0,0); } int Error = GetLastError(); //поиск ошибок по завершению тика if(Error != 0) Print("OnTick() Error ",Error,": ",ErrorDescription(Error)); } |
Нами получены две переменный ssrc1 и ssrc2 для [1] и [2] свечи. Посмотрим перечень параметров, что мы задали функции для переменной ssrc1:
- NULL — текущий символ
- 0 — текущий ТФ
- «ssrc» — наименование индикатора точь-в-точь как он назван в папке, в которой лежит
- SnakeRange, FilterPeriod, MartFiltr, PriceConst — перечень параметров подряд в той последовательности, что они указаны во внешних значениях самого индикатора, притом желательно, чтобы учитывались все внешние переменные.
- 0 — номер буфера для импорта. Т.к. в индикаторе только один буфер, мы ставим ноль.
- 1 — номер свечи, с которой нужно получить данные.
Для переменной ssrc2 различие будет только в номере свечи, 2 вместо 1.
Ниже в коде прописана функция Print только для того, чтобы вы наглядно могли убедиться, что значения внешнего индикатора переданы верно. Без нее в данном деле никак, проверку нужно совершать каждый раз, пока вы не набьете руку. Т.е. нужно прогнать советник на небольшом периоде времени, остановить тестирование. Индикатор появится на графике, после этого вы можете сравнить значение по определенной свече в принте со значением в «Окне Данных».
Посмотрим на скриншот и информацию в журнале. Если навести мышь на первую закрытую свечу, мы увидим, что значение кривой линии и данных индекса совпадают. То же самое и в журнале [1]:0.99 является округленным значением 0.9912 из окна данных.
Импорта данных индикатора TMA Fair
Индикатор TMA Fair представляет собой канал на графике с тремя индексами — верхняя, нижняя и средняя линия. Сравните значения, которые я проставил в красных ценовых метках и в «Окне Данных», они сходятся:
Данный индикатор не имеет открытого кода, поэтому его настройки необходимо прописывать в наш код вручную:
Стоит заметить, что этот авторский индикатор и он используется как сигнальный с кучей алертов. Естественно для нашего кода они ни к чему, поэтому их мы переписывать в советник не будем.
0 1 2 3 4 5 6 |
extern string s3 = "<= TMA Fair Settings =>"; //> > > extern string TimeFrame = "current time frame"; extern int HalfLength = 56; extern int Price = PRICE_CLOSE; extern double ATRMultiplier = 2.0; extern int ATRPeriod = 100; extern bool Interpolate = true; |
Наша стратегия будет до банальности проста: цена пересекла верхний канал и закрепилась за ним — продаем, нижний — покупаем.
Прописываем импорт данных в функцию OnTick ниже кода, что мы написали для первого индикатора.
0 1 2 3 4 5 6 |
//импортируем данные второго индикатора TMA_Fair if(IndicatorType == 2) { tma_high = iCustom(NULL,0,"TMA_Fair",TimeFrame,HalfLength,Price,ATRMultiplier,ATRPeriod,Interpolate,false,false,false,false,false,false,1,1); tma_low = iCustom(NULL,0,"TMA_Fair",TimeFrame,HalfLength,Price,ATRMultiplier,ATRPeriod,Interpolate,false,false,false,false,false,false,2,1); //Print("TMA: High:", DoubleToString(tma_high,Digits), ", Low:", DoubleToString(tma_low,Digits)); } |
Есть вы заметили, в конце передаваемых внешних параметров мы шесть раз прописали false. Это нужно потому, что мы не хотим использовать шесть алертов, что предлагает данный индикатор, но т.к. они уже есть в коде в виде внешних переменных, то мы должны их указать в отключенном виде, т.е. false.
Если вы используете индикатор, где в самом конце есть внешние переменные, значение которых вы готовы оставить по умолчанию, то код можно укоротить. На примере данного кода до:
0 1 |
tma_high = iCustom(NULL,0,"TMA_Fair",TimeFrame,HalfLength,Price,ATRMultiplier,ATRPeriod,Interpolate,1,1); tma_low = iCustom(NULL,0,"TMA_Fair",TimeFrame,HalfLength,Price,ATRMultiplier,ATRPeriod,Interpolate,2,1); |
Но в данном случае вы должны понимать, что если значение хоть одного из алертов будет по умолчанию равно true, то это приведет к ненужным расчетам и замедлит советник. Моя рекомендация: лучше не лениться и прописать все внешние параметры, что есть у индикатора, это поможет избежать ненужных ошибок при кодинге.
Естественно, если вы знаете, что не будете настраивать индикатор и будете использовать его значения по умолчанию, то можно обойтись без некоторых, а то и всех внешних переменных:
0 1 |
tma_high = iCustom(NULL,0,"TMA_Fair","current time frame",56,PRICE_CLOSE,2.0,100,true,false,false,false,false,false,false,1,1); tma_low = iCustom(NULL,0,"TMA_Fair","current time frame",56,PRICE_CLOSE,2.0,100,true,false,false,false,false,false,false,2,1); |
Дописываем условия открытия ордеров
Осталось всего одно действие: ниже прописать условия для открытия советником ордеров на покупку и продажу в зависимости от выбранного типа индикатора:
0 1 2 3 4 5 6 7 8 |
if(CountOrder() == 0) { //если в рынке нет открытых ордеров if((IndicatorType == 1 && ssrc1 > -0.75 && ssrc2 <= -0.75) || (IndicatorType == 2 && Bid <= tma_low)) { if(OpenTrade(OP_BUY)) Print("Ордер на покупку открыт и модифицирован"); } if((IndicatorType == 1 && ssrc1 < 0.75 && ssrc2 >= 0.75) || (IndicatorType == 2 && Bid >= tma_high)) { if(OpenTrade(OP_SELL)) Print("Ордер на продажу открыт и модифицирован"); } } |
Соответственно, если ордеров у этого советника нет в рынке CountOrder() == 0, то должно совпасть одно из двух условий, что мы обсуждали выше, чтобы ордер на покупку или продажу был открыт.
На покупку (продажи зеркально):
- IndicatorType = 1 и ssrc1 > -0.75 && ssrc2 <= -0.75
- IndicatorType = 2 и цена Bid <= tma_low
На графике вход первого индикатора ssrc выглядит так:
Вход индикатора TMA Fair:
Тестировать советник в этом уроке я не буду, с настройками по умолчанию и абсолютно рандомными индикаторами в этом просто нет никакого смысла. Но если вам понравилась данная идея — творите, ничто вас не останавливает.
Заключение
Функция iCustom является очень полезной и альтернативной, если вы устали от стандартных индикаторов МТ4. Она позволит добавить авторский взгляд в код вашего советника и возможно сделает его более прибыльным. Но не стоит забывать делать проверку принтом при импорте данных, потому что новички зачастую делают большое число ошибок именно в заполнении параметров функции и долго ломают голову из-за того, что код работает не корректно.
Не забудьте ознакомиться с нашими бесплатными Скриптами и Индикаторами.
На сегодня это все. Код с примером и индикаторами во вложении.
Рекомендуйте наши уроки заинтересованным друзьям. Всем профитов!
[download url=»http://www.davinci-fx.com/wp-content/uploads/2021/05/Код-урока-3.4.rar» title=»Скачать файлы урока»]