Перенесення коду індикатора на код експерта

  1. MetaQuotes Software Corp. Особливості написання індикаторів користувача. https://www.mql5.com/ua/articles/1497
  2. Микола Косіцин. Багаторазовий перерахунок нульового бару у деяких індикаторах. https://www.mql5.com/ua/articles/1411

Перш, ніж почати розмову на тему, яку я позначив у назві статті, слід було поставити цілком резонне питання: "А навіщо взагалі потрібно переносити код індикатора в код експерта, якщо в більшості випадків експерт, який працює з індикаторами користувача, виглядає незрівнянно простіше свого аналога , який містить усі необхідні для своєї роботи індикатори користувача всередині свого коду?

На мій погляд, це необхідно лише у двох випадках:

  1. Якщо в розрахунках експерта абсолютно не використовуються значення, розраховані на нульовому барі, виникає цілком природне бажання позбутися в експерті від непотрібних перерахунків на нульовому барі, а так само і на першому барах. Це дозволило б утричі скоротити час оптимізації такого експерта, що за дуже складного та ресурсомісткого коду досить актуально!
  2. Комерційне використання експерта з максимальним захистом коду від його декомпілювання.

У другий випадок ситуація цілком зрозуміла і перенесення коду виявляється цілком обгрунтованим. У той час, як у першому випадку, на мій погляд, у більшості ситуацій незрівнянно простіше буде трохи переписати код індикаторів користувача, позбавивши ці індикатори можливості непотрібних для експерта обчислень! Цілком природно, що такі спеціалізовані індикатори будуть придатними.лише для експертів, а не для трейдингу! З огляду на цей варіант вирішення проблеми ми і почнемо статтю.

Приклади оптимізації індикаторів

Насамперед звернемо увагу читача на наступний фрагмент коду індикатора користувача:

У цьому випадку нас буде цікавити рядок:

Суть цієї перевірки зі зменшенням значення змінної counted_bars на одиницю така, що якщо її не зробити, то індикатор користувача за відсутності цього рядка, при зміні нульового бару може надіслати зі своїх буферів в експерта невірні значення з усіма наслідками, що з цього випливають. Форма кривої індикатора в експерті матиме "зажований вигляд"!

У індикаторах користувача змінні limit і counted_bars можуть мати інші назви, але сам програмний код повинен мати ці перевірки! Я так вважаю, що цього пояснення буде цілком достатньо, щоб пролити світло істини на суть заяв деяких, які не бажають вникати в тонкощі програмування експертописців, що в Метатрейдері дані буфера індикатора і ті ж дані, отримані з індикатора користувача в експерті не збігаються! Якщо код індикатора і код експерта написані правильно, то як складним би не був код індикатора, дані в експерті виявляються однаково тими самими!

Але тут доречно помітити, деякі алгоритми згладжування чутливі до початковій точці відліку, з якою виробляється згладжування. Тобто для збігу значень слід забезпечити збіг номерів найстаріших барів у циклах як в індикаторі, так і в коді індикатора всередині експерта, з яких відбувається повний перерахунок всіх барів.

Мені так здається, що цілком вистачить одного прикладу, щоб зрозуміти зміст даного методу оптимізації коду індикатора для його більшшвидкої роботи у експерті. Змінюємо в основному циклі індикатора нуль на одиницю, після чого індикатор перестає перераховувати своє значення на нульовому барі.

У результаті вихідний код виглядатиме так:

Я ще раз наголошу на тому факті, що все вище сказане було зроблено для експертів, які працюють тільки на закритих барах, тобто на всіх барах, крім нульового!

Передбачається, що читач знайомий з оптимізацією коду індикатора для того, щоб цей індикатор перераховувався на нових тиках лише на необчислених барах. На мій погляд, починати вивчення програмування мовою MQL4 слід було б саме з цих речей.

Якщо ви всерйоз зібралися використовувати свого експерта в реальній торгівлі та довіряти йому свої гроші, то вам слід педантично перевіряти у своєму експерті кожну дрібницю, у тому числі і індикатори, з якими він працює. До того ж робити це самому! Та й взагалі, на мій погляд, набагато простіше і розумніше один раз за пару днів розібратися в пристрої індикатора і методах оптимізації його коду, ніж три місяці поспіль терпляче сидіти біля експерта, що оптимізується, що отримує значення з коряво, як курка лапою написаного індикатора!

Я так вважаю, що після всіх цих пояснень у читача має зменшитися бажання без серйозних підстав переносити код індикатора код експерта. Якщо все зробити в індикаторі правильно та оптимально, то і без цього експерт працюватиме не набагато повільніше. Взагалі, спочатку набагато легше і простіше накидати код експерта з використанням індикаторів і всю налагоджувальну частину роботи виробляти в такому вигляді. Ну а якщо після цього ваш експерт показує дійсно визначні результати, то тільки тоді приступати до подальшої оптимізації коду, поодному замінюючи звернення до індикаторів користувача на фрагменти коду індикаторів.

При цьому слід враховувати той факт, що ніяких змін значень прибутковості та збитковості, що тестуються, після заміни коду в експерті не повинно бути! На мій погляд, є сенс розглянути тільки найпростіші випадки перенесення, коли індикатор має більш-менш розумні розміри коду. В іншому випадку рівень складності завдання може виявитися таким, що для його вирішення знадобиться кваліфікація програміста, за якої такого фахівця вже нічого вчити не треба!

Загальна схема будови індикатора

