Вопрос По Тестеру стратегий в MT4. Можно ли менять период тестера, если в коде период прописан жестко

 

 Я прошу прощения, если я не внимательно прочел справку, однако, не нашел.

При тестировании данного кода, тестер стратегий(указываем период Daily) открывает по валютной паре  сделки на Buy( 6 штук за  указанный временной  период) и Sell( 13  штук за указанный временной период) согласно условиям кода. Т.е. всё "чики пуки"

Переменная  PeriodFirst = 1440. Следовательно, по моей(мб не верной) логике, при переключении в тестере период на 5M всё переламывается и умирает. За тот же указанный временной  период открываются ТОЛЬКО сделки типа Buy ( 7  штук. не 6. О_о ). Сделки типа Sell не открываются совсем. 

Ошибка ли это тестера? и если да, то можно ли рассчитывать на успешную работу советника, установленного на 5 минутный график? 

double  RazKus = (iHigh(Symbol(),PeriodFirst,1) - iLow(Symbol(),PeriodFirst,1))/DelSvFirst;
//-- Поиск точек входа. Buy
if(iOpen(Symbol(),PeriodFirst,2)>iClose(Symbol(),PeriodFirst,2)) 
  {
   if((iHigh(Symbol(),PeriodFirst,1)-RazKus<iOpen(Symbol(),PeriodFirst,1)) && (iHigh(Symbol(),PeriodFirst,1)-RazKus<iClose(Symbol(),PeriodFirst,1)))
     {
      //--Точка входа найдена. Перед открытием ордера проверяем, стоит ли у нас динамика на ТП
      double TP=Ask+TakeProfitFirst*Point; if(DinTkPrFirst==True) { TP=Ask+(iHigh(Symbol(),PeriodFirst,1)-iLow(Symbol(),PeriodFirst,1))*ForTheDinamikMn; }
      double tiket=OrderSend(Symbol(),OP_BUY,Lots,Ask,1,iLow(Symbol(),PeriodFirst,1),TP,IntegerToString(MagicNumber1,0),MagicNumber1,0,Blue);
      FileWriteMPM(MagicNumber1,PeriodFirst,1);
     }
  }
//-- Поиск точек входа. Sell
if(iOpen(Symbol(),PeriodFirst,2)<iClose(Symbol(),PeriodFirst,2))
  {
   if((iLow(Symbol(),PeriodFirst,1)+RazKus>iOpen(Symbol(),PeriodFirst,1)) && (iLow(Symbol(),PeriodFirst,1)+RazKus>iClose(Symbol(),PeriodFirst,1)))
     {
      //--Точка входа найдена. Перед открытием ордера проверяем, стоит ли у нас динамика на ТП
      double TP=Bid-TakeProfitFirst*Point; if(DinTkPrFirst==True) { TP=Bid -(iHigh(Symbol(),PeriodFirst,1)-iLow(Symbol(),PeriodFirst,1))*ForTheDinamikMn; }
      double tiket=OrderSend(Symbol(),OP_SELL,Lots,Bid,1,iHigh(Symbol(),PeriodFirst,1),TP,IntegerToString(MagicNumber1,0),MagicNumber1,0,Red);
      FileWriteMPM(MagicNumber1,PeriodFirst,1);
     }
  }

Прошу прощения. Благодарю за правку.

Еще забыл указать - Журнал никаких ошибок не показывает. При визуализации моменты для Sell просто проходятся мимо, как будто "они не подходят под условия"

 

 
Код нужно вставлять так: Правильно вставляем код на форуме
 
NaKaZ:

 Я прошу прощения, если я не внимательно прочел справку, однако, не нашел.

При тестировании данного кода, тестер стратегий(указываем период Daily) открывает по валютной паре  сделки на Buy( 6 штук за  указанный временной  период) и Sell( 13  штук за указанный временной период) согласно условиям кода. Т.е. всё "чики пуки"

Переменная  PeriodFirst = 1440. Следовательно, по моей(мб не верной) логике, при переключении в тестере период на 5M всё переламывается и умирает. За тот же указанный временной  период открываются ТОЛЬКО сделки типа Buy ( 7  штук. не 6. О_о ). Сделки типа Sell не открываются совсем. 

