• Главная
  • Карта сайта
Не найдено

Теорія адаптивних індикаторів і її реалізація в MQL5

  1. Вступ
  2. Використання комплексних чисел і фазових векторів для вимірювання ринкових циклів
  3. Вимірювання періоду циклу
  4. Індикатор періоду циклу (Cycle Period indicator)
  5. Індикатор Cyber ​​Cycle
  6. Адаптивний індикатор Cyber ​​Cycle
  7. Індикатор Center of Gravity
  8. Адаптивна версія індикатора Center of Gravity
  9. індикатор RVI
  10. Адаптивна версія індикатора RVI
  11. Висновки

Вступ

Матеріали цієї статті засновані на двох відмінних книгах Джона Елерса "Rocket Science for Traders" і "Cybernetic Analysis for Stock and Futures" .

Незвичайний підхід до аналізу ринків методами цифрової обробки сигналів і застосування комплексних чисел для розпізнавання ринкових циклів змусив мене заглибитися в цю тему і згодом реалізувати в MQL5 три адаптивних індикатора, запропонованих Джоном Елерса.

У цій статті будуть описані основи теорії адаптивних індикаторів і її реалізація на мові MQL5. Крім того, ми порівняємо адаптивні індикатори з їх неадаптивними аналогами.

Використання комплексних чисел і фазових векторів для вимірювання ринкових циклів

Деякі поняття теорії комплексних чисел можуть привести в здивування читачів, які не мають технічної освіти, тому перед прочитанням статті я рекомендую ознайомитися з теорією комплексних чисел в wikipedia і подивитися підручник за операціями з комплексними числами.

фазовий вектор

Фазовий вектор (Phase Vector) - це вектор, що показує амплітуду і фазу періодичного процесу (циклу). згідно формулою Ейлера комплексне число (що складається з дійсної та уявної частини) можна уявити в показовою формі, де в аргументом в фазі є кут. Це дозволяє наочно ілюструвати періодичні процеси.

На малюнку нижче наведено відео, що ілюструє обертання фазового вектора синусоїди.


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

Я уявляю це наступним чином: повний оборот фазового вектора на 360 градусів (або радіан) є однаковим для всього циклу. Поточний кут фазового вектора показує, в якій частині циклу (фази) ми знаходимося в даний момент. Ось Y відображає поточне значення амплітуди періодичного процесу.

Фазовий вектор може бути розкладений на 2 компоненти: InPhase (cosine) і Quadrature (sine). Докладне пояснення отримання цих компонент наведено в розділі 6 "Перетворення Гільберта" книги "Rocket Science for Traders" . Цей розділ я рекомендую всім, хто хоче розібратися в деталях.

Зараз нам важливо лише те, що для обчислення адаптивних індикаторів нам потрібно перетворити сигнал (waveform) в комплексний сигнал, що складається з двох компонент. Як ми досягнемо цього? Я згадував про перетворення Гільберта? Саме це воно і здатне зробити.

Вимірювання періоду циклу

Для практичного застосування перетворення Гільберта в своїй книзі Джон Елерс розбив його на ряд з чотирьох елементів.

Вираз для компоненти Quadrature виглядає наступним чином:

а компонента InPhase є ціну, запізнюється на 3 бари:

Обчисливши компоненти InPhase і Quadrature (нагадаємо, це дійсна і уявна частини комплексного числа), можна обчислити зсув фази між поточним і попереднім барами. Для поточного бару фаза дорівнює , Для попереднього бару фаза дорівнює .

Скориставшись тригонометричним тотожністю:

ми отримуємо вираз для диференціальної фази DeltaPhase.

На величину DeltaPhase Джон Елерс наклав додаткові обмеження: вона не може бути негативною і обмежена інтервалом <0.1, 1.1> (довжина циклу від 6 до 63 барів). Виявилося, що для реальних даних DeltaPhase не виглядає гладкою, тому необхідно згладжування.

Найкращим способом згладжування для даних з викидами (spiky data) є медіанний фільтр, тому застосовується медіанне згладжування п'яти даних DeltaPhase, результат поміщається в змінну MedianDelta. Значення MedianDelta, поділене на далі використовується для обчислення періоду основного циклу (Dominant Cycle), який є нашою метою.

В процесі тестування з'ясувалося, що при вимірюванні існує зрушення приблизно 0.5, який потрібно прибирати, тому був доданий відповідний член в розрахунковій формулі. Нарешті, обчислене значення Dominant Cycle двічі згладжується за допомогою EMA зі значеннями alpha, рівними 0.33 and 0.1 відповідно. Рекомендую подивитися книгу "Rocket Science for Traders" , В якій наведена ілюстрація робастности алгоритму на прикладі сінусоподобного сигналу, період якого поступово збільшувався з 6 до 40.

