Примеры: Многократный пересчет нулевого бара в некоторых индикаторах

 

New article Многократный пересчет нулевого бара в некоторых индикаторах has been published:

Статья посвящена проблеме пересчета значения индикатора в клиентском терминале MetaTrader 4 при изменении нулевого бара. В ней излагается общая идея добавления в код индикатора дополнительных программных элементов, позволяющих восстанавливать состояние програмного кода, сохраненное до многократного пересчета.

В данной статье мне бы хотелось коснуться проблемы многократного пересчета значения индикатора при изменении нулевого бара в клиентском терминале MetaTrader 4. Суть ее заключается в том, что в интернете развелось огромное множество зачастую достаточно толковых индикаторов, которые абсолютно нормально работают только на исторических данных, а при подключении торговой платформы к серверу они выдают результат, мягко говоря, никакого отношения не имеющий к заложенным в эти индикаторы алгоритмам!

Обычно авторы этих индикаторов, изрядно поломав голову, в конце-концов находят самый простой и далеко не самый умный способ решения проблемы: они просто заставляют свои индикаторы только для вычисления значения на одном последнем, нулевом баре при каждом запуске функции int start() пересчитывать значение индикатора для всех баров графика заново!

Кажется на первый взгляд, что все проблемы при таком решении могут запросто исчезнуть, но на самом деле проблемы эти просто перемещаются в область избыточного поглощения компьютерных ресурсов подобным индикатором, которое при определенном количестве некорректно работающих индикаторов может просто сильно тормозить и подвешивать компьютер!

Author: Nikolay Kositsin

 
Я так понимаю, Николай Косицын - это Godzilla на форуме Альпари? Как ваши успехи с юриковскими индикаторами? Если не секрет.
 

Вы бы подсказали еще что надо делать чтобы линии индикаторов в самом начале графиков не начинали рисоваться как попало (откуданибудь снизу или сверху окна). А также когда индикатор рисуется символами (DRAW_ARROW) то при сдвиге графика в самом начале влево (добавлении новых баров) старые символы почемуто остаются и захламляют картинку. Это все на торговлю конечно не повлияет, больше для ясности и эстетики.

 
Mihause! Увы! Эти вещи требуют написания ещё одной статьи, на которую у меня пока абсолютно нет времени!

Rosh! Я ещё улучшил эти индикаторы, малость переписал код JJMASeries и JurXSeries и заставил эти функции абсолютно нориально работать в экспертах, сделал ещё несколько аналогичных функций под названиями T3Series, ParMASeries, LRMASeries, но так как я сейчас делаю эксперта для чемпионата, то времени для нормального редактирования всего этого, разрастающегося, как снежный ком добра нету абсолютно!!! Теперь только в октябре время появится! Да оно и как-то не совсем умно выкладывать экспертные варианты этих функций до начала чемпионата! Это только самому себе вредить!
 
Mihause:

Вы бы подсказали еще что надо делать чтобы линии индикаторов в самом начале графиков не начинали рисоваться как попало (откуданибудь снизу или сверху окна). А также когда индикатор рисуется символами (DRAW_ARROW) то при сдвиге графика в самом начале влево (добавлении новых баров) старые символы почемуто остаются и захламляют картинку. Это все на торговлю конечно не повлияет, больше для ясности и эстетики.

При прокрутке истории назад закачивается отсутствовавшая история. Для этой части истории расчет индикатора вообще не был проведен.
Чтобы не выводился мусор, о котором Вы пишите, необходимо:
1) Прописать в индикаторные буферы 0 - ноль
2) рассчитать индикатор с самого начала.
Пример из моего индикатора:

if (timeFirstBar==Time[Bars-1])
{
cbi=Bars-IndicatorCounted()-1;
}
else
{
cbi=Bars-1;
for (int iBar=cbi; iBar>0; iBar--) {zzL[iBar]=0.0; zzH[iBar]=0.0; zz[iBar]=0. 0;}
timeFirstBar=Time[cbi];
}

Далее идет код индикатора.

timeFirstBar - время самого первого бара (Bars-1)
cbi - количество непосчитанных баров или номер бара, с которого производится пересчет индикатора

Также это поможет при переходе между реальным и демо счетами. Но частично.
 
К сожалению, стандартный - встроенный в метатрейдер ZigZag - имеет следующий недостаток.
При расчете используются два буфера. Один для найденных нижних перегибов (минимумов), другой для верхних перегибов (максимумов).
Бывают ситуации, когда на одном баре находится и минимум и максимум. В частности, на внешнем баре.
Почему-то автор - тот, кто первый написал этот индикатор - решил, что для построения ZigZag важны именно максимумы. И при нахождении минимума и максимума на одном баре выбирается только максимум.
Вот кусочек кода, где это происходит:

//----+ начало третьего цикла
for(shift=limit; shift>=0; shift--)
{
res=TempBuffer[shift];
if(res!=0.0) ZigZagBuffer[shift]=res;
}
//----+ конец третьего цикла

Горбы как раз и возникают, когда был найден минимум и максимум на одном баре.
Такое вот волюнтаристское решение. Не буду здесь рассуждать, как это проявляется в реальной рыночной ситуации. Но, считаю, что это ошибочное решение.
 
nen:
Mihause:

Вы бы подсказали еще что надо делать чтобы линии индикаторов в самом начале графиков не начинали рисоваться как попало (откуданибудь снизу или сверху окна). А также когда индикатор рисуется символами (DRAW_ARROW) то при сдвиге графика в самом начале влево (добавлении новых баров) старые символы почемуто остаются и захламляют картинку. Это все на торговлю конечно не повлияет, больше для ясности и эстетики.

При прокрутке истории назад закачивается отсутствовавшая история. Для этой части истории расчет индикатора вообще не был проведен.
Чтобы не выводился мусор, о котором Вы пишите, необходимо:
1) Прописать в индикаторные буферы 0 - ноль
2) рассчитать индикатор с самого начала.
Пример из моего индикатора:

if (timeFirstBar==Time[Bars-1])
{
cbi=Bars-IndicatorCounted()-1;
}
else
{
cbi=Bars-1;
for (int iBar=cbi; iBar>0; iBar--) {zzL[iBar]=0.0; zzH[iBar]=0.0; zz[iBar]=0. 0;}
timeFirstBar=Time[cbi];
}

Далее идет код индикатора.

timeFirstBar - время самого первого бара (Bars-1)
cbi - количество непосчитанных баров или номер бара, с которого производится пересчет индикатора

Также это поможет при переходе между реальным и демо счетами. Но частично.
Ясно. Благодарю за совет.
 

ZigZag, поставляемый с метатрейдером, к сожалению, имеет еще одну существенную ошибку. Очень часто он рисует переломы в "воздухе". То есть его переломы не касаются экстремумов баров.
Ниже привожу код оптимизированного (в статье) ZigZag с устраненной ошибкой "переломы в воздухе".