Ошибка ли это тестера? и если да, то можно ли рассчитывать на успешную работу советника, установленного на 5 минутный график? 

Прошу прощения. Благодарю за правку.

Еще забыл указать - Журнал никаких ошибок не показывает. При визуализации моменты для Sell просто проходятся мимо, как будто "они не подходят под условия"

 

Возможно нет истории. Попробуйте подгрузить. Код не смотрел.
 
Tapochun:
Возможно нет истории. Попробуйте подгрузить. Код не смотрел.


Я не совсем понимаю, что нужно сделать и "нужно ли".

Попробую описать последовательно

Включаю тестер - выставляют даты(исследуемый период по валютной паре. Например: с 1.08.14 до 1.04.15) - выставляю период графика Daily - жму старт

Тест проходит, смотрю отчет, результаты(сделки и Buy и Sell), открываю график и наблюдаю желаемый результат - сделки открыты там где это действительно нужно было согласно условиям в коде

НИЧЕГО НЕ МЕНЯЮ, кроме периода графика с Daily на M5 - результат : отчет хлам, в закладке "результаты" - сделки только Buy.

На скрине

Первая часть Daily. есть Buy и Sell

Вторая часть - переключение на M5. Sell отсутствуют совсем

 

Файлы:
6luoi.jpg  122 kb
 
NaKaZ:

 Я прошу прощения, если я не внимательно прочел справку, однако, не нашел.

При тестировании данного кода, тестер стратегий(указываем период Daily) открывает по валютной паре  сделки на Buy( 6 штук за  указанный временной  период) и Sell( 13  штук за указанный временной период) согласно условиям кода. Т.е. всё "чики пуки"

Переменная  PeriodFirst = 1440. Следовательно, по моей(мб не верной) логике, при переключении в тестере период на 5M всё переламывается и умирает. За тот же указанный временной  период открываются ТОЛЬКО сделки типа Buy ( 7  штук. не 6. О_о ). Сделки типа Sell не открываются совсем. 

Ошибка ли это тестера? и если да, то можно ли рассчитывать на успешную работу советника, установленного на 5 минутный график? 

Прошу прощения. Благодарю за правку.

Еще забыл указать - Журнал никаких ошибок не показывает. При визуализации моменты для Sell просто проходятся мимо, как будто "они не подходят под условия"

Поскольку при написании кода оптимальнее всего - это осознанное понимание работы его строк, то порекомендую следующее:

1. Когда проводите в коде с числами типа double  какие-то математические вычисления (сложение, вычитание, умножение, деление, сравнения (if(...))), то рекомендую применять нормализацию. Без неё возможны погрешности и, соответственно, может быть некорректный вывод результатов и/или сравнений.

Здесь вот есть хорошая статья про особенности чисел double

2. При знакомстве с работой функций (или их написании, корректировке), мне, например, хорошо помогает создание тестовых скриптов с выводом результата.

Проецируя это на ваш вопрос, порекомендую вам просто посмотреть как работает на разных таймфреймах, к примеру, тот же iHigh.

Ниже код тестового скрипта. Вы его опробуйте на разных таймфреймах:

void OnStart()
  {
   int i=1;

   double h1440=iHigh(NULL,1440,i);
   Print("Бар #",i,"; 1440: ",DoubleToString(h1440,_Digits));

   double hD1=iHigh(NULL,PERIOD_D1,i);
   Print("Бар #",i,"; PERIOD_D1: ",DoubleToString(hD1,_Digits));
  }

3. P./S.: Удалила первоначальное содержание своего третьего пункта, поскольку ошибочно посчитала повторами в строках сравнения.