Озброївшись теоретичними знаннями, тепер ми готові до того, щоб реалізувати індикатор CyclePeriod на MQL5.

Індикатор періоду циклу (Cycle Period indicator)

Індикатор Cycle Period складається з двох ліній: лінія cycle, що показує період циклу і сигнальної лінії (trigger line), в якості якої зазвичай береться лінія cycle, запізніла на 1 бар.

Подивившись опис в попередньому розділі і вихідний код в функції OnCalculate (), ви легко зможете зрозуміти, які лінії відповідають за вимір періоду циклу.

#property copyright "Copyright 2011, Investeo.pl" #property link "http://Investeo.pl" #property version "1.00" #property indicator_separate_window #property description "Індикатор CyclePeriod - запропонований Джоном Елерса (John F. Ehlers)" # property description "в книзі \" Cybernetic Analysis for Stocks and Futures \ "" #property indicator_buffers 2 #property indicator_plots 2 #property indicator_width1 1 #property indicator_width2 1 #property indicator_type1 DRAW_LINE #property indicator_type2 DRAW_LINE #property indicator_color1 Green #property indicator_color2 Red #property indicator_label1 "Cycle" #property indicator_label2 "Trigger Line" #define Price (i) ((high [i] + low [i]) / 2.0) double Smooth []; double Cycle []; double Trigger []; double Q1 []; double I1 []; double DeltaPhase []; double InstPeriod []; double CyclePeriod []; input double InpAlpha = 0.07; int OnInit () {ArraySetAsSeries (Cycle, true); ArraySetAsSeries (CyclePeriod, true); ArraySetAsSeries (Trigger, true); ArraySetAsSeries (Smooth, true); SetIndexBuffer (0, CyclePeriod, INDICATOR_DATA); SetIndexBuffer (1, Trigger, INDICATOR_DATA); PlotIndexSetDouble (0, PLOT_EMPTY_VALUE, 0.0); PlotIndexSetDouble (1, PLOT_EMPTY_VALUE, 0.0); return (0); } 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 []) {long tickCnt [1]; int i; int ticks = CopyTickVolume (Symbol (), 0, 0, 1, tickCnt); if (ticks! = 1) return (rates_total); double DC, MedianDelta; Comment (tickCnt [0]); if (prev_calculated == 0 || tickCnt [0] == 1) {int nLimit = rates_total-prev_calculated- 1; ArraySetAsSeries (high, true); ArraySetAsSeries (low, true); ArrayResize (Smooth, Bars (_Symbol, _Period)); ArrayResize (Cycle, Bars (_Symbol, _Period)); ArrayResize (CyclePeriod, Bars (_Symbol, _Period)); ArrayResize (InstPeriod, Bars (_Symbol, _Period)); ArrayResize (Q1, Bars (_Symbol, _Period)); ArrayResize (I1, Bars (_Symbol, _Period)); ArrayResize (DeltaPhase, Bars (_Symbol, _Period)); if (nLimit> rates_total- 7) nLimit = rates_total- 7; for (i = nLimit; i> = 0 &&! IsStopped (); i--) {Smooth [i] = (Price (i) + 2 * Price (i + 1) + 2 * Price (i + 2) + Price ( i + 3)) / 6.0; if (i <rates_total- 7) {Cycle [i] = (1.0 -0.5 * InpAlpha) * (1.0 -0.5 * InpAlpha) * (Smooth [i] - 2.0 * Smooth [i + 1] + Smooth [i + 2]) + 2.0 * (1.0 -InpAlpha) * Cycle [i + 1] - (1.0 -InpAlpha) * (1.0 -InpAlpha) * Cycle [i + 2]; } Else {Cycle [i] = (Price (i) - 2.0 * Price (i + 1) + Price (i + 2)) / 4.0; } Q1 [i] = (0.0962 * Cycle [i] + 0.5769 * Cycle [i + 2] - 0.5769 * Cycle [i + 4] - 0.0962 * Cycle [i + 6]) * (0.5 +0.08 * InstPeriod [i + 1]) ; I1 [i] = Cycle [i + 3]; if (Q1 [i]! = 0.0 && Q1 [i + 1]! = 0.0) DeltaPhase [i] = (I1 [i] / Q1 [i] -I1 [i + 1] / Q1 [i + 1]) / (1.0 + I1 [i] * I1 [i + 1] / (Q1 [i] * Q1 [i + 1])); if (DeltaPhase [i] <0.1) DeltaPhase [i] = 0.1; if (DeltaPhase [i]> 0.9) DeltaPhase [i] = 0.9; MedianDelta = Median (DeltaPhase, i, 5); if (MedianDelta == 0.0) DC = 15.0; else DC = (6.28318 / MedianDelta) + 0.5; InstPeriod [i] = 0.33 * DC + 0.67 * InstPeriod [i + 1]; CyclePeriod [i] = 0.15 * InstPeriod [i] + 0.85 * CyclePeriod [i + 1]; Trigger [i] = CyclePeriod [i + 1]; }} Return (rates_total); } Double Median (double & arr [], int idx, int m_len) {double MedianArr []; int copied; double result = 0,0; ArraySetAsSeries (MedianArr, true); ArrayResize (MedianArr, m_len); copied = ArrayCopy (MedianArr, arr, 0, idx, m_len); if (copied == m_len) {ArraySort (MedianArr); if (m_len% 2 == 0) result = (MedianArr [m_len / 2] + MedianArr [(m_len / 2) + 1]) / 2.0; else result = MedianArr [m_len / 2]; } Else Print (__FILE__ + __ FUNCTION__ + "помилка в функції median - невірне кількість скопійованих даних."); return result; }