//+------------------------------------------------------------------+
//| ZigZag. mq4 |
//| Copyright © 2005, MetaQuotes Software Corp. |
//| https://www.metaquotes.net/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2005, MetaQuotes Software Corp."
#property link "https://www.metaquotes.net/"
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_color1 Red
#property indicator_width1 0
#property indicator_style1 1
//----
extern int ExtDepth=12;
extern int ExtDeviation=5;
extern int ExtBackstep=3;
//----
//----
double ZigZagBuffer[];
//+------------------------------------------------------------------+
//| ZigZag initialization function |
//+------------------------------------------------------------------+
int init()
{
//----
SetIndexBuffer(0,ZigZagBuffer);
SetIndexStyle(0,DRAW_SECTION);
SetIndexEmptyValue(0,0.0);
IndicatorShortName("ZigZag("+ExtDepth+","+ExtDeviation+", "+ExtBackstep+")");
//----
return(0);
}
//+------------------------------------------------------------------+
//| ZigZag iteration function |
//+------------------------------------------------------------------+
int start()
{
//----+ проверка количества баров на достаточность для корректного расчёта индикатора
if (Bars-1<ExtDepth)return(0);
//----+ Введение целых переменных памяти для пересчёта индикатора только на неподсчитанных барах
static int time2,time3,time4;
//----+ Введение переменных с плавающей точкой для пересчёта индикатора только на неподсчитанных барах
static double ZigZag2,ZigZag3,ZigZag4;
//----+ Введение целых переменных для пересчёта индикатора только на неподсчитанных барах и получение уже подсчитанных баров
int MaxBar,limit,supr2_bar,supr3_bar,supr4_bar,counted_bars=IndicatorCounted();
//---- проверка на возможные ошибки
if (counted_bars<0)return(-1);
//---- последний подсчитанный бар должен быть пересчитан
if (counted_bars>0) counted_bars--;
//----+ Введение переменных
int shift, back,lasthighpos,lastlowpos;
double val,res,TempBuffer[1];
double curlow,curhigh,lasthigh,lastlow;
//---- определение номера самого старого бара, начиная с которого будет произедён полый пересчёт всех баров
MaxBar=Bars-ExtDepth;
//---- определение номера стартового бара в цикле, начиная с которого будет произедиться пересчёт новых баров
if (counted_bars==0)limit=MaxBar;
else
{
//----
supr2_bar=iBarShift(NULL,0,time2,TRUE);
supr3_bar=iBarShift(NULL,0,time3,TRUE);
supr4_bar=iBarShift(NULL,0,time4,TRUE);
//----
limit=supr3_bar;
if ((supr2_bar<0)||(supr3_bar<0)||(supr4_bar<0))
{
limit=MaxBar;
Print("Стартовый бар не найден, буден произведён пересчёт индикатора на всех барах" );
}
}

//---- инициализация нуля
if (limit>=MaxBar)
{
for (shift=Bars-1; shift>=MaxBar;shift--)ZigZagBuffer [shift]=0. 0;
limit=MaxBar;
}
//----
//---- изменение размера временного буфера
if(ArrayResize(TempBuffer,limit+ExtBackstep+1)!=limit+ExtBackstep+1)return(-1);

//----+-------------------------------------------------+

//----+ начало первого большого цикла
for(shift=limit; shift>=0; shift--)
{
//---
val=Low[Lowest(NULL,0,MODE_LOW,ExtDepth,shift)];
if(val==lastlow) val=0.0;
else
{
lastlow=val;
if((Low[shift]-val)>(ExtDeviation*Point)) val=0. 0;
else
{
for(back=1; back<=ExtBackstep; back++)
{
res=ZigZagBuffer[shift+back];
if((res!=0)&&(res>val)) ZigZagBuffer[shift+back]=0. 0;
}
}
}
if (Low[shift]==val) ZigZagBuffer[shift]=val;
//---
val=High[Highest(NULL,0,MODE_HIGH,ExtDepth,shift)];
if(val==lasthigh) val=0.0;
else
{
lasthigh=val;
if((val-High[shift])>(ExtDeviation*Point)) val=0. 0;
else
{
for(back=1; back<=ExtBackstep; back++)
{
res=TempBuffer[shift+back];
if((res!=0)&&(res<val)) TempBuffer[shift+back]=0. 0;
}
}
}
if (High[shift]==val) TempBuffer[shift]=val;
}
//----+ конец первого большого цикла

// final cutting
lasthigh=-1; lasthighpos=-1;
lastlow= -1; lastlowpos= -1;
//----+-------------------------------------------------+

//----+ начало второго большого цикла

for(shift=limit; shift>=0; shift--)
{
curlow=ZigZagBuffer[shift];
curhigh=TempBuffer[shift];
if((curlow==0)&&(curhigh==0)) continue;
//---
if(curhigh!=0)
{
if(lasthigh>0)
{
if(lasthigh<curhigh) TempBuffer[lasthighpos]=0;
else TempBuffer[shift]=0;
}
//---
if(lasthigh<curhigh || lasthigh<0)
{
lasthigh=curhigh;
lasthighpos=shift;
}
lastlow=-1;
}
//----
if(curlow!=0)
{
if(lastlow>0)
{
if(lastlow>curlow) ZigZagBuffer[lastlowpos]=0;
else ZigZagBuffer[shift]=0;
}
//---
if((curlow<lastlow)||(lastlow<0))
{
lastlow=curlow;
lastlowpos=shift;
}
lasthigh=-1;
}
}
//----+ конец второго большого цикла

//----+-------------------------------------------------+

//----+ начало третьего цикла
for(shift=limit; shift>=0; shift--)
{
res=TempBuffer[shift];
if(res!=0.0) ZigZagBuffer[shift]=res;
}
//----+ конец третьего цикла

//+--- Восстановление значений индикаторного буффера, которые могли быть утеряны
if (limit<MaxBar)
{
ZigZagBuffer[supr2_bar]=ZigZag2;
ZigZagBuffer[supr3_bar]=ZigZag3;
ZigZagBuffer[supr4_bar]=ZigZag4;
for(int qqq=supr4_bar-1; qqq>supr3_bar; qqq--)ZigZagBuffer[qqq]=0;
for(int ggg=supr3_bar-1; ggg>supr2_bar; ggg--)ZigZagBuffer[ggg]=0;
}
//+---+============================================+

//+--- исправление возникающих горбов
double vel1, vel2, vel3, vel4;
int bar1, bar2, bar3, bar4;
int count;
if (limit==MaxBar)supr4_bar=MaxBar;
for(int bar=supr4_bar; bar>=0; bar--)
{
if (ZigZagBuffer[bar]!=0)
{
count++;
vel4=vel3;bar4=bar3;
vel3=vel2;bar3=bar2;
vel2=vel1;bar2=bar1;
vel1=ZigZagBuffer[bar];bar1=bar;
if (count<3)continue;
if ((vel3<vel2)&&(vel2<vel1))ZigZagBuffer[bar2]=0;
if ((vel3>vel2)&&(vel2>vel1))ZigZagBuffer[bar2]=0;
if ((vel2==vel1)&&(vel1!=0 ))ZigZagBuffer[bar1]=0;
}
}
//+--- запоминание времени трёх последних перегибов Зигзага и значений индикатора в этих точках
time2=Time[bar2];
time3=Time[bar3];
time4=Time[bar4];
ZigZag2=vel2;
ZigZag3=vel3;
ZigZag4=vel4;
//+---
//---- завершение вычислений значений индикатора
return(0);
}
//---+ +---------------------------------------------------------------------+

 