Однако, напишу, что с моей точки зрения, по возможности, лучше функции и вычисления не применять в сравнениях (то бишь, вывести результат работы функций/вычислений до сравнения в переменную/ые и уже затем сравнивать в if полученные переменные.
/*Ну и при знакомстве со статьёй выше, про особенности чисел double, обратите внимание на сравнения чисел double.*/

4. Вам нужно прописать в коде ещё и проверки на ошибки. К примеру, посмотрите описание функции OrderSend и/или, по поиску на сайте, как другие делают проверки на ошибки.

Если кратко, то всё вышеописанное - это первое, на что обратила внимание, когда увидела ваш код.

 

P./S.:

5. С учётом последнего вашего поста порекомендую вам ещё почитать статьи про тестер стратегий, перечисленные в Справке к торговому терминалу (вызывается клавишей F1).

Эти статьи (их немного и они не большие) там по следующему пути: Клиентский терминал / Статьи / Тестер 

P./S.:

Подкорректировала третий пункт.

 
NaKaZ:

Всё-таки добавлю третий постскриптум:

Если что, то мои советы посмотреть статьи (да и в целом посты) - не являются проявлением какого-либо высокомерия и тому подобного негатива.

Тем более, что обращаясь, вы обращались вежливо и не давали повода для какого-то негатива пока.

Решила это сказать, поскольку некоторые люди, бывает, не понимают, что о некоторых вещах или не сказать в двух словах (будут упущены какие-то моменты; при "сжатой" подаче инфы, по незнанию важных для применения нюансов, может вольно/невольно сформироваться в уме не всегда применимый шаблон и т.д.) и/или требуется пересказ уже имеющегося (а это время для написания; многократный повтор одного и того же; да и можно при пересказе что-то существенное упустить/исказить, в том числе невольно). Как-то так, если кратко и без учёта других нюансов.

В любом случае - успеха вам!
 
DiPach:

Всё-таки добавлю третий постскриптум:

Если что, то мои советы посмотреть статьи (да и в целом посты) - не являются проявлением какого-либо высокомерия и тому подобного негатива.

Тем более, что обращаясь, вы обращались вежливо и не давали повода для какого-то негатива пока.

Решила это сказать, поскольку некоторые люди, бывает, не понимают, что о некоторых вещах или не сказать в двух словах (будут упущены какие-то моменты; при "сжатой" подаче инфы, по незнанию важных для применения нюансов, может вольно/невольно сформироваться в уме не всегда применимый шаблон и т.д.) и/или требуется пересказ уже имеющегося (а это время для написания; многократный повтор одного и того же; да и можно при пересказе что-то существенное упустить/исказить, в том числе невольно). Как-то так, если кратко и без учёта других нюансов.

В любом случае - успеха вам!

:) на данный момент читаю Клиентский терминал / Статьи / Тестер

Сразу же после вашего поста, я поправил код. 

 

double  RazKus = (iHigh(Symbol(),PeriodFirst,1) - iLow(Symbol(),PeriodFirst,1))/DelSvFirst;
   RazKus = NormalizeDouble(RazKus,_Digits);

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

Пункт №2 я опробовал - на всех ТФ в торговом терминале скрип вывел по обеим строчкам одно и тоже значение. Скрип выбрасывал на ТФ пары GBP/USD. Везде показали:

2015.04.13 21:05:36.464 qq GBPUSD,Daily: Áàð #1; 1440: 1.47234

 2015.04.13 21:05:36.464 qq GBPUSD,Daily: Áàð #1; PERIOD_D1: 1.47234

Пункт №3 - успел углядеть до удаления, было состояние О_о, О_О  :-)

По совету выводить результаты функций в переменные, а затем переменные использовать в условиях:

Изначально код был больше раза в 2. В строчках описывались переменные и присваивались значения из iOpen,iClose  и т.д.