Ми можемо перевірити його, приєднавши до будь-якого графіку - він буде працювати на будь-якому інструменті і будь-якому таймфрейме (див. Рис).

За допомогою цього індикатора ми зможемо реалізувати нове покоління адаптивних індикаторів - індикаторів, які адаптуються до поточного періоду ринкового циклу.

Індикатор Cyber ​​Cycle

Індикатор Cyber ​​Cycle є високочастотний фільтр з книги " Cybernetic analysis for stocks and futures ".

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

Код цього та інших індикаторів статті переписаний на MQL5 на основі індикаторів на мові EFL (Tradestation), наведених в книзі.

#property copyright "Copyright 2011, Investeo.pl" #property link "http://Investeo.pl" #property version "1.00" #property indicator_separate_window #property description "Індикатор CyberCycle - запропонований Джоном Елерса (John F. Ehlers)" # property description "в книзі \" Cybernetic Analysis for Stocks and Futures \ "" #property indicator_buffers 2 #property indicator_plots 2 #property indicator_width1 1 #property indicator_width2 1 #property indicator_type1 DRAW_LINE #property indicator_type2 DRAW_LINE #property indicator_color1 Green #property indicator_color2 Red #property indicator_label1 "Cycle" #property indicator_label2 "Trigger Line" #define Price (i) ((high [i] + low [i]) / 2.0) double Smooth []; double Cycle []; double Trigger []; input double InpAlpha = 0.07; int OnInit () {ArraySetAsSeries (Cycle, true); ArraySetAsSeries (Trigger, true); ArraySetAsSeries (Smooth, true); SetIndexBuffer (0, Cycle, INDICATOR_DATA); SetIndexBuffer (1, Trigger, INDICATOR_DATA); PlotIndexSetDouble (0, PLOT_EMPTY_VALUE, 0.0); PlotIndexSetDouble (1, PLOT_EMPTY_VALUE, 0.0); return (0); } 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 []) {long tickCnt [1]; int i; int ticks = CopyTickVolume (Symbol (), 0, 0, 1, tickCnt); if (ticks! = 1) return (rates_total); Comment (tickCnt [0]); if (prev_calculated == 0 || tickCnt [0] == 1) {int nLimit = rates_total-prev_calculated- 1; ArraySetAsSeries (high, true); ArraySetAsSeries (low, true); ArrayResize (Smooth, Bars (_Symbol, _Period)); ArrayResize (Cycle, Bars (_Symbol, _Period)); if (nLimit> rates_total- 4) nLimit = rates_total- 4; for (i = nLimit; i> = 0 &&! IsStopped (); i--) {Smooth [i] = (Price (i) + 2 * Price (i + 1) + 2 * Price (i + 2) + Price ( i + 3)) / 6.0; if (i <rates_total- 5) {Cycle [i] = (1.0 -0.5 * InpAlpha) * (1.0 -0.5 * InpAlpha) * (Smooth [i] - 2.0 * Smooth [i + 1] + Smooth [i + 2]) + 2.0 * (1.0 -InpAlpha) * Cycle [i + 1] - (1.0 -InpAlpha) * (1.0 -InpAlpha) * Cycle [i + 2]; } Else {Cycle [i] = (Price (i) - 2.0 * Price (i + 1) + Price (i + 2)) / 4.0; } Trigger [i] = Cycle [i + 1]; }} Return (rates_total); }

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

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

Адаптивний індикатор Cyber ​​Cycle

Суть даної статті - показати, яким чином ми можемо зробити індикатори адаптивними, а саме, яким чином обчислювати їх з динамічним періодом циклу замість статичного. Для цього ми повинні підключити індикатор CyclePeriod для читання поточного періоду циклу і в подальшому використовувати його значення в функції OnCalculate () .

Спочатку нам потрібно отримати хендл індикатора:

hCyclePeriod = (NULL, 0, "CyclePeriod", InpAlpha); if (hCyclePeriod == INVALID_HANDLE) {Print ( "Індикатор CyclePeriod не найден!"); return (- 1); }

а потім запросити дані індикаторного буфера у функції OnCalculate ():

int copied = CopyBuffer (hCyclePeriod, 0, i, 1, CyclePeriod); if (copied <= 0) {Print ( "Помилка: Помилка отримання значень індикатора CyclePeriod."); return - 1; } Alpha1 = 2.0 / (CyclePeriod [0] + 1.0);

Значення alpha в експоненційному згладжуванні пов'язано з періодом простий ковзної середньої Length співвідношенням , При обчисленні alpha в індикаторі адаптивного Cyber ​​Cycle Джон Елерс використовує період Dominant Cycle як значення Length.

Повний код індикатора приведений нижче:

#property copyright "Copyright 2011, Investeo.pl" #property link "http://Investeo.pl" #property version "1.00" #property indicator_separate_window #property description "Адаптивна версія індикатора CyberCycle - запропонований Джоном Елерса (John F. Ehlers) "#property description" в книзі \ "Cybernetic Analysis for Stocks and Futures \" "#property indicator_buffers 2 #property indicator_plots 2 #property indicator_width1 1 #property indicator_width2 1 #property indicator_type1 DRAW_LINE #property indicator_type2 DRAW_LINE #property indicator_color1 Green #property indicator_color2 Red #property indicator_label1 "Cycle" #property indicator_label2 "Trigger Line" #define Price (i) ((high [i] + low [i]) / 2.0) double Smooth []; double Cycle []; double Trigger []; int hCyclePeriod; input double InpAlpha = 0.07; int OnInit () {ArraySetAsSeries (Cycle, true); ArraySetAsSeries (Trigger, true); ArraySetAsSeries (Smooth, true); SetIndexBuffer (0, Cycle, INDICATOR_DATA); SetIndexBuffer (1, Trigger, INDICATOR_DATA); PlotIndexSetDouble (0, PLOT_EMPTY_VALUE, 0.0); PlotIndexSetDouble (1, PLOT_EMPTY_VALUE, 0.0); hCyclePeriod = (NULL, 0, "CyclePeriod", InpAlpha); if (hCyclePeriod == INVALID_HANDLE) {Print ( "Індикатор CyclePeriod не найден!"); return (- 1); } Return (0); } 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 []) {long tickCnt [1]; int i; int ticks = CopyTickVolume (Symbol (), 0, 0, 1, tickCnt); if (ticks! = 1) return (rates_total); double CyclePeriod [1], alpha1; Comment (tickCnt [0]); if (prev_calculated == 0 || tickCnt [0] == 1) {int nLimit = rates_total-prev_calculated- 1; ArraySetAsSeries (high, true); ArraySetAsSeries (low, true); ArrayResize (Smooth, Bars (_Symbol, _Period)); ArrayResize (Cycle, Bars (_Symbol, _Period)); if (nLimit> rates_total- 4) nLimit = rates_total- 4; for (i = nLimit; i> = 0 &&! IsStopped (); i--) {Smooth [i] = (Price (i) + 2 * Price (i + 1) + 2 * Price (i + 2) + Price ( i + 3)) / 6.0; int copied = CopyBuffer (hCyclePeriod, 0, i, 1, CyclePeriod); if (copied <= 0) {Print ( "Помилка: Помилка отримання значень індикатора CyclePeriod."); return - 1; } Alpha1 = 2.0 / (CyclePeriod [0] + 1.0); if (i> = 0) {Cycle [i] = (1.0 -0.5 * alpha1) * (1.0 -0.5 * alpha1) * (Smooth [i] - 2.0 * Smooth [i + 1] + Smooth [i + 2]) + 2.0 * (1.0 -alpha1) * Cycle [i + 1] - (1.0 -alpha1) * (1.0 -alpha1) * Cycle [i + 2]; } Else {Cycle [i] = (Price (i) - 2.0 * Price (i + 1) + Price (i + 2)) / 4.0; } Trigger [i] = Cycle [i + 1]; }} Return (rates_total); }

Індикатор наведено на малюнку:

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

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

Індикатор Center of Gravity

Під центром тяжіння (center of gravity) фізичних об'єктів на увазі точку рівноваги. Ідея полягає в тому, щоб ввести це поняття в трейдинг, грунтуючись на зв'язку величини запізнювання (lags) різних фільтрів з їх коеффіцінтамі.

Для простої ковзної середньої (SMA, Simple Moving Average) всі коефіцієнти однакові, центр тяжіння знаходиться посередині.

Для зваженої ковзної середньої (WMA, Weighted Moving Average) останні ціни є більш важливими, ніж старі. Більш точно, коефіцієнти WMA описують контур трикутника.

