Автоматическое нахождение экстремумов на основе заданного ценового перепада
Введение
Многие популярные торговые стратегии базируются на использовании различных графических моделей: "голова и плечи", "двойная вершина/двойное дно" и другие. В некоторых стратегиях анализируется расхождение экстремумов на графиках. При автоматизации таких торговых систем возникает необходимость в нахождении пиков и впадин на графиках, а потом — в их эффективной обработке и интерпретации. Существующие инструменты не всегда дают возможность найти экстремумы согласно установленным критериям. В статье представлены эффективные алгоритмы и программные решения для нахождения и обработки экстремумов на графиках цен в зависимости от ценового перепада.
1. Существующие инструменты для поиска экстремумов
1.1. Фракталы и подобные инструментыФракталы — популярный инструмент для нахождения экстремумов. Они позволяют находить минимум и максимум цены для серии из 5 баров (рис. 1). Экстремумы определяются как при сильном, так и при слабом движении цены. При правильном выборе таймфрейма фракталы могут показывать достаточно неплохие результаты, но их результативность сильно зависит от рыночных условий.
Рис. 1. Результаты использования фракталов: экстремумы с относительным размером от 140 до 420 пипсов при наличии тренда (а), экстремумы при отсутствии движения цены, относительный размер не более 50 пипсов (б)
Во втором случае относительный размер экстремумов (изменение цены от одного экстремума к другому) может не превышать нескольких пипсов. Такие малозначимые пики и впадины в большинстве случаев при торговле вручную не учитываются. Изменение таймфрейма не позволит отфильтровать малозначимые экстремумы — при длительном флэте они все равно будут определены.
Может возникнуть и другая, обратная сложность: определены будут не все экстремумы. Если за небольшой промежуток времени наблюдались крупные колебания цен, с большим количеством пиков и впадин, — они не будут обнаружены. Фракталы позволят обнаружить только 2 экстремума на временном интервале, который определяется 5 барами текущего таймфрейма. Исходя из вышеизложенного, нельзя рекомендовать фракталы для автоматического обнаружения всех или большинства важных экстремумов при автоматической торговле.
Инструменты, представленные в статье "Вклад Томаса Демарка в технический анализ", имеют те же недостатки, что и фракталы. Если выбирать большое окно для поиска экстремумов, то многие из них могут быть пропущены. Если же окно имеет небольшой размер – определяются малозначимые экстремумы. В любом случае, при обработке результатов нужно либо постоянно вручную оптимизировать параметры и отбрасывать малозначимые минимумы и максимумы, либо разрабатывать для этого специальный алгоритм.
1.2. Использование скользящих средних при поиске экстремумов
При автоматизации поиска экстремумов заманчивой выглядит идея с использованием некоей средней линии, например, скользящей средней. Поиск экстремума выполняется на заданном количестве баров при условии, если цена уходит от средней линии на некоторое заданное в пунктах расстояние. Такой инструмент позволяет отфильтровать малозначимые пики и впадины, а значит, выглядит лучше, чем фракталы. Однако проблему нахождения близкорасположенных максимумов и минимумов он по-прежнему не решает (рис.2, а).
Рис. 2. Использование скользящих средних при поиске экстремумов: 2 экстремума определяются как один (а), пропуск экстремума расположенного в непосредственной близости от скользящей средней (б)
Можно попытаться совместно использовать скользящие средние и фракталы. Скользящая средняя используется для фильтрации малозначимых экстремумов, а фракталы — для их последующего поиска на указанном интервале. Но и такая схема не снимает всех проблем. Все равно возникает необходимость оптимально выбрать окно. В результате, каким бы способом мы бы не искали максимум или минимум, все равно необходим постоянный подбор параметров окна для поиска. При их неоптимальном подборе из двух слишком близко расположенных друг к другу пиков может определяться только один (рис.2, а).
Рассмотрим еще одну сложность, свойственную этому способу определения экстремумов. Если возникает рыночная ситуация с резкими колебаниями цены, скользящая средняя, в зависимости от периода, может на такой сигнал существенно не отреагировать. В такой ситуации (рис.2, б) впадина, располагающаяся между двумя вершинами и находящаяся близко от скользящей средней не будет определена. Подобные ситуации на рынке достаточно редки, но они заставляют задуматься о правильном выборе окна скользящей средней.
Итак, представленные выше способы определения экстремумов, а также производные от них имеют недостатки и требуют дополнительных программных решений. Рассмотрим подробнее сложности, возникающие при определении экстремумов, а также алгоритмы, позволяющие их преодолеть.
2. Проблемы и неоднозначности, возникающие при поиске экстремумов
2.1. Выбор размаха перепада для нахождения пиков и впадинВ существующих стратегиях и тактиках использование экстремумов может быть явным или неявным. При этом задача по определению экстремумов часто субъективна: на одном и том же графике один человек увидит одни вершины и впадины, а другой отметит совсем иные. Рассмотрим одну из известных графических моделей – «Двойную вершину».
Рис. 3. Графическая модель "Двойная вершина"
На двух графиках (рис. 3) представлена одна и та же модель, но в зависимости от размаха перепада между экстремумами мы можем ее определить, а можем и не определить. Так, на первом графике после первой вершины идет впадина, а затем размещается вторая вершина. Соответственно, если бы впадины между вершинами не было, мы бы не смогли определить графическую модель «Двойная вершина». Модель была бы определена как обычный экстремум. Аналогичная ситуация возникает, если впадина выражена неявно, и тогда в зависимости от субъективной точки зрения «Двойная вершина» может быть определена или не определена. В таком случае на первом графике мы найдем эту модель скорее, чем на втором, при этом различие между графиками будет состоять лишь в размере перепада между соседними экстремумами.
Рассмотрим и другой пример: в некоторых стратегиях тренд определяется как восходящий, если последующие экстремумы (и пики, и впадины) размещаются выше предыдущих. Аналогично определяется и нисходящий тренд. На рассматриваемом примере (рис. 4) можно определить направление тренда, и в этом случае явно используются экстремумы.
Рис. 4. Разнонаправленное движение цены на одном и том же графике: восходящий тренд (а), нисходящий тренд (б)
Получается, что на одном и том же графике можно найти и восходящий, и нисходящий тренд. В первом случае (рис. 4, а) определенные экстремумы 1,2,3,4 явно указывают на восходящую тенденцию. Определяя на том же графике в качестве экстремумов точки 2,5,6,3 (рис. 4, б), можно сделать вывод о наличии нисходящего тренда. Используя совершенно иные экстремумы, в итоге можно получить любой из двух вариантов. Исходя из этого, делаем вывод о том, что именно размах перепада будет в наибольшей степени влиять на положение экстремумов.
2.2. Эффективное разделение соседних пиков или впадинЕсть и вторая проблема, возникающая при определении экстремумов. Для эффективного определения и разделения двух или более пиков между ними должна быть впадина. Это утверждение справедливо как для первого примера – нахождения двойной вершины, так и для второго, но тут ситуация еще интересней. На представленных графиках (рис. 5,6), согласно приведенной выше стратегии, определить тренд можно только после нахождения экстремумов.
Рис. 5. Определение пиков и впадин при долгосрочном инвестировании
Рис. 6. Определение малозначимых пиков и впадин
Если не найти впадины, разделяющие пики, и, аналогично, пики разделяющие впадины, стратегия не сможет работать согласно установленным критериям, хотя на графике визуально определяется восходящий тренд. Рассмотрим один характерный пример. При восходящем движении цены образуется один пик, а за ним следующий, более высокий. Если впадина между ними отсутствует или слабо выражена, то в качестве экстремума будет определен только более высокий пик. В случае, когда экстремумы определяются относительно некоторой средней линии, например скользящей средней, задача разделения двух соседних пиков или впадин тоже актуальна. И в этом случае для разделения двух вершин также нужно использовать экстремум между ними.
Обобщив вышеизложенное, для всех стратегий, явно или неявно применяющих экстремумы, можно использовать следующее предположение: цена движется от пика к впадине и от впадины к пику, как в ситуации при движении от прошлого к будущему, так и в обратном направлении. Если не использовать такое предположение, то два пика на ценовом графике в зависимости от субъективной точки зрения:
- либо будут обнаружены,
- либо будет определен только более высокий пик,
- либо же ни один из этих двух экстремумов не будет обнаружен.
Аналогичная ситуация и для впадин. На основе этого предположения можно разработать алгоритм для однозначного нахождения экстремумов с использованием выбранного размаха перепада.
2.3. Определение первого экстремумаТретья возникающая проблема тоже связана с ценовым перепадом и заключается в определении первого экстремума. Для любой торговой тактики или стратегии самые новые экстремумы будут более важны, чем более давние. А как мы уже выяснили, от определения одного экстремума зависит местоположение соседних пиков и впадин. Поэтому, если выбирать экстремум на некотором отдалении от текущего времени, то полученные результаты будут сильнее зависеть от отдаленных исторических данных, а от самых свежих колебаний цены будут зависеть слабо. Эта проблема существует при использовании индикатора ZigZag. Положение последних экстремумов слабо зависит от последних ценовых колебаний.
Совсем иная ситуация возникает при нахождении экстремумов с конца графика. В этом случае вначале находится ближайший с конца пик либо впадина, после чего однозначно определяются все остальные. В зависимости от стратегии, которая используется и в зависимости от выбранного размаха перепада можно применять 3 варианта:
- находится ближайший пик,
- находится ближайшая впадина,
- находится ближайший экстремум (либо пик, либо впадина).
Рассмотрим случай нахождения ближайшего экстремума. Выбрав определенный размах перепада, можно однозначно определить первый ближайший экстремум. Однако определение это произойдет с некоторой задержкой, которая может негативно сказаться на работе стратегии. Ведь чтобы «увидеть» экстремум, мы должны зафиксировать установленное размахом перепада изменение цены относительно него. На изменение цены требуется определенное время, это и вызывает задержку. Можно также использовать в качестве экстремума последнее известное значение цены, но маловероятно, что именно эта точка впоследствии окажется пиком или впадиной.
В таком случае целесообразно найти первый экстремум с помощью дополнительного коэффициента. Целесообразно представить его значение в виде дробной части от размаха перепада, который используется для нахождения остальных экстремумов. Например, выберем значение 0.5.
Выбранное значение дополнительного коэффициента будет определять минимальное изменение цены от текущего значения до значения минимальной цены для ближайшей впадины (максимального значения цены для ближайшей вершины) позволяющее определить эту впадину (вершину) в качестве экстремума. Если перепад между текущим значением цены и экстремальным значением для ближайшей вершины или впадины меньше заданной величины, то такой экстремум не будет определен. В этом случае есть определенная уверенность в том что, найденный первый экстремум в последующем действительно окажется пиком или впадиной. В то же время решается задача максимально раннего определения экстремумов, последующего их анализа и при необходимости – открытия сделок.
Рассмотрим пример, для которого размах перепада задан на уровне 140 пипсов. Для определения первого экстремума будет использоваться дополнительный коэффициент. В первом случае его значение 0.9 (рис. 7, а) а во втором – 0.7 (рис. 7, б). Тогда значение дополнительного коэффициента будет определять минимальный ценовой перепад в пипсах, позволяющий обнаружить первый экстремум. Для первого случая это будет перепад 126 пипсов, а для второго — 98 пипсов. В обоих случаях рассматривается один и тот же график. Вертикальная линия условно показывает текущий момент времени, для которого производится расчет, а точками обозначены найденные на данном участке экстремумы.
Рис. 7. Влияние дополнительного коэффициента на определение экстремумов: для значения 0.9 (126 пипсов) первый экстремум определяется при перепаде 205 пипсов (а), при значении 0,7 (98 пипсов) первый экстремум определяется уже при перепаде в 120 пипсов, определение остальных двух выполняется согласно заданному размаху перепада (б)
Выбранное значение дополнительного коэффициента для первого случая позволило определить первую впадину только при перепаде в 205 пипсов, в то время как минимальный ценовой перепад равен значению 126 пипсов. Для второго случая при выбранном дополнительном коэффициенте равном 0,7 (98 пипсов), первая впадина определена при перепаде 120 пипсов относительно текущего значения цены. Два следующих за ней экстремума определялись согласно заданному размаху перепада, равному 140 пипсов. Соответственно, ценовой перепад между первой впадиной и следующим за ней пиком несколько больше 140 пипсов. Вторая впадина также определена ценовым перепадом более 140 пипсов, но уже относительно найденной вершины.
Как видим, значение дополнительного коэффициента существенно влияет на положение первого найденного экстремума, а может влиять и на его тип. Для различных значений (в пределах от 0 до 1) первым может быть определен либо пик, либо впадина для одного и того же графика. Первые два экстремума, которые обнаруживались для второго случая (рис. 7, б) в первом случае и вовсе не были определены.
Также стоит заметить, что при еще более низком значении коэффициента первый экстремум будет определен несколько быстрее. Так, для второго случая (рис. 7, б) при значении дополнительного коэффициента 0.4, первый найденный экстремум мог быть определен на 5 баров раньше (на 5 минут раньше по шкале времени текущего таймфрейма).
3. Алгоритмические решения задач нахождения экстремумов и их программная реализация
Начнем с выбора ценового перепада для построения экстремумов. Очевидно, что в зависимости от таймфрейма, размер баров и параметры экстремумов будут сильно отличаться. Наличие или отсутствие пиков и впадин будет зависеть еще и от наличия тренда, времени суток и других факторов. Используя существующие индикаторы, например фракталы и подобные инструменты, мы сможем найти экстремумы на любом таймфрейме, как при наличии тренда, так и в его отсутствие. Если использовать скользящую среднюю при поиске пиков и впадин, то размер экстремумов по отношению к скользящей средней может быть 2 пункта, а может быть и 100. Будут ли для нас интересны экстремумы размером в 2 пункта при внутридневной торговле? Скорее всего, нет. При долгосрочном инвестировании мы не обратим внимание и на экстремумы меньше 20 пунктов, вне зависимости от рассматриваемого таймфрейма.
Именно поэтому необходимо ввести термин размаха перепада – под ним будем мы понимать некоторое минимальное значение размера экстремумов. В качестве точки отсчета можно использовать скользящую среднюю, с ее помощью определить расстояние до экстремума и ограничить его размер снизу. Но в этом случае период скользящей средней будет существенно влиять на положение найденных пиков и впадин, и выбрать какой-то один период в качестве эталона будет сложно.
Поэтому в дальнейшем будем использовать предположение, согласно которому цена движется от пика к впадине, а от впадины к пику и размах перепада будем определять как минимальное изменение цены в пунктах между двумя соседними экстремумами — пиком и впадиной. Если какой-то из экстремумов уже определен, то соседний по отношению к нему должен находится на расстоянии, не меньшем, чем расстояние, заданное размахом перепада. Используя такой критерий, мы имеем возможность определять экстремумы вне зависимости от таймфрейма и наличия тренда. Такой инструмент отлично подойдет как при внутридневной торговле, так и для долгосрочного инвестирования.
Рассмотрим алгоритм его работы. Для начала визуально определим экстремумы, используя один и тот же график, но в первом случае размах перепада – 60 пипсов (рис. 8), а во втором — 30 пипсов (рис. 9). Предположим также, что первый экстремум уже найден (точка 1), и мы ищем предшествующие ему.
Рис. 8. Использование размаха перепада 60 пипсов
Рис. 9. Использование размаха перепада 30 пипсов
Поиск экстремумов проводится с конца графика (от точки 1). В первом случае в представленном окне было найдено 4 экстремума, во втором случае на том же отрезке их 10. При увеличении размаха перепада на указанной части графика экстремумы вовсе не будут определяться. Поэтому, исходя из волатильности рынка и выбранного таймфрейма, нужно реалистично подходить к выбору окна для поиска экстремумов. Под окном в данном случае мы понимаем количество баров, на котором осуществляем поиск.
Объединив все вышесказанное, предложим для поиска экстремумов итерационный алгоритм. Почему итерационный? Потому что после первого пика обязательно должна располагаться впадина, а после нее — второй пик и т. д. Если второй пик не найден (график упрямо не хочет идти вверх), производится переопределение положения впадины, и на временном ряде она отодвигается все дальше и дальше. Таким же образом можно скорректировать положение первого пика и любого другого экстремума. Еще нужно отбросить те варианты, где один и тот же бар одновременно определяется и как вершина, и как впадина.
Конечно же, такой подход требует большого количества вычислений. Его целесообразно применять для определения нескольких экстремумов, и чем их меньше, тем быстрее будет работать программа. Скорость вычислений будет определяться еще и окном, в котором производится поиск. Но такой подход себя оправдывает, поскольку позволяет однозначно находить пики и впадины при максимальном влиянии самых свежих ценовых колебаний. Если нужно определять много экстремумов, я рекомендовал бы использовать индикатор ZigZag.
3.2 Программная реализация индикатораКод итерационного алгоритма, представленный ниже, в целях максимального быстродействия использует небольшое количество итераций. Это упрощение не приводит к существенной потере качества обнаружения экстремумов. Основные входящие параметры — окно для поиска экстремумов и размах перепада.
input double delta_points=160; // размах перепада, определяющий минимальное расстояние между пиком и впадиной в пунктах
input double first_extrem=0.9; // дополнительный коэффициент для поиска первого экстремума
input double reload_time=5; // значение временного интервала, через который производится перерасчет значений индикатора в секундах
Тело программы содержит 3 вложенных цикла, необходимые для определения четырех экстремумов. В данной части программы определяются только первая впадина и связанные с ней экстремумы. Определение первого пика и связанных с ним экстремумов реализуется похожим образом.
datetime Time[];
ArraySetAsSeries(Low,true);
int copied1=CopyLow(Symbol(),0,0,bars+2,Low);
ArraySetAsSeries(High,true);
int copied2=CopyHigh(Symbol(),0,0,bars+2,High);
ArraySetAsSeries(Time,true);
int copied3=CopyTime(Symbol(),0,0,bars+2,Time);
double delta=delta_points*Point(); // размах перепада между экстремумами в абсолютных величинах
int j,k,l;
int j2,k2,l2;
double j1,k1,l1;
int min[6]; // массив, определяющий впадины, значение соответствует номеру бара для найденного экстремума
int max[6]; // массив, определяющий пики, значение соответствует номеру бара для найденного экстремума
int mag1=bars;
int mag2=bars;
int mag3=bars;
int mag4=bars;
j1=SymbolInfoDouble(Symbol(),SYMBOL_BID)+(1-first_extrem)*delta_points*Point();
// при поиске первого экстремума дополнительный коэффициент определяет минимальную цену, ниже которой должна располагаться первая впадина
j2=0; // на первой итерации поиск ведется начиная с последнего бара истории
for(j=0;j<=15;j++) // цикл, определяющий первую впадину - min[1]
{
min[1]=minimum(j2,bars,j1);
//определяется ближайшая впадина на указанном интервале
j2=min[1]+1; // на следующей итерации поиск ведется от уже найденной впадины min[1]
j1=Low[min[1]]+delta;
//минимальная цена для впадины, найденной на последующей итерации должна быть ниже, чем минимальная цена для впадины, найденной на текущей итерации
k1=Low[min[1]];
//минимальная цена для впадины при поиске последующей вершины определяет максимальную цену, выше которой должна располагаться эта вершина
k2=min[1]; //поиск пика, располагающегося за впадиной, ведется от найденной впадины min[1]
for(k=0;k<=12;k++) // цикл, определяющий первый пик - max[1]
{
max[1]=maximum(k2,bars,k1);
//--- определяется ближайший пик на указанном интервале
k1=High[max[1]]-delta;
//максимальная цена для пика, найденного на последующей итерации должна быть выше, чем максимальная цена для пика, найденного на текущей итерации
k2=max[1]+1; // на следующей итерации поиск ведется от уже найденного пика max[1]
l1=High[max[1]];
//максимальная цена для вершины при поиске последующей впадины определяет минимальную цену, ниже которой должна располагаться эта впадина
l2=max[1]; // поиск впадины, располагающейся за пиком, ведется от найденного пика max[1]
for(l=0;l<=10;l++) // цикл, определяющий вторую впадину - min[2], и второй пик max[2]
{
min[2]=minimum(l2,bars,l1);
//---определяется ближайшая впадина на указанном интервале
l1=Low[min[2]]+delta;
//минимальная цена для впадины, найденной на последующей итерации должна быть ниже, чем минимальная цена для впадины, найденной на текущей итерации
l2=min[2]+1; // на следующей итерации поиск ведется от уже найденной впадины min[2]
max[2]=maximum(min[2],bars,Low[min[2]]);
//определяется ближайший пик на указанном интервале
if(max[1]>min[1] && min[1]>0 && min[2]>max[1] && min[2]<max[2] && max[2]<mag4)
//отфильтровываются совпадающие экстремумы и особые случаи
{
mag1=min[1]; // на каждой итерации в случае выполнения условия, положения найденных экстремумов запоминаются
mag2=max[1];
mag3=min[2];
mag4=max[2];
}
}
}
}
min[1]=mag1; // экстремумы определены, в ином случае всем переменным присваивается значение bars
max[1]=mag2;
min[2]=mag3;
max[2]=mag4;
Задача нахождения ближайшего бара, минимальная цена для которого ниже указанной величины (или максимальная цена для которого выше указанной величины), довольно проста и вынесена в отдельную функцию.
//функция определяет ближайшую впадину на указанном интервале, которая располагается ниже цены price0 на расстоянии большем, чем размах перепада
{
double High[],Low[];
ArraySetAsSeries(Low,true);
int copied4=CopyLow(Symbol(),0,0,bars+2,Low);
int i,e;
e=bars;
double pr=price0-delta_points*Point(); // значение цены ниже которой должна располагаться впадина уже с добавлением размаха перепада
for(i=a;i<=b;i++) // поиск впадины в окне заданном параметрами a и b
{
if(Low[i]<pr && Low[i]<Low[i+1]) // определяется ближайшая впадина, после которой начинается рост цены
{
e=i;
break;
}
}
return(e);
}
int maximum(int a,int b,double price1)
//--- функция определяет ближайший пик на указанном интервале, который располагается выше цены price1 на расстоянии большем, чем размах перепада
{
double High[],Low[];
ArraySetAsSeries(High,true);
int copied5=CopyHigh(Symbol(),0,0,bars+2,High);
int i,e;
e=bars;
double pr1=price1+delta_points*Point(); // значение цены выше которой должен располагаться пик уже с добавлением размаха перепада
for(i=a;i<=b;i++) // поиск пика в окне заданном параметрами a и b
{
if(High[i]>pr1 && High[i]>High[i+1]) // определяется ближайший пик, после которого начинается падение цены
{
e=i;
break;
}
}
return(e);
}
Задача нахождения экстремумов решена, но только в первом приближении. Мы должны учитывать, что найденный по этому алгоритму пик, располагающийся между двумя впадинами, может не быть самым высоким на указанном интервале. Так как поиск начинался с конца графика, то и уточнение положений пиков и впадин должно проводиться с конца для первого, второго, третьего и последующих экстремумов. Проверка и коррекция положения для пиков и для впадин вынесены в отдельные функции. Программная реализация для уточнения положения экстремумов выглядит следующим образом:
min[1]=check_min(min[1],max[1]); // проверка и коррекции положения первой впадины на указанном интервале
max[1]=check_max(max[1],min[2]); // проверка и коррекции положения первого пика на указанном интервале
min[2]=check_min(min[2],max[2]); // проверка и коррекции положения второй впадины на указанном интервале
int check_min(int a,int b)
// функция для проверки и коррекции положения впадины на указанном интервале
{
double High[],Low[];
ArraySetAsSeries(Low,true);
int copied6=CopyLow(Symbol(),0,0,bars+1,Low);
int i,c;
c=a;
for(i=a+1;i<b;i++) // при поиске впадины проверяются все бары, заданные окном
{
if(Low[i]<Low[a] && Low[i]<Low[c]) // если найдена впадина, которая располагается ниже
c=i; // положение впадины переопределяется
}
return(c);
}
//--- функция для проверки и коррекции положения пика на указанном интервале
{
double High[],Low[];
ArraySetAsSeries(High,true);
int copied7=CopyHigh(Symbol(),0,0,bars+1,High);
int i,d;
d=a;
for(i=(a+1);i<b;i++) // при поиске впадины проверяются все бары заданные окном
{
if(High[i]>High[a] && High[i]>High[d]) // если найдена вершина, которая располагается выше
d=i; // положение вершины переопределяется
}
return(d);
}
Если было найдено 4 экстремума, то можно ограничиться уточнением только положения первых трех. Дело в том, что функция для проверки и коррекции положения работает в диапазоне, определяющемся для текущего экстремума его собственным положением и положением следующего за ним экстремума. После уточнения можно быть уверенным в том, что найденные экстремумы соответствуют поставленным критериям.
Далее производится поиск первого с конца графика пика, после чего производится сравнение положений первой впадины и первого пика. В результате проведенных вычислений на выходе мы получаем положение ближайшего с конца графика первого экстремума и остальных связанных с ним экстремумов.
Остановлюсь еще раз на определении первого экстремума. Выше было предложено ввести дополнительный коэффициент для его нахождения — дробную часть от размаха перепада, например 0.7. При этом высокие его значения (0.8…0.9) позволяют с высокой степенью точности определять первый экстремум, но с запаздыванием, а низкие значения (0.1…0.25) позволяют уменьшить запаздывание до минимума, но в таком случае сильно размывается точность определения. Соответственно, значение дополнительного коэффициента нужно выбирать в зависимости от используемой стратегии.
Найденные пики и впадины обозначаются стрелками. Стрелки отображают координаты экстремумов (одна координата отображает положение на временном ряде, другая — значение максимальной/минимальной цены для найденного пика/впадины). Поскольку при расчете выполняется множество вычислений, в программе предусмотрен входной параметр, задающий значение интервала, через который производится перерасчет значений индикатора. Если в выбранном окне пики и впадины не найдены, индикатор выдает соответствующее сообщение. Программная реализация графического отображения экстремумов выглядит так:
if(min[1]<Max[1]) // в случае если впадина расположена ближе, обозначается ее положение и положение связанных с ней экстремумов
{
ObjectDelete(0,"id_1"); // удаление обозначений сделанных на предыдущем шаге
ObjectDelete(0,"id_2");
ObjectDelete(0,"id_3");
ObjectDelete(0,"id_4");
ObjectDelete(0,"id_5");
ObjectDelete(0,"id_6");
ObjectCreate(0,"id_1",OBJ_ARROW_UP,0,Time[min[1]],Low[min[1]]); // обозначаем первую впадину
ObjectSetInteger(0,"id_1",OBJPROP_ANCHOR,ANCHOR_TOP);
//--- для найденной первой впадины привязка осуществляется по положению на временном ряде и значению минимальной цены
ObjectCreate(0,"id_2",OBJ_ARROW_DOWN,0,Time[max[1]],High[max[1]]); // обозначаем первый пик
ObjectSetInteger(0,"id_2",OBJPROP_ANCHOR,ANCHOR_BOTTOM);
//--- для найденного пика привязка осуществляется по положению на временном ряде и значению максимальной цены
ObjectCreate(0,"id_3",OBJ_ARROW_UP,0,Time[min[2]],Low[min[2]]); // обозначаем вторую впадину
ObjectSetInteger(0,"id_3",OBJPROP_ANCHOR,ANCHOR_TOP);
//--- для найденной второй впадины привязка осуществляется по положению на временном ряде и значению минимальной цены
}
if(min[1]>Max[1]) // в случае если пик расположен ближе обозначается его положение и положение связанных с ним экстремумов
{
ObjectDelete(0,"id_1"); // удаление обозначений сделанных на предыдущем шаге
ObjectDelete(0,"id_2");
ObjectDelete(0,"id_3");
ObjectDelete(0,"id_4");
ObjectDelete(0,"id_5");
ObjectDelete(0,"id_6");
ObjectCreate(0,"id_4",OBJ_ARROW_DOWN,0,Time[Max[1]],High[Max[1]]); // обозначаем первый пик
ObjectSetInteger(0,"id_4",OBJPROP_ANCHOR,ANCHOR_BOTTOM);
//для найденного первого пика привязка осуществляется по положению на временном ряде и значению максимальной цены
ObjectCreate(0,"id_5",OBJ_ARROW_UP,0,Time[Min[1]],Low[Min[1]]); // обозначаем первую впадину
ObjectSetInteger(0,"id_5",OBJPROP_ANCHOR,ANCHOR_TOP);
//для найденной впадины привязка осуществляется по положению на временном ряде и значению минимальной цены
ObjectCreate(0,"id_6",OBJ_ARROW_DOWN,0,Time[Max[2]],High[Max[2]]); // обозначаем второй пик
ObjectSetInteger(0,"id_6",OBJPROP_ANCHOR,ANCHOR_BOTTOM);
//для найденного второго пика привязка осуществляется по положению на временном ряде и значению максимальной цены
}
if(min[1]==Max[1]) Alert("В заданном окне, ",bars," баров, экстремумов не найдено");
// в случае если экстремумы не найдены, выводится соответствующее сообщение
В процессе деинициализации индикатора объекты, обозначающие пики и впадины, удаляются.
По представленным алгоритмам был разработан пользовательский индикатор, который разыскивает экстремумы и обозначает их на графике (рис. 10).
Рис. 10. Результаты работы индикатора: размах перепада 120 пипсов (а), размах перепада 160 пипсов (б)
Полученные результаты определяются размахом перепада. Для значения 120 пипсов и меньше (рис. 10, а) экстремумы располагаются достаточно близко друг от друга и размер окна не так важен. Для значения 160 пипсов и более (рис. 10, б) экстремумы располагаются достаточно далеко. Это необходимо учитывать при выборе окна для их поиска. В случае застоя на рынке оптимально подобранное окно позволит не только автоматически найти пики и впадины при появлении небольшого движения, но и отфильтровать (пропустить) экстремумы, разделенные очень большими временными интервалами.
3.3 Советник, реализующий стратегию расхождения между гистограммой MACD и ценамиПрименить представленные алгоритмы можно для реализации разных стратегий. Результаты работы индикатора scale_factor хорошо подходят для работы с графическими моделями, такими как «голова и плечи», «двойная вершина», «двойное дно» и др. Можно использовать их и в стратегиях, использующих расхождение пиков и впадин для графиков цен и индикаторов. Один из примеров – эксперт, который работает по стратегии расхождения графика цены и гистограммы MACD. Эта стратегия достаточно хорошо освещена в литературе, в книге Александра Элдера "Как играть и выигрывать на бирже".
Согласно этой стратегии, если цена, поднимаясь, образует новый пик, который размещается выше предыдущего, но при этом пик, образованный гистограммой MACD, ниже предыдущего – это сигнал для продажи.
При движении цены вниз, если образуется новая впадина, которая размещается ниже ближайшей впадины, но при этом впадина, образованная гистограммой MACD, размещается выше предыдущей впадины гистограммы, возникает сигнал, указывающий на поворотный момент и необходимость совершения покупки.
Эксперт, реализующий представленный выше алгоритм, однозначно находит необходимые для работы пики и впадины, согласно указанному размаху перепада, в первую очередь ориентируясь на самые последние изменения на ценовом графике.
Входящие параметры — окно для поиска экстремумов и размах перепада. Также необходимо задать минимальное расхождение цен для 2 последних пиков при росте (для 2 последних впадин при падении цены), минимальное расхождение гистограммы MACD для экстремумов. Задается риск на каждой сделке в валюте депозита и дополнительный коэффициент. Параметр guard_points определяет дополнительное смещение стоп-лосса вниз относительно минимального значения цены для ближайшей впадины, если открывается длинная позиция. Соответственно, стоп-лосс смещается вверх при открытии короткой позиции. Также есть возможность выводить параметры найденных экстремумов в случае открытия сделок (show_info=1).
input double delta_points=160; // размах перепада, определяющий минимальное расстояние между пиком и впадиной в пунктах
input double first_extrem=0.9; // дополнительный коэффициент для поиска первого экстремума
input int orderr_size=10; // риск на каждой сделке
input double macd_t=0.00002; // минимальное расхождение гистограммы MACD
input double trend=100; // минимальное расхождение цен для 2 ближайших пиков/впадин
input double guard_points=30; // смещение стоплосса
input int time=0; // временная задержка в секундах
input int show_info=0; // вывод информации об экстремумах
Расчеты могут выполняться на каждом тике, как и торговые операции. Но стратегия будет хорошо работать и при введении временной задержки. После определения основных параметров в абсолютных величинах переходим к нахождению экстремумов. Первая часть программы позволяет обнаружить экстремумы, в случае если первый из них — впадина, после чего выполняется уточнение их положений. Вторая часть кода позволяет находить экстремумы для случая, когда первой с конца графика размещается вершина. На следующем шаге выполняется уточнение параметров пиков и впадин.
{
Sleep(1000*time); // введение временной задержки
double High[],Low[];
ArraySetAsSeries(Low,true);
int copied1=CopyLow(Symbol(),0,0,bars+2,Low);
ArraySetAsSeries(High,true);
int copied2=CopyHigh(Symbol(),0,0,bars+2,High);
ArraySetAsSeries(Time,true);
int copied3=CopyTime(Symbol(),0,0,bars+2,Time);
MqlTick last_tick;
double Bid=last_tick.bid;
double Ask=last_tick.ask;
double delta=delta_points*Point(); // размах перепада в абсолютных величинах
double trendd=trend*Point(); // минимальное расхождение цен для 2 ближайших пиков/впадин в абсолютных величинах
double guard=guard_points*Point(); // смещение стоплосса в абсолютных величинах
int j,k,l;
int j2,k2,l2;
double j1,k1,l1;
int min[6]; // массив, определяющий впадины в случае если первый найденный экстремум - впадина, значение соответствует номеру бара для найденного экстремума
int max[6]; // массив, определяющий пики в случае если первый найденный экстремум - впадина, значение соответствует номеру бара для найденного экстремума
int Min[6]; // массив, определяющий впадины в случае если первый найденный экстремум - пик, значение соответствует номеру бара для найденного экстремума
int Max[6]; // массив, определяющий пики в случае если первый найденный экстремум - пик, значение соответствует номеру бара для найденного экстремума
int mag1=bars;
int mag2=bars;
int mag3=bars;
int mag4=bars;
j1=SymbolInfoDouble(Symbol(),SYMBOL_BID)+(1-first_extrem)*delta_points*Point();
// при поиске первого экстремума дополнительный коэффициент определяет минимальную цену, ниже которой должна располагаться первая впадина
j2=0; // на первой итерации поиск ведется начиная с последнего бара истории
for(j=0;j<=15;j++) // цикл, определяющий первую впадину - min[1]
{
min[1]=minimum(j2,bars,j1);
//определяется ближайшая впадина на указанном интервале
j2=min[1]+1; //на следующей итерации поиск ведется от уже найденной впадины min[1]
j1=Low[min[1]]+delta;
//--- минимальная цена для впадины, найденной на последующей итерации должна быть ниже, чем минимальная цена для впадины, найденной на текущей итерации
k1=Low[min[1]];
//минимальная цена для впадины при поиске последующей вершины определяет максимальную цену, выше которой должна располагаться эта вершина
k2=min[1]; // поиск пика располагающегося за впадиной ведется от найденной впадины min[1]
for(k=0;k<=12;k++) // цикл, определяющий первый пик - max[1]
{
max[1]=maximum(k2,bars,k1);
//--- определяется ближайший пик на указанном интервале
k1=High[max[1]]-delta;
//--- максимальная цена для пика, найденного на последующей итерации должна быть выше, чем максимальная цена для пика, найденного на текущей итерации
k2=max[1]+1; // на следующей итерации поиск ведется от уже найденного пика max[1]
l1=High[max[1]];
//--- максимальная цена для вершины при поиске последующей впадины определяет минимальную цену, ниже которой должна располагаться эта впадина
l2=max[1]; // поиск впадины, располагающейся за пиком, ведется от найденного пика max[1]
for(l=0;l<=10;l++) // цикл, определяющий вторую впадину - min[2], и второй пик max[2]
{
min[2]=minimum(l2,bars,l1);
//--- определяется ближайшая впадина на указанном интервале
l1=Low[min[2]]+delta;
//минимальная цена для впадины, найденной на последующей итерации должна быть ниже, чем минимальная цена для впадины, найденной на текущей итерации
l2=min[2]+1; //на следующей итерации поиск ведется от уже найденной впадины min[2]
max[2]=maximum(min[2],bars,Low[min[2]]);
//определяется ближайший пик на указанном интервале
if(max[1]>min[1] && min[1]>0 && min[2]>max[1] && min[2]<max[2] && max[2]<mag4)
//--- отфильтровываются совпадающие экстремумы и особые случаи
{
mag1=min[1]; // на каждой итерации в случае выполнения условия, положения найденных экстремумов запоминаются
mag2=max[1];
mag3=min[2];
mag4=max[2];
}
}
}
}
//--- экстремумы определены, в ином случае всем переменным присваивается значение bars
min[1]=mag1;
max[1]=mag2;
min[2]=mag3;
max[2]=mag4;
//--- проверка и коррекции положения экстремумов на указанном интервале
max[1]=check_max(max[1],min[2]);
min[2]=check_min(min[2],max[2]);
//---------------------------------------------------------------------------------------------------------------
mag1=bars;
mag2=bars;
mag3=bars;
mag4=bars;
j1=SymbolInfoDouble(Symbol(),SYMBOL_BID)-(1-first_extrem)*delta_points*Point();
// при поиске первого экстремума дополнительный коэффициент определяет максимальную цену, выше которой должна располагаться первая вершина
j2=0; // на первой итерации поиск ведется начиная с последнего бара истории
for(j=0;j<=15;j++) // цикл, определяющий первую вершину - Max[1]
{
Max[1]=maximum(j2,bars,j1);
//определяется ближайшая вершина на указанном интервале
j1=High[Max[1]]-delta;
//максимальная цена для пика, найденного на последующей итерации должна быть выше, чем максимальная цена для пика, найденного на текущей итерации
j2=Max[1]+1; // на следующей итерации поиск ведется от уже найденной вершины Max[1]
k1=High[Max[1]];
//максимальная цена для вершины при поиске последующей впадины определяет минимальную цену, ниже которой должна располагаться эта впадина
k2=Max[1]; // поиск впадины располагающейся за пиком ведется от найденного пика Max[1]
for(k=0;k<=12;k++) //цикл, определяющий первую вершину - Min[1]
{
Min[1]=minimum(k2,bars,k1);
//--- определяется ближайшая впадина на указанном интервале
k1=Low[Min[1]]+delta;
//минимальная цена для впадины, найденной на последующей итерации должна быть ниже, чем минимальная цена для впадины, найденной на текущей итерации
k2=Min[1]+1; // на следующей итерации поиск ведется от уже найденной впадины Min[1]
l1=Low[Min[1]];
//---минимальная цена для впадины при поиске последующей вершины определяет максимальную цену, выше которой должна располагаться эта вершина
l2=Min[1]; // поиск вершины, располагающейся за впадиной, ведется от найденной впадины Min[1]
for(l=0;l<=10;l++)//цикл, определяющий вторую вершину - Max[2], и вторую впадину Min[2]
{
Max[2]=maximum(l2,bars,l1);
//определяется ближайшая вершина на указанном интервале
l1=High[Max[2]]-delta;
//максимальная цена для пика, найденного на последующей итерации должна быть выше, чем максимальная цена для пика, найденного на текущей итерации
l2=Max[2]+1; //на следующей итерации поиск ведется от уже найденной вершины Max[2]
Min[2]=minimum(Max[2],bars,High[Max[2]]);
//---определяется ближайшая впадина на указанном интервале
if(Max[2]>Min[1] && Min[1]>Max[1] && Max[1]>0 && Max[2]<Min[2] && Min[2]<bars)
//--- отфильтровываются совпадающие экстремумы и особые случаи
{
mag1=Max[1]; // на каждой итерации в случае выполнения условия, положения найденных экстремумов запоминаются
mag2=Min[1];
mag3=Max[2];
mag4=Min[2];
}
}
}
}
Max[1]=mag1; // экстремумы определены, в ином случае всем переменным присваивается значение bars
Min[1]=mag2;
Max[2]=mag3;
Min[2]=mag4;
Max[1]=check_max(Max[1],Min[1]); // проверка и коррекции положения экстремумов на указанном интервале
Min[1]=check_min(Min[1],Max[2]);
Max[2]=check_max(Max[2],Min[2]);
В дальнейшем можно использовать как первый найденный пик, так и первую найденную впадину, но наиболее целесообразным представляется вариант использования максимально близко расположенного экстремума, а также пиков и впадин, полученных на его основе.
Для обоих вариантов рассчитывается значение лота, а также значения индикатора, соответствующие положениям экстремумов. Проверяется условие правильного обнаружения экстремумов и отсутствия открытых позиций.
Если наблюдается расхождение значений цен для экстремумов и гистограммы MACD, и это расхождение не меньше значений, установленных входящими параметрами, открывается соответствующая позиция. Расхождения при этом должны быть разнонаправленными.
//рассчитывается значение лота при покупке
double lot_sell=NormalizeDouble(0.1*orderr_size/(NormalizeDouble(((High[Max[1]]-SymbolInfoDouble(Symbol(),SYMBOL_ASK)+guard)*10000),0)+0.00001),2);
//--- рассчитывается значение лота при продаже
int index_handle=iMACD(NULL,PERIOD_CURRENT,12,26,9,PRICE_MEDIAN);
double MACD_all[];
ArraySetAsSeries(MACD_all,true);
int copied4=CopyBuffer(index_handle,0,0,bars+2,MACD_all);
double index_min1=MACD_all[min[1]];
double index_min2=MACD_all[min[2]];
//--- рассчитываются значения индикатора, соответствующие положениям экстремумов, в случае если первый экстремум - впадина
double index_Max1=MACD_all[Max[1]];
double index_Max2=MACD_all[Max[2]];
//рассчитываются значения индикатора, соответствующие положениям экстремумов, в случае если первый экстремум - вершина
bool flag_1=(min[2]<bars && min[2]!=0 && max[1]<bars && max[1]!=0 && max[2]<bars && max[2]!=0); //Проверяется условие правильного обнаружения экстремумов
bool flag_2=(Min[1]<bars && Min[1]!=0 && Max[2]<bars && Max[2]!=0 && Min[2]<bars && Min[2]!=0);
bool trend_down=(Low[min[1]]<(Low[min[2]]-trendd));
bool trend_up=(High[Max[1]]>(High[Max[2]]+trendd));
//---разница значений цен для экстремумов должна быть не менее установленной величины
openedorder=PositionSelect(Symbol()); //проверяем также условие отсутствия открытых позиций
if(min[1]<Max[1] && trend_down && flag_1 && !openedorder && (index_min1>(index_min2+macd_t)))
//в случае если первый экстремум - впадина, открывается сделка на покупку
//разница значений индикатора MACD для экстремумов не меньше значения установленного входящим параметром macd_t
// сделка открывается в случае разнонаправленного движения для цены и индикатора рассчитанных по значениям экстремумов
{
if(show_info==1) Alert("За последние",bars," баров расстояние в барах до ближ. впадины и экстремумов",min[1]," ",max[1]," ",min[2]);
//--- вывод информации об экстремумах
MqlTradeResult result={0};
MqlTradeRequest request={0};
request.action=TRADE_ACTION_DEAL;
request.magic=123456;
request.symbol=_Symbol;
request.volume=lot_buy;
request.price=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
request.sl=Low[min[1]]-guard;
request.tp=MathAbs(2*SymbolInfoDouble(Symbol(),SYMBOL_BID)-Low[min[1]])+guard;
request.type=ORDER_TYPE_BUY;
request.deviation=50;
request.type_filling=ORDER_FILLING_FOK;
OrderSend(request,result);
}
if(min[1]>Max[1] && trend_up && flag_2 && !openedorder && (index_Max1<(index_Max2-macd_t)))
//в случае если первый экстремум - вершина, открывается сделка на продажу
//разница значений индикатора MACD для экстремумов не меньше значения установленного входящим параметром macd_t
// сделка открывается в случае разнонаправленного движения для цены и индикатора рассчитанных по значениям экстремумов
{
if(show_info==1) Alert("За последние ",bars," баров расстояние в барах до ближ. пика и экстремумов",Max[1]," ",Min[1]," ",Max[2]);
//---вывод информации об экстремумах
MqlTradeResult result={0};
MqlTradeRequest request={0};
request.action=TRADE_ACTION_DEAL;
request.magic=123456;
request.symbol=_Symbol;
request.volume=lot_sell;
request.price=SymbolInfoDouble(Symbol(),SYMBOL_BID);
request.sl=High[Max[1]]+guard;
request.tp=MathAbs(High[Max[1]]-2*(High[Max[1]]-SymbolInfoDouble(Symbol(),SYMBOL_ASK)))-guard;
request.type=ORDER_TYPE_SELL;
request.deviation=50;
request.type_filling=ORDER_FILLING_FOK;
OrderSend(request,result);
}
При открытии короткой позиции стоп-лосс выставляется по положению ближайшего пика, при открытии длинной позиции — по позиции ближайшей впадины, что позволяет выставлять реалистичные цели, как при значительных колебаниях цены, так и при затишье на рынке. В обоих случаях тейк-профит выставляется симметрично стоп-лоссу относительно текущего значения цены. При внутридневной торговле выбирается небольшой размах перепада, а при долгосрочном инвестировании размах перепада лучше выбирать в несколько раз больше.
Рассмотрим работу эксперта на примере (рис. 11). Основные входные параметры, которые были использованы: размах перепада — 160 пипсов, минимальное расхождение гистограммы MACD – 0,0004; минимальное расхождение цен для 2 ближайших пиков/впадин – 120 пипсов и дополнительный коэффициент – 0.9.
Рис. 11. Результаты работы эксперта
Вначале проходил поиск последних 3 экстремумов. В момент времени, когда было принято решение об открытии длинной позиции, были найдены одна вершина и две впадины (отмечены стрелками). Эксперт, в отличие от индикатора, положение экстремумов не обозначает, но при необходимости, используя параметр show_info, и присваивая ему значение 1, мы можем получить информацию о положении экстремумов при открытии сделок.
Расхождение цен для 2 ближайших впадин составило 148 пипсов. Это больше указанного значения. Расхождение гистограммы MACD для этих же экстремумов составляет 0.00062, и эта величина тоже больше заданного значения. Учитывая разнонаправленное изменение цен и значений индикатора, найденное по 2 последним впадинам, в точке определяемой дополнительным коэффициентом (его значение 150 пипсов) была открыта длинная позиция. При использовании меньших значений дополнительного коэффициента позиция могла быть открыта ранее, соответственно, и прибыль можно было бы зафиксировать раньше.
Напоследок представляю результаты тестирования эксперта (рис. 12). В процессе тестирования установлено максимальное влияние на прибыльность параметров macd_t и trend. Чем больше значение этих параметров, тем больше количество прибыльных сделок в процентном соотношении. Но с увеличением прибыльности одновременно происходит снижение общего количества сделок.
Так, для параметров macd_t = 0,0006 и trend=160 (рис. 12), из 44 сделок 56% было прибыльным. В случае использования значений macd_t = 0,0004 и trend=120 было проведено 84 сделки, из них 51% прибыльных.
Рис. 12. Результаты тестирования эксперта
При оптимизации стратегии, в первую очередь, надо оптимально подобрать параметры macd_t и trend. И, конечно же, на параметры сделок будет влиять значение размаха перепада и дополнительного коэффициента. Размах перепада будет определять количество найденных экстремумов, а значит — и количество сделок. Дополнительный коэффициент определяет, насколько плотно размещаются тейк-профит и стоп-лосс при открытии позиций.
Эта стратегия и ряд других может работать максимально корректно только при использовании предложенных инструментов. Иначе могут возникать ситуации, когда сделка с установленными тейк-профитом и стоп-лоссом на расстоянии 200 пунктов от текущего значения цены будет открываться на основании сигналов, полученных при использовании экстремумов размером 5 пунктов и менее. Значимость таких экстремумов в данном случае крайне низка. И в этих, и во многих других ситуациях традиционные инструменты либо определяют слишком много малозначимых экстремумов, либо и вовсе не позволяют выявить пики и впадины. Также традиционным инструментам свойственны проблемы, связанные с определением экстремумов, размещенных в конце временного ряда.
Заключение
Представленные в статье алгоритмы и программные решения дают возможность однозначно находить экстремумы на графиках цен, в зависимости от ценового перепада. Полученные результаты применимы как при определении графических моделей, так и при реализации торговых стратегий, использующих графические модели и индикаторы. Разработанные инструменты имеют ряд преимуществ по сравнению с давно известными решениями. Определяются только важные экстремумы, и они подлежат обнаружению вне зависимости от ситуации на рынке, одинаково хорошо определяются как при наличии тенденции, так и в боковом движении. Находятся только те экстремумы, размер которых больше заданной величины, остальные пики и впадины игнорируются. Поиск экстремумов ведется с конца графика, что позволяет получить результаты, максимально зависящие от самых свежих ценовых колебаний. Полученные результаты слабо зависят от выбранного таймфрейма и определяются только заданным ценовым перепадом.
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Вместо заданной амплитуды между фракталами, на мой взгляд, эффективнее использовать функции Highest и Lowest
(задать условие , что эти уровни являются фракталами). Это более универсальный алгоритм, так как нет жёсткой привязки
к величине амплитуды между противоположными фракталами. Достаточно задать, что амплитуда не менее .... пунктов.