Изначально я писал код так(и пока вопрос не решиться, я оставлю его таким, как советуете вы)

 double HG,OP,CL,LW,OP2,CL2;
   HG = iHigh(Symbol(),PeriodFirst,1); OP = iOpen(Symbol(),PeriodFirst,1); CL = iClose(Symbol(),PeriodFirst,1);LW = iLow(Symbol(),PeriodFirst,1);
   OP2 = iOpen(Symbol(),PeriodFirst,2); CL2 = iClose(Symbol(),PeriodFirst,2);
   double  RazKus = (HG - LW)/DelSvFirst;
   RazKus = NormalizeDouble(RazKus,_Digits);
   //-- Поиск точек входа. Buy
   if ( OP2 > CL2 ) {
      if ( (HG - RazKus < OP) && (HG - RazKus < CL) ){
      //--Точка входа найдена. Перед открытием ордера проверяем, стоит ли у нас динамика на ТП
      double TP = Ask + TakeProfitFirst*Point; if ( DinTkPrFirst == True ) { TP = Ask + (HG - LW)*ForTheDinamikMn; }
      double ticket = OrderSend(Symbol(), OP_BUY,  Lots, Ask, 1, LW, TP, IntegerToString(MagicNumber1,0), MagicNumber1, 0, Blue);
      if(ticket<0) {  Print("OrderSend завершилась с ошибкой #",GetLastError());  }  else  Print("Функция OrderSend успешно выполнена");
      FileWriteSvechi(MagicNumber1,PeriodFirst,1);
      }
   }
   //-- Поиск точек входа. Sell
   if ( OP2 < CL2 ){
      if ( (LW + RazKus > OP) && (LW + RazKus > CL) ){
      //--Точка входа найдена. Перед открытием ордера проверяем, стоит ли у нас динамика на ТП
      double TP = Bid - TakeProfitFirst*Point; if ( DinTkPrFirst == True ) { TP = Bid - (HG - LW)*ForTheDinamikMn; }
      double ticket = OrderSend(Symbol(), OP_SELL, Lots, Bid, 1, HG, TP, IntegerToString(MagicNumber1,0), MagicNumber1, 0, Red);
      if(ticket<0) {    Print("OrderSend завершилась с ошибкой #",GetLastError());    } else   Print("Функция OrderSend успешно выполнена");
      FileWriteSvechi(MagicNumber1,PeriodFirst,1); 
      }
   }

 Пункт №4 - При старте теста - в журнале, при появлении ошибок - выводятся сообщения. Наиболее частые ошибки - а они у меня появлялись там очень часто :( я уже даже выучил, так часто я их вижу. Обработку в код не добавляю - лентяй наверн

Пункт №5 - Читаю, но ответ на вопрос не нахожу.

П.С. iHigh(и т.п.) - разве может получить не нормализованное значение ? О_о Собственно, поэтому я и не делал нормализацию. Она же в конкретном случае - лишняя нагрузка.(Не верная логика?)

 

У меня нету опыта в написании программ, да и знаний горсть. И может я много даже из ваших советов не понял - куда именно вы меня вели и что я по прежнему упускаю.

НО - скрипт я проверил в торговом терминале и на каждом из ТФ он показал одно и то же значение.

Посему остается вопрос -

 

 

 

---------------------------------------Советник отработал именно так, как это нужно. Как этого желалось. Переключаем на М5 и сделки SELL тютю...

 

 

 

Простите за много букв и картинок. 

И прошу прощения, если я что то из советов упустил, что помогло бы решить проблему. "Пните" в нужную сторону мой еле работающий разум :) 

 

 
NaKaZ:

По вашим смайлам вживую представила ваши глаза по удалённому третьему пункту. ))) Sorry.

По нормализации же вам надо ещё раз посмотреть статью. )

Не торопитесь. При написании кода часто верна пословица: "чем тише едешь, тем дальше будешь".)


Теперь по вашему посту... Хотя не совсем в том порядке, что вы написали, но попробую сформулировать ответ. Cмотря на ваш код - мне так проще будет как-то относительно кратко сейчас ответить. И что-то можете даже посчитать "отходом от темы" на первый взгляд. Нет. Просто то, что писала выше, касалась того, что бросилось сразу в глаза. Сейчас же попутно добавлю ещё кое-что, тем более, что оно "перекликается" с ранее сказанным.

1. Вы берёте данные с первой и второй дневных свеч. И чтобы переменные/функции не считались на каждом тике, думаю, часть из них вам нужно прописать отдельным блоком ("редкие расчёты" по истечении какого-то периода (временного интервала)).

2. Далее, вместо:

   double  RazKus = (HG - LW)/DelSvFirst;
   RazKus = NormalizeDouble(RazKus,_Digits);

полагаю, можно прописать так:

double  RazKus = NormalizeDouble((HG - LW)/DelSvFirst,_Digits);

Но этот совет, "грубый" в плане расчётной корректности, поскольку не знаю, что за переменная DelSvFirst. Вполне допускаю, что могут требоваться какие-то дополнительные прописывания/проверки. К примеру, чтобы избежать деления на ноль.