Загальна вираз для обчислення центра ваги при заданому вікні спостереження виглядає наступним чином:

Положення точки рівноваги виходить підсумовуванням твори положення ціни в вікні на відповідну ціну (+1 введено через те, що нумерація проводиться від 0 до N, а не від 1 до N)

Головною особливістю індикатора Center of gravity є те, що він збільшується і зменшується уздовж коливань і по суті є осциллятором без запізнювання (zero-lag oscillator).

Вихідний код наведено нижче:

#property copyright "Copyright 2011, Investeo.pl" #property link "http://Investeo.pl" #property version "1.00" #property indicator_separate_window #property description "Індикатор CG - запропонований Джоном Елерса (John F. Ehlers)" # property description "в книзі \" Cybernetic Analysis for Stocks and Futures \ "" #property indicator_buffers 2 #property indicator_plots 2 #property indicator_width1 1 #property indicator_width2 1 #property indicator_type1 DRAW_LINE #property indicator_type2 DRAW_LINE #property indicator_color1 Green #property indicator_color2 Red #property indicator_label1 "Cycle" #property indicator_label2 "Trigger Line" #define Price (i) ((high [i] + low [i]) / 2.0) double Smooth []; double Cycle []; double Trigger []; input double InpAlpha = 0.07; input int InpCGLength = 10; int OnInit () {ArraySetAsSeries (Cycle, true); ArraySetAsSeries (Trigger, true); ArraySetAsSeries (Smooth, true); SetIndexBuffer (0, Cycle, INDICATOR_DATA); SetIndexBuffer (1, Trigger, INDICATOR_DATA); PlotIndexSetDouble (0, PLOT_EMPTY_VALUE, 0.0); PlotIndexSetDouble (1, PLOT_EMPTY_VALUE, 0.0); return (0); } 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 []) {long tickCnt [1]; int i; double Num, Denom; int ticks = CopyTickVolume (Symbol (), 0, 0, 1, tickCnt); if (ticks! = 1) return (rates_total); Comment (tickCnt [0]); if (prev_calculated == 0 || tickCnt [0] == 1) {int nLimit = rates_total-prev_calculated- 1; ArraySetAsSeries (high, true); ArraySetAsSeries (low, true); ArrayResize (Smooth, Bars (_Symbol, _Period)); ArrayResize (Cycle, Bars (_Symbol, _Period)); if (nLimit> rates_total-InpCGLength) nLimit = rates_total-InpCGLength; for (i = nLimit; i> = 0 &&! IsStopped (); i--) {Num = 0,0; Denom = 0,0; for (int count = 0; count <InpCGLength; count ++) {Num + = (1.0 + count) * Price (i + count); Denom + = Price (i + count); } If (Denom! = 0.0) Cycle [i] = -Num / Denom + (InpCGLength + 1.0) / 2.0; else Cycle [i] = 0,0; Trigger [i] = Cycle [i + 1]; }} Return (rates_total); }

Скріншот наведено нижче. Зверніть увагу на невелике запізнення (lag).

Адаптивна версія індикатора Center of Gravity

Осцилятори Center of Gravity визначає "центр ваги" даних вікна фіксованої довжини. Адаптивний осцилятор Center of Gravity як довжини динамічного вікна використовує період переважного циклу (Dominant Cycle).

Для знаходження періоду переважаючого циклу використовується наступний код:

copied = CopyBuffer (hCyclePeriod, 0, i, 1, CyclePeriod); if (copied <= 0) {Print ( "Помилка: Помилка отримання значень індикатора CyclePeriod."); return - 1; } CG_len = floor (CyclePeriod [0] / 2.0);

Повний вихідний код індикатора приведений нижче, порівняйте його з неадаптівной версією (див. Також з індикатор Adaptive Cyber ​​Cycle).

#property copyright "Copyright 2011, Investeo.pl" #property link "http://Investeo.pl" #property version "1.00" #property indicator_separate_window #property description "Адаптивна версія індикатора CG - запропонований Джоном Елерса (John F. Ehlers) "#property description" в книзі \ "Cybernetic Analysis for Stocks and Futures \" "#property indicator_buffers 2 #property indicator_plots 2 #property indicator_width1 1 #property indicator_width2 1 #property indicator_type1 DRAW_LINE #property indicator_type2 DRAW_LINE #property indicator_color1 Green #property indicator_color2 Red #property indicator_label1 "Cycle" #property indicator_label2 "Trigger Line" #define Price (i) ((high [i] + low [i]) / 2.0) double Smooth []; double Cycle []; double Trigger []; int hCyclePeriod; input double InpAlpha = 0.07; int OnInit () {ArraySetAsSeries (Cycle, true); ArraySetAsSeries (Trigger, true); ArraySetAsSeries (Smooth, true); SetIndexBuffer (0, Cycle, INDICATOR_DATA); SetIndexBuffer (1, Trigger, INDICATOR_DATA); PlotIndexSetDouble (0, PLOT_EMPTY_VALUE, 0.0); PlotIndexSetDouble (1, PLOT_EMPTY_VALUE, 0.0); hCyclePeriod = (NULL, 0, "CyclePeriod", InpAlpha); if (hCyclePeriod == INVALID_HANDLE) {Print ( "Індикатор CyclePeriod не найден!"); return (- 1); } Return (0); } 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 []) {long tickCnt [1]; int i, copied; double Num, Denom; double CG_len; int ticks = CopyTickVolume (Symbol (), 0, 0, 1, tickCnt); if (ticks! = 1) return (rates_total); double CyclePeriod [1]; Comment (tickCnt [0]); if (prev_calculated == 0 || tickCnt [0] == 1) {int nLimit = rates_total-prev_calculated- 1; ArraySetAsSeries (high, true); ArraySetAsSeries (low, true); ArrayResize (Smooth, Bars (_Symbol, _Period)); ArrayResize (Cycle, Bars (_Symbol, _Period)); copied = CopyBuffer (hCyclePeriod, 0, 0, 1, CyclePeriod); if (copied <= 0) {Print ( "Помилка: Помилка отримання значень індикатора CyclePeriod."); return - 1; } If (nLimit> rates_total- int (CyclePeriod [0]) - 2) nLimit = rates_total- int (CyclePeriod [0]) - 2; for (i = nLimit; i> = 0 &&! IsStopped (); i--) {copied = CopyBuffer (hCyclePeriod, 0, i, 1, CyclePeriod); if (copied <= 0) {Print ( "Помилка: Помилка отримання значень індикатора CyclePeriod."); return - 1; } CG_len = floor (CyclePeriod [0] / 2.0); Num = 0,0; Denom = 0,0; for (int count = 0; count <int (CG_len); count ++) {Num + = (1.0 + count) * Price (i + count); Denom + = Price (i + count); } If (Denom! = 0.0) Cycle [i] = - Num / Denom + (CG_len + 1.0) / 2.0; else Cycle [i] = 0,0; Trigger [i] = Cycle [i + 1]; }} Return (rates_total); }

Малюнок індикатора AdaptiveCG наведено на малюнку:

індикатор RVI

RVI - це індикатор Relative Vigor Index , Який заснований на тому факті, що для бичачих ринків ціна закриття зазвичай вище ціни відкриття, а для ведмежих ринків ціна закриття, як правило, нижче ціни відкриття.

Сила (vigor) руху вимірюється як відношення різниці цін закриття і відкриття до торгового діапазону.

Цей індикатор добре відомий багатьом користувачам терміналу MetaTrader, тепер він включений в стандартну поставку.

Проте, я приведу тут його код:

#property copyright "2009 MetaQuotes Software Corp." #property link "https://www.mql5.com" #property description "Relative Vigor Index" #property indicator_separate_window #property indicator_buffers 2 #property indicator_plots 2 #property indicator_type1 DRAW_LINE #property indicator_type2 DRAW_LINE #property indicator_color1 Green #property indicator_color2 Red # property indicator_label1 "RVI" #property indicator_label2 "Signal" input int InpRVIPeriod = 10; double ExtRVIBuffer []; double ExtSignalBuffer []; #define TRIANGLE_PERIOD 3 #define AVERAGE_PERIOD (TRIANGLE_PERIOD * 2) void OnInit () {SetIndexBuffer (0, ExtRVIBuffer, INDICATOR_DATA); SetIndexBuffer (1, ExtSignalBuffer, INDICATOR_DATA); IndicatorSetInteger (INDICATOR_DIGITS, 3); PlotIndexSetInteger (0, PLOT_DRAW_BEGIN, (InpRVIPeriod- 1) + TRIANGLE_PERIOD); PlotIndexSetInteger (1, PLOT_DRAW_BEGIN, (InpRVIPeriod- 1) + AVERAGE_PERIOD); IndicatorSetString (INDICATOR_SHORTNAME, "RVI (" + string (InpRVIPeriod) + ")"); PlotIndexSetString (0, PLOT_LABEL, "RVI (" + string (InpRVIPeriod) + ")"); PlotIndexSetString (1, PLOT_LABEL, "Signal (" + string (InpRVIPeriod) + ")"); } 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 & TickVolume [], const long & Volume [], const int & Spread []) {int i, j, nLimit; double dValueUp, dValueDown, dNum, dDeNum; if (rates_total <= InpRVIPeriod + AVERAGE_PERIOD + 2) return (0); if (prev_calculated <0) return (0); nLimit = InpRVIPeriod + 2; if (prev_calculated> InpRVIPeriod + TRIANGLE_PERIOD + 2) nLimit = prev_calculated- 1; if (prev_calculated == 0) {for (i = 0; i <InpRVIPeriod + TRIANGLE_PERIOD; i ++) ExtRVIBuffer [i] = 0,0; for (i = 0; i <InpRVIPeriod + AVERAGE_PERIOD; i ++) ExtSignalBuffer [i] = 0,0; } For (i = nLimit; i <rates_total &&! IsStopped (); i ++) {dNum = 0,0; dDeNum = 0,0; for (j = i; j> i-InpRVIPeriod; j--) {dValueUp = Close [j] -Open [j] + 2 * (Close [j- 1] -Open [j- 1]) + 2 * ( Close [j- 2] -Open [j- 2]) + Close [j- 3] -Open [j- 3]; dValueDown = High [j] -Low [j] + 2 * (High [j- 1] -Low [j- 1]) + 2 * (High [j- 2] -Low [j- 2]) + High [ j- 3] -Low [j- 3]; dNum + = dValueUp; dDeNum + = dValueDown; } If (dDeNum! = 0.0) ExtRVIBuffer [i] = dNum / dDeNum; else ExtRVIBuffer [i] = dNum; } NLimit = InpRVIPeriod + TRIANGLE_PERIOD + 2; if (prev_calculated> InpRVIPeriod + AVERAGE_PERIOD + 2) nLimit = prev_calculated- 1; for (i = nLimit; i <rates_total &&! IsStopped (); i ++) ExtSignalBuffer [i] = (ExtRVIBuffer [i] + 2 * ExtRVIBuffer [i- 1] + 2 * ExtRVIBuffer [i- 2] + ExtRVIBuffer [i- 3]) / AVERAGE_PERIOD; return (rates_total); }

