Форум » Консультации по программированию » Zig-Zag на истории отрисовывается корректно, а в реале не всегда » Ответить

Zig-Zag на истории отрисовывается корректно, а в реале не всегда

hoz: Написал я индикатор Zig-Zag (после достаточно длительного использования другого). Хотелось, написать самому, что бы научится это делать. К тому же, в том, который я раньше использовал был косяк, который не очень очевиден, на первый взгляд. В нём есть 1 недочёт, но он не критичный. Его я опускаю сейчас. Но самый сложный вопрос, который меня напрягает уже несколько дней это то, что индикатор отрисовывает на истории всё согласно логике и на реале тоже обычно всё так, НО есть места, в которых индикатор ведёт себя странно и не адекватно. Смысл в том, что иногда не обновляются хаи/лоу Zig-Zag'а. Как найти причину? Отладчик в подобных ситуациях не применим т.к. здесь будет писаться огромный пласт данных и такое очень сложно анализировать. Вот какой момент я обнаружил в тестере: Код прилагаю. Код читабелньый с комментариями, где они нужны. Единственное, это ещё прилагаю библиотеку TimeSeries, которая должна лежать в папке EnvironmentInfo

Ответов - 11

Scriptong: hoz пишет: НО есть места, в которых индикатор ведёт себя странно и не адекватно. Посмотрел на код бегло, замечая основные моменты. Сразу скажу, что лучше всего начинать с простого - сделать свой ZZ для текущего ТФ. Вы же сразу взялись за сложное - за MTF индикатор. А MTF-индикаторы - достаточно сложные, когда сталкиваешься с ними впервые. Наиболее распространенная ошибка, которая появляется при разработке MTF-индикатора, в коде имеется. Это оперирование индексом одного ТФ, когда происходит обработка другого ТФ. Эта ошибка бросается в глаза в функции: int previousExtremumIndexOtherTF(int i) { // Индекс обрабатываемого бара c таймфрейма i_tf //--- for (int bar = i + 1; bar < barsTotal(i_tf); bar++) { if (extremum_price_other_tf_buffer[bar] == EMPTY_VALUE) continue; return bar; } //--- return barsTotal(i_tf) - 1; } Вы воспринимаете индекс i как индекс бара старшего ТФ. Так, если индикатор работает на ТФ М15 (как на рисунке), а оперирует данными с ТФ Н1 (как по умолчанию в настроечном параметре), то цикл с итератором bar пробегает по индексам ТФ Н1, но использует индексацию текущего ТФ. Ведь extremum_price_other_tf_buffer - это индикаторный буфер, который, как ни крути, использует нумерацию текущего ТФ. Поэтому правилом №1 при разработке MTF-индикаторов является использование индикаторныъ буферов только для отображения данных и ни в коем случае - для хранения данных с других ТФ. Для хранения данных других ТФ необходимо использовать свои массивы и структуры, но никак не индикаторные буфера.

hoz: Scriptong пишет: Посмотрел на код бегло, замечая основные моменты. Сразу скажу, что лучше всего начинать с простого - сделать свой ZZ для текущего ТФ. Вы же сразу взялись за сложное - за MTF индикатор. А MTF-индикаторы - достаточно сложные, когда сталкиваешься с ними впервые. Обычный я написал уже месяц назад за пару дней. Вот он..

hoz: Scriptong пишет: Наиболее распространенная ошибка, которая появляется при разработке MTF-индикатора, в коде имеется. Это оперирование индексом одного ТФ, когда происходит обработка другого ТФ. Эта ошибка бросается в глаза в функции:  цитата: int previousExtremumIndexOtherTF(int i) { // Индекс обрабатываемого бара c таймфрейма i_tf //--- for (int bar = i + 1; bar < barsTotal(i_tf); bar++) { if (extremum_price_other_tf_buffer[bar] == EMPTY_VALUE) continue; return bar; } //--- return barsTotal(i_tf) - 1; } Вы воспринимаете индекс i как индекс бара старшего ТФ. Так, если индикатор работает на ТФ М15 (как на рисунке), а оперирует данными с ТФ Н1 (как по умолчанию в настроечном параметре), то цикл с итератором bar пробегает по индексам ТФ Н1, но использует индексацию текущего ТФ. Ведь extremum_price_other_tf_buffer - это индикаторный буфер, который, как ни крути, использует нумерацию текущего ТФ. Поэтому правилом №1 при разработке MTF-индикаторов является использование индикаторныъ буферов только для отображения данных и ни в коем случае - для хранения данных с других ТФ. Для хранения данных других ТФ необходимо использовать свои массивы и структуры, но никак не индикаторные буфера. Так я же не для отрисовки это значение запоминаю, а для того, что бы найти предыдущий экстремум на другом ТФ (в данном случае H1). Я уже поправил сверху пару дней назад этот момент: SetIndexBuffer(1, extremum_price_other_tf_buffer, INDICATOR_CALCULATIONS); // Второй буфер - экстремумы Zig-Zag'а таймфрейма i_tf ArraySetAsSeries(extremum_price_other_tf_buffer, true); То что размер массива для хранения данных другого ТФ, который повыше будет иметь размер количества баров на ТФ открытого графика (в моём случает ТФ М15) я понимаю. Но разница какая? Главное что не меньше. Этот массив забьётся данными с 0-го индекса до какого-то индекса (разница количество баров на ТФ М15). Остальные индексы будут иметь дефолтное значение. Но мешать это не должно кака я понимаю. Последний вариант мультитаймфреймного индикатора вот выложил.


Scriptong: hoz пишет: То что размер массива для хранения данных другого ТФ, который повыше будет иметь размер количества баров на ТФ открытого графика (в моём случает ТФ М15) я понимаю. Но разница какая? Так ведь нумерация баров смещается постоянно, несинхронно со смещением баров старшего ТФ. В итоге При поиске индекса бара экстремума получается не тот индекс бара, который нужно. Отсюда и все беды. Сейчас вот проверил индикатор в тестере. Явно видно, что индикатор опаздывает с регистрацией экстремумов. Новый экстремум регистрируется, когда закрывается бар старшего ТФ, но на текущем ТФ этот экстремум уже давно имеется.

Scriptong: Кстати, советую не использовать библиотеку TimeSeries по двум причинам: В МQL5 уже имеются функции, идентичные функциям MQL4. Речь об i-функциях: iLow, iOpen, iTime и т. д. С ними работать намного проще, чем с CopyXXXX Библиотека не совсем корректная. К примеру, функция barOpenPrice (любая из перезагрузок) может вернуть "мусор" в качестве корректного значения, хотя функция CopyOpen при этом отработала с ошибкой. Это возможно в тех случаях, когда данные заданного бара имеются, но попросту не были скопированы в barsOpenPrice. Функция CopyOpen вернет значение 0 (а не -1, как ожидается в коде), что приведет к возврату неинициализированного значения из barsOpenPrice. Правильный подход - сравнить количество скопированных баров с ожидаемым количеством. Если эти значения не равны, то получаем ошибку.

Scriptong: Чтобы было понятнее, почему нельзя применять выбранный Вами способ хранения данных о предыдущем экстремуме, рассмотрим конкретную ситуацию: Как раз ситуация дублирования минимумов. Дело в том, что индикатор предыдущим экстремумом считает не минимум, а максимум. То есть выполняется вот эта часть кода функции iterationHandling: // На предыдущем баре тенденция отрисовки Zig-Zag'а была восходящей if (tendention__other_tf_buffer[i + 1] == TREND_UPWARDS) { if (barHighPrice(i, i_tf) >= barHighPrice(i + 1, i_tf)) { // Было обновление максимума if (barHighPrice(i, i_tf) >= extremum_price_other_tf_buffer[previousExtremumIndexOtherTF]) { if (tendention__other_tf_buffer[previousExtremumIndexOtherTF] == TREND_UPWARDS) cleanBuffers(TREND_UPWARDS, previousExtremumIndexOtherTF); setBuffers(TREND_UPWARDS, barHighPrice(i, i_tf), i); // Draw :: wingdings("extremum_increase_" + (string)i, LOCATION_BELOW, 71, 2, clrBlue, i, 2.5); } else { setBuffers(TREND_DOWNWARDS, barLowPrice(i, i_tf), i); } return; } // Обновления максимума не было, но был пробой минимума предыдущей свечи. Смена направления if (barLowPrice(i, i_tf) < barLowPrice(i + 1, i_tf)) { if (i < 10) Print("Смена с восх. на нисх. Бар: ", iTime(NULL, i_tf, i), ", пред. экстремум: ", iTime(NULL, i_tf, previousExtremumIndexOtherTF), ", high: ", iHigh(NULL, i_tf, previousExtremumIndexOtherTF), ", low: ", iLow(NULL, i_tf, previousExtremumIndexOtherTF), ", extremum: ", extremum_price_other_tf_buffer[previousExtremumIndexOtherTF]); setBuffers(TREND_DOWNWARDS, barLowPrice(i, i_tf), i); // Draw :: wingdings("UPPEST_EXTREMUM_" + (string)i, LOCATION_ABOVE, 83, 2, clrRed, i, 2.5); return; } } Вставил в код логирование значений. Что получилось, видно на рисунке. Вместо ожидаемого нахождения экстремума на баре 2018.09.26 21:00 видим, что экстремум найден на следующем баре, 22:00, там, где его в принципе нет. Также видно, что значение самого экстремума какое-то "мусорное" - 1.31705 вместо ожидаемого 1.31856. Для исправления ситуации я бы посоветовал просто запоминать в коде последний найденный экстремум и все (создать специальную структуру). Зачем сохранять все имеющиеся экстремумы? Это лишнее.

hoz: Действительно, я пришёл к такому же выводу. Храня только актуальные данные проще с ними работать. Отдохнул недельку от зиг-зага. Решил продолжить. Вот что получилось сейчас. По сути, базовая часть уже работает. Т.е. зиг-заг рисует по базовому сценарию. Дальше будет доработка. Но остался 1 интересный момент. В коде я задаю пустое значение, по которому нет отрисовки: PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0); // Установка пустого значения буфера extremum_price_buffer, по которому нет отрисовки Так вот мне такой вариант не нравится. Хочется сделать, как я обычно, делаю: PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE); // Установка пустого значения буфера extremum_price_buffer, по которому нет отрисовки Но если я задаю, что нет отрисовки именно по EMPTY_VALUE, то зиг-заг ломается. Причём, самое интересное, что я нигде в коде не присваиваю в этот буфер 0.0,.. тем не менее, когда 0.0 значение по которому нет отрисовки, то всё корректно. Как это понять интересно?

Scriptong: hoz пишет: PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE); // Установка пустого значения буфера extremum_price_buffer, по которому нет отрисовки Но если я задаю, что нет отрисовки именно по EMPTY_VALUE, то зиг-заг ломается. Причём, самое интересное, что я нигде в коде не присваиваю в этот буфер 0.0,.. тем не менее, когда 0.0 значение по которому нет отрисовки, то всё корректно. Проверить, к сожалению, не могу, т. к. не хватает файлов HOZ_Code\Enums\TrendDirection.mqh и Structures\ZZ_properties.mqh. По тому, как сделано сейчас в коде, могу лишь заметить, что имется явное несоответствие между заданным пустым значением (0.0) и тем, чем заполняется буфер индикатора по умолчанию (EMPTY_VALUE). То есть в таком варианте как раз и должно проявляться неправильное поведение индикатора. Ведь значение EMPTY_VALUE им должно восприниматься как корректное значение для отрисовки. Обратите внимание на строки 124, 141, 164 и 182. В них буфер принимает значение EMPTY_VALUE, а не 0.

hoz: Scriptong пишет: Обратите внимание на строки 124, 141, 164 и 182. В них буфер принимает значение EMPTY_VALUE, а не 0. В том то и абсурдность ситуации. В таком виде всё работает. А если при связывании буфера в OnInit() переопределить пустое значение, по которому не отрисовывается как не 0.0 .. как сейчас и есть, а как EMPTY_VALUE, то индикатор рисует абы что. Вот в чём суть. На данный момент имеется такое: SetIndexBuffer(0, extremum_price_buffer, INDICATOR_DATA); // Первый буфер - экстремумы Zig-Zag'а PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0); // Установка пустого значения буфера extremum_price_buffer, по которому нет отрисовки ArraySetAsSeries(extremum_price_buffer, true); И всё работает. А если сделать так: SetIndexBuffer(0, extremum_price_buffer, INDICATOR_DATA); // Первый буфер - экстремумы Zig-Zag'а PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE); // Установка пустого значения буфера extremum_price_buffer, по которому нет отрисовки ArraySetAsSeries(extremum_price_buffer, true); Не работает... Вот что странно.

Scriptong: hoz пишет: Не работает... Вот что странно. Это не странность. Код работает с данными другого ТФ и при этом как бы игнорирует то, что происходит на текущем ТФ. А происходит следующее. При первом запуске индикатор проходит по всей истории старшего и текущего ТФ, заполняя буфер extremum_price_buffer корректными значениями. Затем приходит время нового бара текущего ТФ, но не старшего. Согласно вот этому условию: if (lastBarTimeOtherTF == iTime(_Symbol, i_tf, 0)) return; которое расположено в начале функции ZigZag, обработка нового бара игнорируется. То есть в буфер не записывается какое-либо значение. Там остается то, что находится в памяти компьютера, на котором исполняется программа. Там может быть все, что угодно, т. н. "мусор". Чаще всего этот "мусор" равен 0.0. Вот откуда берется нулевое значение. Но так будет не всегда, т. к. это все-таки "мусор". Мораль здесь такова: всегда инициализировать значения переменных. В данном случае - элементов массива-таймсерии. Сам терминал МТ5, в отличие от терминала МТ4, их не инициализирует. На скорую руку создал такой вот код, решающий проблему (думаю, можно придумать что-то получше): void ZigZag(int limit) { //--- datetime dtOtherTFNullBar = iTime(NULL, i_tf, 0); for (int i = 0; iTime(NULL, PERIOD_CURRENT, i) >= dtOtherTFNullBar; ++i) extremum_price_buffer[ i ] = EMPTY_VALUE; if (lastBarTimeOtherTF == iTime(_Symbol, i_tf, 0)) return; if (limit > 1) ZZ.init(); for (int i = limit; i > 0; i--) { if (limit == 1) ZZ.barIndex++; iterationHandling(i); } lastBarTimeOtherTF = iTime(_Symbol, i_tf, 0); } Условие: if (limit == 1) убрал за ненадобностью, т. к. это контроль за образованием нового бара старшего ТФ, но никак не текущего.

hoz: Действительно. Вот всё, что необходимо.



полная версия страницы