/* Позже "увидела", что DelSvFirst равно 3. И, видимо устав к тому времени, даже зачеркнула фразу выше.

Затем вновь убрала зачеркивание, поскольку проверку на ноль и предусмотреть не отрицательное значение, если этого ранее нет в коде - не помешает организовать для DelSvFirst.*/


Этот RazKus - в редко срабатываемую часть кода (ту часть, что будет обрабатываться по истечении какого-то периода, а не на каждом тике).

По остальным переменным (и их необходимости) советую смотреть исходя из логики частоты срабатываний.

К примеру:

HG = iHigh(Symbol(),PeriodFirst,1);

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


3. По нормализации - она применяется при математических расчётах с участием чисел double и в редко вызываемом участке кода будет не так уж и часто срабатывать. Но может уберечь от каких-то неточных расчётов там, где вы даже неожидаете.


4. Сравнения, которые не требуются на каждом тике, думаю, стоит так же отнести в ту часть кода (основных условий), которая будет срабатывать по истечении определённого периода и применить при этом флаги. К примеру:

   if(NormalizeDouble(OP2-CL2,_Digits)>0)
     {
      double hgRazKus=NormalizeDouble(HG-RazKus,_Digits);
      if(NormalizeDouble(hgRazKus-OP,_Digits)<0 && NormalizeDouble(hgRazKus-CL,_Digits)<0)
        {
         flagBuy=true;//А флаг выключать (делать равным false) в блоке, где будет открываться ордер на покупку. Как-то так
        }
     }


5. А уже в той части кода, что будет обрабатываться на каждом тике уже прописывать:

   if(flagBuy==true)
     {
...
     }


6. Расчёт тейкпрофита вам обязательно нужно нормализовать.


P./S.: Чуть позже ещё напишу. С учётом ваших вопросов в теме.

 

/*только значительно позже обратила внимание на обаятельное название вашего советника :)

и... как интересно у вас всё-таки преобразовалось при выводе на печать слово "Бар" из тестового скрипта...:

2015.04.13 21:05:36.464 qq GBPUSD,Daily: Áàð #1; 1440: 1.47234*/

Так... Продолжение )

7. Ещё немного о переменных. Какие-то, думаю, можно отнести к глобальным или объявить со static, чтобы были видны в разных частях кода и сохраняли свои значения.

Какие-то - объявить только в пределах функции, в которой они применяются и далее не требуются (как, например, в примере выше по double hgRazKus).

Если переменная объявлена на глобальном уровне кода советника, то в том же OnInit её лучше инициализировать, например, "обнулять", если не требуется задать другое начальное значение. При объявлении переменной со static или в виде обычной локальной внутри функции, ей так же присваивается какое-то значение.

Это к тому, что, к примеру, если у вас:

double HG,LW;

будут в глобальных переменных, то в OnInit их стоит обнулить (поскольку неизвестно, что им может присвоить начально терминал и/или чтобы, к примеру, обнулять значения при смене таймфрейма/символа)

Да и, в зависимости от ситуации, отсутствие сбрасывания предыдущих данных, может влиять на корректность расчётов (ну и, если что, важно не переборщить со сбрасыванием данных в самом коде).


8. Поскольку получается, что вы тестируете советник и на пятиминутке, а применяете данные со старшего таймфрейма, то корректность результатов может зависеть ещё и от готовности данных старшего таймфрейма (в общем-то, поэтому и рекомендовала статьи по тестеру, да и Tapochun, как понимаю, именно это имел в виду, когда писал про то, что возможно нет истории и рекомендовал её подгрузить).

Кроме того, при тестировании полная идентичность не гарантирована (различия могут зависеть, в том числе, и от выбора варианта спреда).

Однако, исходя из прилагаемых вами скринов, у меня всё-таки есть сомнения, что проблема сейчас у вас может быть из-за неполной истории или вашего выбора варианта спреда.

Но, в любом случае, статьи по тестеру (особенно в плане применения на младших таймфреймах данных старших таймфреймов) - с моей точки зрения, однозначно, не лишние в совете.


