Учимся писать эксперты для MetaTrader. Урок №20
|

Учимся писать эксперты для MetaTrader. Урок №20

   Здравствуйте, Дорогие читатели. В прошлом выпуске мы писали АМА Кауфмана. Благодаря одному из читателей журнала, под ником Simca, в коде индикатора найдена ошибка. Так же им была предложна интересная схема оптимизации кода. В этом выпуске будет дан ответ на вопрос одного из читателей журнала.


   Исправление ошибки и оптимизация
  
   Принципиальная ошибка состоит в вычислении переменной signal.
   В прошлом коде переменная выглядит таким образом:
   signal=abs(c[shift]-c[shift+n-1]);
  
   Исправленный вариант выглядит так:
   signal=abs(c[shift]-c[shift+n]);


   Переменная signal считалась на 1 бар меньше положенного.


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


   Довольно не красивый момент прошлого кона состоит в том, что прошлое значение индикатора мы читаем непосредственно из массива индикатора. Этого можно легко избежать. Мы знаем, что при перезапуске индикатора (эксперта) старые значения переменных сохраняются. Поэтому проще использовать конструкцию такого вида.


   АМА (новое)= АМА (старое)+….


   Привожу часть письма, которое написал мне Уважаемый Simca.


   <… Ваш фрагмент выглядит так:
  
AMA1=c[Nbars];
For shift=Nbars Downto 0 Begin

AMA=(c[shift]*ssc*ssc)+GetIndexValue(shift+1)*(1-ssc*ssc);
if shift=Nbars then AMA=(c[shift]*ssc*ssc)+AMA1*(1-ssc*ssc);
SetIndexValue(shift,AMA);
End;


   Я бы написал то же самое вот так:


AMA=c[Nbars];
For shift=Nbars Downto 0 Begin

AMA=(c[shift]*ssc*ssc)+AMA*(1-ssc*ssc);
SetIndexValue(shift,AMA);
End;


   По моему так проще. :) Нет смысла брать предыдущее значение AMA из массива, оно и так сохранится из предыдущего цикла. А начальное значение, которое вы задаете через переменную AMA1 и проверяете на каждой итерации, можно просто до цикла изначально присвоить самой переменной AMA. Суть не изменится, а алгоритм значительно упростится. Переменная AMA1 в этом случае вообще не нужна. :) ….>


   Но на этом Уважаемый Simca не остановился, привожу цитату следующего письма.


   <… В общем, мой алгоритм индикатора после произведенных изменений выглядел следующим образом:



Inputs: Range(10), FastMA(2), SlowMA(30);
Variables: cb(0), i(0), Noise(0), ER(0), SSC(0), AMA(0);
SetLoopCount(0);
AMA=Close[Bars-Range];
for cb=Bars-Range-1 downto 0 begin
Noise=0;
for i=cb to cb+Range-1 begin
Noise=Noise+Abs(Close[i]-Close[i+1]);
end;
ER=Abs(Close[cb]-Close[cb+Range])/Noise;
SSC=ER*(2/(FastMA+1)-2/(SlowMA+1))+2/(SlowMA+1);
AMA=Close[cb]*SSC*SSC+AMA*(1-SSC*SSC);
SetIndexValue(cb,AMA);
end;


   Как видите, я не использую “количество баров на которых отображается индикатор” (Nbars) а отображаю его на всем доступном диапазоне баров. Это конечно же вопрос не принципиальный, просто мой комп. прекрасно успевает просчитать весь диапазон. :)
   Вместо shift я привык писать cb (сокращение от CurrentBar). :) Бары я считаю от (Bars-Range-1) чтобы на внутреннем цикле не выскочить за пределы количества баров. Соответственно для первого значения AMA взял цену бара перед первым рассчитываемым (Bars-Range). Переменную signal я не стал описывать отдельно а перенес непосредственно в расчет ER потому что она все равно больше нигде не используется. Все остальное мы уже обсудили. :) В общем тут и так все ясно и особо описывать нечего. :)


   Теперь собственно перейдем к совершенствованию того что есть. Начнем с расчета SSC. Невооруженным глазом видно что в нем куча рассчитываемых параметров не зависящих от переменных цикла. Логичным будет рассчитать их один раз до цикла и внутри использовать готовые значения. Я тут ничего не изобретаю, все это собственно было в статье.
   2/(SlowMA+1) – представляет собой так называемую “SlowSC” (термин из статьи).
   2/(FastMA+1) – это “FastSC”.
   Так как внутри цикла собственно “FastSC” нас не интересует, а важна только разность FastSC-SlowSC, то будем использовать два предварительно (перед основным циклом) вычисляемых коэффициента:


   k1=2/(SlowMA+1);
   k2=2/(FastMA+1)-k1;
  
   После этого расчет SSC приобретает значительно более короткий (и главное более быстрый :) вид):


   SSC=ER*k2+k1;


   Далее. При расчете AMA в классическом виде используется слишком много операций умножения (неоправданная роскошь). :) Значит преобразуем формулу к более приемлемому виду (считаться будет быстрее). Вид преобразования есть в статье, но если он не очевиден, распишу его подробнее:


   Price – цена Close[cb].
   AMA1 – предыдущее значение AMA (но мы-то помним :) что можно будет просто использовать AMA с прошлой итерации цикла).


   AMA=Price*SSC^2+AMA1*(1-SSC^2);
   раскроем скобки
   AMA=Price*SSC^2+AMA1-AMA1*SSC^2;
   переставим элементы для удобства
   AMA=AMA1+Price*SSC^2-AMA1*SSC^2;
   ну и вынесем за скобки SSC^2
   AMA=AMA1+SSC^2*(Price-AMA1)


   Теперь расчет AMA имеет вид:


   AMA=AMA+SSC*SSC*(Close[cb]-AMA);


   Вот это другое дело, :) всего два умножения и сложений не прибавилось. :)
   Этот вид формулы кстати тоже в статье был.


   Ну вот вроде бы все что можно соптимизировали. По крайней мере мне так показалось.
   Итого получили индикатор в следующем виде:



Inputs: Range(10), FastMA(2), SlowMA(30);
Variables: cb(0), i(0), k1(0), k2(0), Noise(0), ER(0), SSC(0), AMA(0);
SetLoopCount(0);
k1=2/(SlowMA+1);
k2=2/(FastMA+1)-k1;
AMA=Close[Bars-Range];
for cb=Bars-Range-1 downto 0 begin
Noise=0;
for i=cb to cb+Range-1 begin
Noise=Noise+Abs(Close[i]-Close[i+1]);
end;
ER=Abs(Close[cb]-Close[cb+Range])/Noise;
SSC=ER*k2+k1;
AMA=AMA+SSC*SSC*(Close[cb]-AMA);
SetIndexValue(cb,AMA);
end;


   Для того чтобы окончательно убедиться что мы все сделали правильно (ведь мы только оптимизировали алгоритм не изменяя его сути) запускаем на графике оба индикатора одновременно. Видим полное стопроцентное совпадение кривых. Значит оптимизация корректна. :) Но работать второй вариант будет быстрее. …>


   Достойно подражания!


   Я хочу повторить один из главных моментов оптимизации. АМА (новое)= АМА (старое)+…. Он позволяет не считывать прошлое значение индикатора из массива. Т.к. индикаторов использующих в своей формуле прошлое значение самого себя не мало, всем было бы полезно взять на вооружение этот, простой и удобный, прием.


   Ответ на вопрос


   Уважаемый Юрий спрашивает.
   <… Как сделать чтобы советник закрывал позицию ( если таковая имееться) по данному инструменту в момент закрытия бара и входил в рынок в момент формирования следующего нового бара. …>


   МТ не <понимает>, что вот-вот бар закроется. Но МТ знает точно время. Из этого и будем исходить.


   Вот часть кода, которая закрывает позицию перед закрытием бара Н1. По аналогии для открытия позиции, я думаю, Вы сможете сделать сами.


if Minute=59 then {
if TotalTrades>0 then
{
for cnt=1 to TotalTrades
{
if OrderValue(cnt,VAL_TYPE)<=OP_SELL and
OrderValue(cnt,VAL_SYMBOL)=Symbol then
{
If OrderValue(cnt,VAL_TYPE)=OP_BUY then
{
CloseOrder(OrderValue(cnt,VAL_TICKET),OrderValue(cnt,VAL_LOTS),Bid,3,Violet);
exit;
};
If OrderValue(cnt,VAL_TYPE)=OP_SELL then
{
CloseOrder(OrderValue(cnt,VAL_TICKET),OrderValue(cnt,VAL_LOTS),ask,3,Violet);
Exit;
};
};
};};
};


   Вот начальное значение для эксперта на D1 : if Hour=23 then {


   Заключение
   Еще раз хочу выразить глубокую благодарность, читателю под ником Simca.
   Уверен, что всем было бы интересно посмотреть на чужие работы, дабы не вариться в собственном соку. Если у кого-то из Вас появилось желание поделиться мыслями, обязательно пишите.


Халхальян Артур
artur@fxtest.ru