На малюнку нижче наведено скріншот стандартного індикатора RVI з періодом 10 за замовчуванням:

Адаптивна версія індикатора RVI

Як і для попередніх індикаторів (точніше, їх адаптивних версій), нам потрібно отримати значення Dominant Cycle з індикатора CyclePeriod і застосувати його до періоду індикатора RVI. Мінлива Length обчислюється як зважене значення ковзної середньої періоду CyclePeriod, усереднене по 4 останнім барам.

copied = CopyBuffer (hCyclePeriod, 0, 0, 4, CyclePeriod); if (copied <= 0) {Print ( "Помилка: Помилка отримання значень індикатора CyclePeriod."); return - 1; } AdaptiveRVIPeriod = int (floor ((4 * CyclePeriod [0] + 3 * CyclePeriod [1] + 2 * CyclePeriod [2] + CyclePeriod [3]) / 20.0));

Нижче наведено повний вихідний код адаптивної версії індикатора RVI.

#property copyright "2009 MetaQuotes Software Corp." #property copyright "2011, Adaptive version Investeo.pl" #property link "https://www.mql5.com" #property description "Adaptive Relative Vigor Index" #property indicator_separate_window #property indicator_buffers 2 #property indicator_plots 2 #property indicator_type1 DRAW_LINE #property indicator_type2 DRAW_LINE #property indicator_color1 Green #property indicator_color2 Red #property indicator_label1 "AdaptiveRVI" #property indicator_label2 "Signal" #define Price (i) ((high [i] + low [i]) / 2.0) input int InpRVIPeriod = 10 ; double ExtRVIBuffer []; double ExtSignalBuffer []; int hCyclePeriod; input double InpAlpha = 0.07; int AdaptiveRVIPeriod; #define TRIANGLE_PERIOD 3 #define AVERAGE_PERIOD (TRIANGLE_PERIOD * 2) int OnInit () {SetIndexBuffer (0, ExtRVIBuffer, INDICATOR_DATA); SetIndexBuffer (1, ExtSignalBuffer, INDICATOR_DATA); IndicatorSetInteger (INDICATOR_DIGITS, 3); hCyclePeriod = (NULL, 0, "CyclePeriod", InpAlpha); if (hCyclePeriod == INVALID_HANDLE) {Print ( "Не знайдено індикатор CyclePeriod!"); return (- 1); } PlotIndexSetInteger (0, PLOT_DRAW_BEGIN, (InpRVIPeriod- 1) + TRIANGLE_PERIOD); PlotIndexSetInteger (1, PLOT_DRAW_BEGIN, (InpRVIPeriod- 1) + AVERAGE_PERIOD); IndicatorSetString (INDICATOR_SHORTNAME, "AdaptiveRVI"); PlotIndexSetString (0, PLOT_LABEL, "AdaptiveRVI"); PlotIndexSetString (1, PLOT_LABEL, "Signal"); return 0; } 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 & TickVolume [], const long & Volume [], const int & Spread []) {int i, j, nLimit; double dValueUp, dValueDown, dNum, dDeNum; double CyclePeriod [4]; int copied; copied = CopyBuffer (hCyclePeriod, 0, 0, 4, CyclePeriod); if (copied <= 0) {Print ( "Помилка: Помилка отримання значень індикатора CyclePeriod."); return - 1; } AdaptiveRVIPeriod = int (floor ((4 * CyclePeriod [0] + 3 * CyclePeriod [1] + 2 * CyclePeriod [2] + CyclePeriod [3]) / 20.0)); if (rates_total <= AdaptiveRVIPeriod + AVERAGE_PERIOD + 2) return (0); if (prev_calculated <0) return (0); nLimit = AdaptiveRVIPeriod + 2; if (prev_calculated> AdaptiveRVIPeriod + TRIANGLE_PERIOD + 2) nLimit = prev_calculated- 1; if (prev_calculated == 0) {for (i = 0; i <AdaptiveRVIPeriod + TRIANGLE_PERIOD; i ++) ExtRVIBuffer [i] = 0,0; for (i = 0; i <AdaptiveRVIPeriod + AVERAGE_PERIOD; i ++) ExtSignalBuffer [i] = 0,0; } For (i = nLimit; i <rates_total &&! IsStopped (); i ++) {copied = CopyBuffer (hCyclePeriod, 0, rates_total-i- 1, 4, CyclePeriod); if (copied <= 0) {Print ( "FAILURE: Could not get values ​​from CyclePeriod indicator."); return - 1; } AdaptiveRVIPeriod = int (floor ((4 * CyclePeriod [0] + 3 * CyclePeriod [1] + 2 * CyclePeriod [2] + CyclePeriod [3]) / 20.0)); dNum = 0,0; dDeNum = 0,0; for (j = i; j> MathMax (i-AdaptiveRVIPeriod, 3); j--) {dValueUp = Close [j] -Open [j] + 2 * (Close [j- 1] -Open [j- 1] ) + 2 * (Close [j- 2] -Open [j- 2]) + Close [j- 3] -Open [j- 3]; dValueDown = High [j] -Low [j] + 2 * (High [j- 1] -Low [j- 1]) + 2 * (High [j- 2] -Low [j- 2]) + High [ j- 3] -Low [j- 3]; dNum + = dValueUp; dDeNum + = dValueDown; } If (dDeNum! = 0.0) ExtRVIBuffer [i] = dNum / dDeNum; else ExtRVIBuffer [i] = dNum; } NLimit = AdaptiveRVIPeriod + TRIANGLE_PERIOD + 2; if (prev_calculated> AdaptiveRVIPeriod + AVERAGE_PERIOD + 2) nLimit = prev_calculated- 1; for (i = nLimit; i <rates_total &&! IsStopped (); i ++) ExtSignalBuffer [i] = (ExtRVIBuffer [i] + 2 * ExtRVIBuffer [i- 1] + 2 * ExtRVIBuffer [i- 2] + ExtRVIBuffer [i- 3]) / AVERAGE_PERIOD; return (rates_total); }

Скріншот адаптивного індикатора RVI з динамічної шириною вікна:

Висновки

У статті розглянуті адаптивні версії трьох технічних індикаторів індикаторів і представлена ​​їх реалізація на MQL5.

Після читання статті, я сподіваюся, тепер вам зрозумілий механізм написання адаптивних індикаторів.

Автор закликає до експериментів і створення адаптивних версій інших індикаторів.

Переклад з англійської проведений MetaQuotes Software Corp.
Оригінальна стаття: https://www.mql5.com/en/articles/288

Як ми досягнемо цього?
Я згадував про перетворення Гільберта?
Новости
Провайдеры:
  • 08.09.2015

    Batyevka.NET предоставляет услуги доступа к сети Интернет на территории Соломенского района г. Киева.Наша миссия —... 
    Читать полностью

  • 08.09.2015
    IPNET

    Компания IPNET — это крупнейший оператор и технологический лидер на рынке телекоммуникаций Киева. Мы предоставляем... 
    Читать полностью

  • 08.09.2015
    Boryspil.Net

    Интернет-провайдер «Boryspil.net» начал свою работу в 2008 году и на данный момент является одним из крупнейших поставщиков... 
    Читать полностью

  • 08.09.2015
    4OKNET

    Наша компания работает в сфере телекоммуникационных услуг, а именно — предоставлении доступа в сеть интернет.Уже... 
    Читать полностью

  • 08.09.2015
    Телегруп

    ДП «Телегруп-Украина» – IT-компания с 15-летним опытом работы на рынке телекоммуникационных услуг, а также официальный... 
    Читать полностью

  • 08.09.2015
    Софтлинк

    Высокая скоростьМы являемся участником Украинского центра обмена трафиком (UA — IX) с включением 10 Гбит / сек... 
    Читать полностью