9. Пример тестового скрипта приводила по нескольким причинам:

  • Для наглядности к словам о тестовых проверках;
  • Чтобы вы самостоятельно и на всякий случай посмотрели, что проблема, в данном случае, не в данной функции в целом, при её применении на разных таймфреймах (или какой-то другой функции). И поскольку сама так делаю, как и писала выше, при возникновении проблем/изменениях в работе функций/знакомстве с работой функций (вариантами работы функций);

/*и при необходимости, с проверкой её и/или других в основном коде советника/индикатора, или в "мелком" (плин, так классно вы назвали советник с тестовым кодом - "мелким"! :)), с выводом результатов через Print или Comment.*/

  • С моей точки зрения, оптимальнее прописать в коде советника iHigh и подобные ей функции с PERIOD_D1, нежели с 1440 (то есть и для этого привела код);
/*если при применении PERIOD_D1 вместо 1440, возможны подводные камни, то, надеюсь, меня поправит кто-либо другой,  по возможности описав их (сама подводных камней не замечала при этом)*/

На всякий случай напишу (хотя, думаю, наверное это и излишне с моей стороны и вы сами в курсе), что объявление переменной PeriodFirst в таком случае может выглядеть так (если для отображения и выбора во входных параметрах советника):

input ENUM_TIMEFRAMES PeriodFirst=PERIOD_D1;

или так, если отображение во входных параметрах не требуется:

ENUM_TIMEFRAMES PeriodFirst=PERIOD_D1;
  • Ещё из причин: видя отсутствие применения нормализаций в коде советника, пришла к выводу, что и DoubleToString при выводе на печать значений double можете не сильно жаловать пока. А без применения DoubleToString может быть некорректным "визуальный" вывод значений на печать и, как следствие, возможно ошибочно посчитать, что расчёт идёт некорректный там, где он корректен по факту (не имею ввиду проблему сейчас у вас конкретно, а имею в виду в целом). Ну и посчитала, что можете обратить внимание на эту функцию в коде скрипта. Как-то так.

И да, при выводе iOpen, iHigh, iLow, iClose - у меня нет уверенности, что обязательно требуется нормализация через NormalizeDouble, если нет сопутствующих дополнительных математических расчётов(+-/*). Но при выводе на печать iHigh и т.п., не рекомендовала бы игнорировать DoubleToString.


10. Исходя из скринов, могу предположить, что не происходит срабатывание условий для продажи  - не вижу на скринах вывода на печать ошибок при установке ордера. Т.е., по скринам получается, что до установки ордера на продажу дело не доходит. Соответственно, с моей точки зрения, не помешала бы поэтапная проверка тех строк, включая сравнения, что предшествуют установке ордера.


В общем... как-то так, если кратко.

Приношу извинение. Про ордер бай, ForTheDinamikMn, TakeProfitFirst здесь по своей ошибке написала (как мОрок на глазах был, хотя, прежде чем написать, ведь и смотрела на скрины в этом плане не раз и не два). Поэтому сейчас и удалила, оставив эту фразу вместо того, что было.

Нет... Про ForTheDinamikMn с TakeProfitFirst зря удалила. Предварительные проверки на корректность их значений, если этого нет ранее где-то у вас в коде советника, стОит прописать.

Так. Лан. Отдохнуть мне пора, сильно похоже... Sorry за правки в тексте.

 
NaKaZ:

 Я прошу прощения, если я не внимательно прочел справку, однако, не нашел.

При тестировании данного кода, тестер стратегий(указываем период Daily) открывает по валютной паре  сделки на Buy( 6 штук за  указанный временной  период) и Sell( 13  штук за указанный временной период) согласно условиям кода. Т.е. всё "чики пуки"

Переменная  PeriodFirst = 1440. Следовательно, по моей(мб не верной) логике, при переключении в тестере период на 5M всё переламывается и умирает. За тот же указанный временной  период открываются ТОЛЬКО сделки типа Buy ( 7  штук. не 6. О_о ). Сделки типа Sell не открываются совсем. 

Ошибка ли это тестера? и если да, то можно ли рассчитывать на успешную работу советника, установленного на 5 минутный график? 

Запустите тестер на Д1 и распринтуйте:

  • кол-во баров Д1, доступных советнику (iBars),
  • значения нескольких последних баров с Д1 (время, OHLC).
Потом запустите на М5 и сравните.

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