ZigZag с устраненными ошибками. Алгоритм устранения горбов проще, чем предложенный автором статьи. И отсутствует прорисовка перегибов в воздухе. Но в этом коде нет оптимизации.

//+------------------------------------------------------------------+
//| Custom Moving Average. mq4 |
//| Copyright © 2005, MetaQuotes Software Corp. |
//| https://www.metaquotes.net/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2005, MetaQuotes Software Corp."
#property link "https://www.metaquotes.net/"

#property indicator_chart_window
#property indicator_buffers 1
#property indicator_color1 Red
//---- indicator parameters
extern int ExtDepth=12;
extern int ExtDeviation=5;
extern int ExtBackstep=3;
//---- indicator buffers
double ExtMapBuffer[];
double ExtMapBuffer2[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int init()
{
IndicatorBuffers(2);
//---- drawing settings
SetIndexStyle(0,DRAW_SECTION);
//---- indicator buffers mapping
SetIndexBuffer(0,ExtMapBuffer);
SetIndexBuffer(1,ExtMapBuffer2);
SetIndexEmptyValue(0,0.0);
SetIndexEmptyValue(1,0.0);
// ArraySetAsSeries(ExtMapBuffer,true);
// ArraySetAsSeries(ExtMapBuffer2,true);
//---- indicator short name
IndicatorShortName("ZigZag("+ExtDepth+","+ExtDeviation+", "+ExtBackstep+")");
//---- initialization done
return(0);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
int start()
{
int shift, back,lasthighpos,lastlowpos;
double val,res;
double curlow,curhigh,lasthigh,lastlow;

for(shift=Bars-ExtDepth; shift>=0; shift--)
{
val=Low[Lowest(NULL,0,MODE_LOW,ExtDepth,shift)];
if(val==lastlow) val=0.0;
else
{
lastlow=val;
if((Low[shift]-val)>(ExtDeviation*Point)) val=0. 0;
else
{
for(back=1; back<=ExtBackstep; back++)
{
res=ExtMapBuffer[shift+back];
if((res!=0)&&(res>val)) ExtMapBuffer[shift+back]=0. 0;
}
}
}
if (Low[shift]==val) ExtMapBuffer[shift]=val;
//--- high
val=High[Highest(NULL,0,MODE_HIGH,ExtDepth,shift)];
if(val==lasthigh) val=0.0;
else
{
lasthigh=val;
if((val-High[shift])>(ExtDeviation*Point)) val=0. 0;
else
{
for(back=1; back<=ExtBackstep; back++)
{
res=ExtMapBuffer2[shift+back];
if((res!=0)&&(res<val)) ExtMapBuffer2[shift+back]=0. 0;
}
}
}
if (High[shift]==val) ExtMapBuffer2[shift]=val;
}

// final cutting
lasthigh=-1; lasthighpos=-1;
lastlow=-1; lastlowpos=-1;

for(shift=Bars-ExtDepth; shift>=0; shift--)
{
curlow=ExtMapBuffer[shift];
curhigh=ExtMapBuffer2[shift];
if((curlow==0)&&(curhigh==0)) continue;
//---
if(curhigh!=0)
{
if(lasthigh>0)
{
if(lasthigh<curhigh) ExtMapBuffer2[lasthighpos]=0;
else ExtMapBuffer2[shift]=0;
}
//---
if(lasthigh<curhigh || lasthigh<0)
{
lasthigh=curhigh;
lasthighpos=shift;
}
lastlow=-1;
}
//----
if(curlow!=0)
{
if(lastlow>0)
{
if(lastlow>curlow) ExtMapBuffer[lastlowpos]=0;
else ExtMapBuffer[shift]=0;
}
//---
if((curlow<lastlow)||(lastlow<0))
{
lastlow=curlow;
lastlowpos=shift;
}
lasthigh=-1;
}
}

for(shift=Bars-1; shift>=0; shift--)
{
if(shift>=Bars-ExtDepth) ExtMapBuffer[shift]=0. 0;
else
{
res=ExtMapBuffer2[shift];
if(res!=0.0)
{
if (ExtMapBuffer[shift]!=0. 0) ExtMapBuffer[shift]=0. 0; else ExtMapBuffer[shift]=res;
}
}
}
}

 

И опять про ZigZag.
В той части, где исправление возникающих горбов последнюю строчку кода в цикле проверки на гобы
надо заменить на: if ((vel2==vel1)&&(vel1!=0 )) {ExtMapBuffer[bar1]=0;bar=bar3+1;}
Иначе, не все горбы устраняются

Причина обращения: