Обсуждение статьи "Рецепты MQL5 - Создаем кольцевой буфер для быстрого расчета индикаторов в скользящем окне"
Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий
fxsaber, 2016.09.16 14:32
void OnStart() { double Index = -345.23; double Size = -432.98; double Array[]; // При любом размере (и не целочисленном) будет работать ArrayResize(Array, (int)Size < 0 ? (int)MathAbs(Size) : (int)Size); // При любом индексе (и не целочисленном) ВСЕГДА (кроме нуля - если размер массива ноль) будет выполняться без ошибок. // Таким образом массив становится бесконечной копией самого себя в обе стороны Array[(int)Index < 0 ? ArraySize(Array) + ((int)Index % ArraySize(Array)) : (int)Index % ArraySize(Array)] = 1; }
int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { //--- bool calc = false; for(int i = prev_calculated; i < rates_total; i++) { Sma.AddValue(price[i]); buff[i] = Sma.SMA(); calc = true; } if(!calc) { Sma.ChangeValue(MaPeriod-1, price[rates_total-1]); buff[rates_total-1] = Sma.SMA(); } return(rates_total-1); }
int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { //--- if (prev_calculated < rates_total) for(int i = prev_calculated; i < rates_total; i++) { Sma.AddValue(price[i]); buff[i] = Sma.SMA(); } else { Sma.ChangeValue(MaPeriod-1, price[rates_total-1]); buff[rates_total-1] = Sma.SMA(); } return(rates_total-1); }
ЗЫ Если prev_calculated обнулится, будут ошибки.
Очень хорошая статья на очень важную тему.
Кольцевой буфер - важнейший механизм.
В дополнение к статье хочу добавить свое представление, как использование кольцевых буферов может развиваться дальше:
Если парралельно с записью значений параметра в кольцевой буфер, создать для этого параметра второй кольцевой буфер, который будет записывать время между изменениями значений в первом буфере, то на основе данных обоих кольцевых буферов можно построить кривую изменения значения в текущем периоде. Дальше, с помощью математических операций можно извлечь характер изменения значения параметра, - т.е. его текущую сигнатуру, и работать не с конкретными значениями, а с контекстом изменения параметра за весь текущий период .
P.S. Было бы здорово, чтобы кто то это реализовал в как самостоятельный механизм. Попробуйте?
Спасибо за статью! У меня есть несколько вопросов и едких замечаний )
По возможности избегайте запросов по получению данных множества таймфреймов. Вместо этого для расчетов воспользуйтесь одним (наименьшим) таймфреймом. Например, если вам требуется рассчитать два индикатора на M1 и H1, получите данные M1, сконвертируйте их в H1 и затем подайте эти данные для расчета индикатора на H1. Такой подход сложнее, но позволит существенно сэкономит память.
За счет чего получится экономия?
Сгенерированная терминалом тайм-серия Н1 занимает больше памяти, чем такая же тайм-серия, сгенерированная внутри советника?
Однако все равно нам потребовалось почти 3 Гб оперативной памяти. Можно ли как-то еще уменьшить эту цифру? Можно, если оптимизировать количество таймфреймов. Попробуем немного изменить тестировочный код, и вместо 21 таймфрейма будем использовать только один — PERIOD_M1. При этом количество индикаторов останется прежним, просто некоторые из них будут дублироваться:
Теперь те же 504 индикатора в режиме внутреннего расчета занимают уже 548 Мб оперативной памяти.
Этого хода вообще не понял. Как можно сравнивать расчет индикаторов по 21 тайм-фрейму с расчетом по 1 ТФ? Результаты расчетов совсем разные, какая разница, сколько при этом памяти расходуется?
Трудно найти более актуальное применение кольцевых буферов, чем в деле трейдинга. Тем более удивительно, что до сегодняшнего момента этот алгоритм построения данных еще не освещался в MQL-сообществе.
Константин Груздев еще в 2012 выкладывал свой класс и несколько примеров. Поиск их находит.
А вообще, конечно, техника хорошая. Один недостаток — нужно переписывать все индикаторы.
Спасибо за статью! У меня есть несколько вопросов и едких замечаний )
По возможности избегайте запросов по получению данных множества таймфреймов. Вместо этого для расчетов воспользуйтесь одним (наименьшим) таймфреймом. Например, если вам требуется рассчитать два индикатора на M1 и H1, получите данные M1, сконвертируйте их в H1 и затем подайте эти данные для расчета индикатора на H1. Такой подход сложнее, но позволит существенно сэкономит память.
За счет чего получится экономия?
Сгенерированная терминалом тайм-серия Н1 занимает больше памяти, чем такая же тайм-серия, сгенерированная внутри советника?
К сожалению да. При том намного. И еще не имеет значения запросили вы один бар, или всю доступную историю. Во внутреннюю память будут скопированы все данные по указанному таймфрейму. Сколько точно будет скопировано я конечно не знаю, но по моим замерам памяти, было видно, что копируется практически все.
Однако все равно нам потребовалось почти 3 Гб оперативной памяти. Можно ли как-то еще уменьшить эту цифру? Можно, если оптимизировать количество таймфреймов. Попробуем немного изменить тестировочный код, и вместо 21 таймфрейма будем использовать только один — PERIOD_M1. При этом количество индикаторов останется прежним, просто некоторые из них будут дублироваться:
Теперь те же 504 индикатора в режиме внутреннего расчета занимают уже 548 Мб оперативной памяти.
Этого хода вообще не понял. Как можно сравнивать расчет индикаторов по 21 тайм-фрейму с расчетом по 1 ТФ? Результаты расчетов совсем разные, какая разница, сколько при этом памяти расходуется?
Если для расчета нескольких индикаторов на разных таймфреймах использовать только один наименьший таймфрейм, память удастся сэкономить. Допустим есть два индикатора, один считает значения на M1 другой на H1. Мы можем загрузить котировки для M1 и для H1. Каждому индикатору подсунуть свои котировки и получить от них значения. Однако только за счет того, что будет загружен H1, использование памяти сильно возрастет. Поэтому если мы запросим M1, затем сконвертируем M1 в H1 и подсуним эти данные индикатору на H1, то память существенно сэкономится. Экономия достигается за счет того, что во внутренних буферах MetaTrader для хранения котировок H1 выделяется гораздо больше памяти, чем если бы эти котировки хранились внутри эксперта.
Интересна еще одна особенность, которая не была упомянута в статье. В тестере стратегии модель выделения памяти другая и гораздо более экономичная. Памяти в нем, при задействовании множества таймфремов выделяется гораздо меньше и при том все рассчитывается нормально.
...
В общем целей при написании статьи было три:
- Создать быстрые алгоритмы для расчетов индикаторов внутри эксперта (выполнено).
- Создать удобный интерфейс для расчетов в кольцевом буфере (выполнено).
- Создать экономный расчет по памяти (не выполнено).
Очень хорошая статья на очень важную тему.
Кольцевой буфер - важнейший механизм.
В дополнение к статье хочу добавить свое представление, как использование кольцевых буферов может развиваться дальше:
Если парралельно с записью значений параметра в кольцевой буфер, создать для этого параметра второй кольцевой буфер, который будет записывать время между изменениями значений в первом буфере, то на основе данных обоих кольцевых буферов можно построить кривую изменения значения в текущем периоде. Дальше, с помощью математических операций можно извлечь характер изменения значения параметра, - т.е. его текущую сигнатуру, и работать не с конкретными значениями, а с контекстом изменения параметра за весь текущий период .
P.S. Было бы здорово, чтобы кто то это реализовал в как самостоятельный механизм. Попробуйте?
То что Вы описываете это просто индикатор. Создаете класс, допустим CTradeChange. Внутри него разместите два синхронных кольцевых буфера: один запоминает N последних цен, другой N последних значений времени:
CTradeChange change;
...
change.Add(value, TimeCurrent());
Далее в методе Add выполните расчет разницы между текущим и предыдущем значение времени.
В общем целей при написании статьи было три:
- Создать быстрые алгоритмы для расчетов индикаторов внутри эксперта (выполнено).
- Создать удобный интерфейс для расчетов в кольцевом буфере (выполнено).
- Создать экономный расчет по памяти (не выполнено).
Спасибо за ответы.
Я при разработке упомянутой в статье панели пришел к необходимости ограничивать кол-во отображаемых на чартах баров (5000). Именно из-за памяти...
В МТ4 с этим было гораздо проще: во-первых, каждый ТФ загружался независимо, а значит не требовал подгрузки М1, а, во-вторых, индикаторные буферы занимали столько места, на сколько их заполняешь (хоть 100 баров, если больше не нужно).
В пятерке стало универсальнее и целостнее за счет генерации ТФ из М1, но с памятью бывают сложности.
То что Вы описываете это просто индикатор. Создаете класс, допустим CTradeChange. Внутри него разместите два синхронных кольцевых буфера: один запоминает N последних цен, другой N последних значений времени:
Далее в методе Add выполните расчет разницы между текущим и предыдущем значение времени.
Странный ответ Вы дали. Похоже Вы вообще не поняли мысль. Процетирую ее еще раз:
"Если парралельно с записью значений параметра в кольцевой буфер, создать для этого параметра второй кольцевой буфер, который будет записывать время между изменениями значений в первом буфере, то на основе данных обоих кольцевых буферов можно построить кривую изменения значения в текущем периоде. Дальше, с помощью математических операций можно извлечь характер изменения значения параметра, - т.е. его текущую сигнатуру, и работать не с конкретными значениями, а с контекстом изменения параметра за весь текущий период ."
Обратите внимание, я не задавал вопроса о том, как построить два синхронных кольцевых буфера, так как их механизм достаточно прост, а предложил попробывать развить область применения кольцевых буферов дальше, чем описано в статье.
Предлагаемая Вами в статье область применения - получение конкретных значений по индексам внутри текущего периода.
Предлагаемое мною расширение области - получение сигнатур изменения значений внутри текущего периода. Это может быть осуществлено с помощью построения кривой (не на графике) на основе данных двух кольцевых буферов:
1. Буфера со значениями текущего периода.
2. Буфера с временными промежутками между значениями первого буфера.
Совместив данные можно построть кривую математически (на графике не обязательно) и алгоритмически представить и изучить ее внутри программы. Для этого нужно сканировать оба буфера и рассмотреть сигнатуру изменения параметра.
Это решение открывает возможность оперировать не только конкретными значениями параметра внутри периода, а сигнатурой его изменения за весь период.
Например, можно будет указать программе:
if(Характер_изменения_значения_параметра_за_период == BIG_WAVE)Лот += 10;
При этом константа BIG_WAVE - это и есть сигнатура, которая выражает характер изменения значения за текущий период.
Можно например построить 5 шаблонов сигнатур:
FLAT, RISING, BIG_WAVE, FALLING, SMALL_WAVES.
Каждая из этих констант - шаблон определенного характера изменения параметра внутри текущего периода.
Нужно разработать формат записи сигнатуры, и создать эти шаблоны. Далее, алгоритм будет считывать текущую сигнатуру и сравнивать ее с шаблонами, находя наиблежайшие совпадения (абсолютного совпадения быть не может) между характерами текущего изменения и одним из шаблонов.
Преимущества этого подхода перед стандартным использованием конкретных значений очевидны.
Использование сигнатур дает возможность опираться в решениях на контекст извлеченный из массива данных и не пытаться извлечь этот контекст из одиночных значений.
P.S. Надеюсь на этот раз Вы поняли мою мысль.
Странный ответ Вы дали. Похоже Вы вообще не поняли мысль. Процетирую ее еще раз:
...
Предлагаемое мною расширение области - получение сигнатур изменения значений внутри текущего периода. Это может быть осуществлено с помощью построения кривой (не на графике) на основе данных двух кольцевых буферов:
P.S. Надеюсь на этот раз Вы поняли мою мысль.
Я прекрасно понял Вашу мысль и в первый раз.
Что такое "сигнатура изменений значений"? Это некоторая величина, изменяемая в динамике. Следовательно это индикатор. Развивать кольцевой буфер для этого не надо, а достаточно просто создать на основе нескольких этих кольцевых индикаторов алгоритм расчета того самого "характера изменения" о котором Вы говорите.

- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Опубликована статья Рецепты MQL5 - Создаем кольцевой буфер для быстрого расчета индикаторов в скользящем окне:
Кольцевой буфер — самый простой и в то же время наиболее эффективный способ организации данных для расчетов в скользящем окне. В статье описано, как устроен этот алгоритм, и показано, как с его помощью сделать вычисление в скользящем окне простым и эффективным процессом.
В начале расчета индикатор просто добавляет новые значения в кольцевой буфер скользящей средней. При этом количество добавляемых значений контролировать не требуется. Все расчеты и удаление устаревших элементов происходят в автоматическом режиме. Если вызов индикатора происходит при изменении цены последнего бара, то требуется лишь изменить последнее значение скользящей средней на новое, чем и занимается метод ChangeValue.
Графическое отображение самого индикатора эквивалентно одноименному стандартному индикатору MovingAverage:
Рис. 1. Отображение простой скользящей средней, рассчитанной в кольцевом буфере.
Автор: Vasiliy Sokolov