Цілком природно, що у реального індикатора може бути інша кількість індикаторних значень, інша кількість індикаторних буферів, що беруть участь у розрахунках та інша кількість циклів розрахунку значень індикаторних буферів, але це ніяк не змінює суті розуміння запропонованої структурної схеми. В інших випадках все буде абсолютно аналогічним.

Тепер видалимо з цієї схеми всі зайві елементи, які нас у цій ситуації абсолютно не цікавлять і в експерті абсолютно ні до чого:

За великим рахунком, цілком можна було б без особливих проблем помістити цей код код експерта, якби не пара маленьких прикрих непорозумінь!

  1. Насамперед слід врахувати той факт, що функція IndicatorCounted() в експертах не працює!
  2. Зробити масиви індикаторними в блоці ініціалізації ми теж не зможемо!

Так що для повного збереження коду індикатора насамперед нам слід зробити аналог функції IndicatorCounted() і якимось чином емулювати аналоги індикаторних буферів в експерті. емуляцію індикаторних буферів в експерті, на жаль, за допомогою стандартних функцій безпосередньо зробити не вийде.Аналогів для SetIndexBuffer() та IndicatorBuffers() для експертів на даний момент немає! Так що цю проблему нам доведеться обходити з іншого боку, благо можливостей у MQL4 для цього достатньо.

Емуляція індикаторних буферів в експерті

Спочатку розглянемо самі процеси, що відбуваються в індикаторних буферах трохи докладніше.

  1. Значення, які присвоюються змінним індикаторного буфера, не губляться між циклами, доки індикатор приєднано до графіка та працює термінал.
  2. Якщо відбувається зміна нульового (найновішого) бару на графіку, то всі елементи індикаторного буфера зсуваються.
  3. Якщо прийшов один новий бар, значення змінної limit змінилося з одиниці на двійку в індикаторі!, а всі елементи буфера зрушилися на одиницю, тобто нульовий елемент став першим, перший - другим, другий - третім і так далі.
  4. Цілком природно, що розмірність індикаторного буфера змінюється. Якщо на черговому тику нульовий бар залишився тим самим, то всі елементи буфера залишаються на своїх місцях.

Тепер займемося емуляцією індикаторних буферів. Для цього ми скористаємося наступними стандартними функціями мови MQL4: ArraySize(), ArrayResize() та ArraySetAsSeries(). Сам код емуляції індикаторних буферів досить простий і суть його роботи можна сформулювати двома словами наступним чином: при зміні нульового бару відновлюється прямий порядок визначення елементів у буферах, робиться за допомогою функції ArrayResize() додавання нових осередків у буферах з боку нових барів, а після цього змінюється на зворотний порядок визначення елементів у буферах, і порожні комірки опиняються в емульованому індикаторному буфері серед найперших.

До речі, цей метод емуляції індикаторних буферів можна використовуватиі в індикаторах у випадку, якщо для проміжних розрахунків індикатора восьми буферів не вистачає. Як це робиться, можна побачити, якщо переглянути файли SMI.mq4 і SMI_New.mq4.

індикатора

Заміна функції IndicatorCounted().

Тепер слід розібратися з емуляцією функції IndicatorCounted(). Ця функція повертає кількість барів поточного графіка, які не змінені після останнього виклику індикатора. Якщо трохи перефразувати це визначення, можна сказати так. Ця функція повертає кількість барів поточного графіка, які були в наявності у клієнтському терміналі на попередньому тику! Для повного збігу значень від отриманої кількості барів слід відкинути одиницю. Так що цю функцію ми можемо запросто замінити статичною цілою змінною, яку після отримання значення ініціалізувати значенням зумовленої змінної Bars-1. Після цього індикаторна схема матиме такий вигляд:

Подальші перетворення коду індикатора та підсумкова схема його будови

Звичайно, в такому вигляді можна було б без проблем послідовно частинами перетягнути код індикатора в код експерта, якщо припустити, що в експерті нам цей індикатор потрібен для роботи на поточному графіку і один раз! Зрозуміло, якщо індикатор використовується двічі, можна просто у другому випадку змінити імена всіх змінних індикатора і додати отриманий код ще раз. У цьому випадку станеться певне ускладнення експерта.

З обробкою даних з інших таймфреймів також все досить просто вирішується. Замінюємо в індикаторі визначені змінні типу Bars на таймсерії типу

NULL - на string symbol;, 0(у таймсеріях) - на int timeframe;, Close[bar] - на

Тепер слідрозібратися з рядками індикатора:

У плані запропонованої заміни функції IndicatorCounted() у нашому варіанті побудови індикатора змінна counted_bars ніколи не буде меншою за нуль, так що рядки:

у коді експерта можна видалити. З наступними двома рядками:

історія та сама, їх слід видалити, тому що вони тільки уповільнюють марними перерахунками першого бару роботу експерта і в коді самого експерта ця перевірка абсолютно марна. Після цього підсумковий код, що залишився для перенесення в експерта, став виглядати наступним чином:

Але тут слід зважити на один момент. Існують індикатори, які при багаторазовому перерахунку нульового бару на першому барі на початку циклів розрахунку запам'ятовують значення деяких змінних для повернення коду у вихідний стан (Стаття). У експерті це запам'ятовування після видалення останніх двох рядків має відбуватися не першому, але в нульовому барі. Зазвичай у таких індикаторах присутні на початку циклів розрахунку індикатора такі фрагменти коду:

В результаті маємо наступний переписаний фрагмент:

У коді індикатора також можуть бути розроблені мною функції згладжування типу XXXSeries(). Для того, щоб використовувати в коді експерта фрагменти коду індикатора з такими функціями, ці функції слід замінити на експертні аналоги.