English 中文 Español Deutsch 日本語 Português
Мой первый "грааль"

Мой первый "грааль"

MetaTrader 4Тестер | 28 июля 2006, 11:57
20 116 39
Сергей Ковалев
Сергей Ковалев

".. не программируется искусство, не выводятся
два чувства поэзии. Таланты
не выращиваются квадратно-гнездовым
способом. Они рождаются. Они национальные
богатства - как залежи радия,
сентябрь в Сигулде или целебный источник .."
(А.А.Вознесенский)


     Слово "Грааль" уходит корнями в далёкое прошлое и означает изумрудную чашу, испив из которой, человек обретает силу, власть, бессмертие. Подробнее узнать об этом вы можете здесь или воспользовавшись средствами поиска в сети Internet.

    В современной среде программистов слово "грааль" имеет ироничный оттенок - под этим подразумевается невозможность создания "универсальной" программы на все случаи жизни, а  в случае программирования на языке MQL4 - невозможность создания эксперта, показывающего фантастический результат в реальной торговле.

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

    Вместе с тем, выявленные на сегодняшний день закономерности не могут в полной мере служить фундаментальной основой для прогнозирования рынка с большой вероятностью успеха. Подтверждением тому - противоречивые прогнозы ведущих аналитиков именитых банков и финансовых организаций. У всех без исключения аналитиков очень хорошо получается объяснять уже случившиеся события и одновременно с этим мало кто из них может показать серию  действительно уверенных прогнозов.

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

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

 

1. Из чего состоит "грааль".


    В формальной логике принято считать, что ссылка на авторитеты доказательством не является. Зная это, начинающий "граальщик" рассуждает приблизительно так: "А Вы можете доказать,  что создать "грааль" невозможно?  Нет?  А раз нет, то значит возможно!". При этом не принимая во внимание, что возможность создания "грааля" тоже не доказана. И не учитывая и зачастую даже не изучив опыт других "златоискателей", но воодушевлённый мыслью "Я смогу!", основанной исключительно на собственном энтузиазме и  непосвящённости,  он садится за клавиатуру.

1.1. Формальная стратегия.

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

    Для поиска хороших критериев наш герой открывает клиентский терминал MetaTrader 4 и смотрит на график EURUSD в минутном масштабе. Очень легко заметить, что курс валют изменяется волнами:  вверх - вниз,  вверх - вниз. Герой решает зарабатывать на этих волнах. Но для того, чтобы "поймать" волну, нужно как-то определить, что подъём уже кончился и вот уже начинается спуск.

   Если критерием направления выбрать простое движение курса, то из этого ничего не выйдет, потому что черные и белые  свечи часто сменяют друг друга, причём амплитуда этого мелкого дребезжания зачастую находится в пределах спреда или близко к тому. Кроме того, хотелось бы открыться на вершине волны, а не на склоне. И волны, к сожалению, имеют разную высоту. Немного поразмыслив, наш герой определяет следующие критерии:
  • Считать, что нужно открываться вниз (Sell), если курс прошёл вверх некоторое количество пунктов, например, 10 - 15 (движение А-В на Рис.1), а потом перестал идти вверх и пошёл на несколько пунктов вниз (движение В-С на Рис.1), скажем, пункта на 3. При этом прогнозируется движение рынка вниз (C-D на Рис.1). Этот же критерий использовать для закрытия Buy.
  • Считать, что нужно открываться вверх (Buy), если курс прошёл некоторое количество пунктов вниз, например, 10 - 15 (движение C-D на Рис1), а потом перестал идти вниз и пошёл на несколько пунктов вверх (движение D-E на Рис1), на те же 3 пункта.  При этом прогнозируется движение рынка вверх (E-F на Рис.1). Этот же критерий использовать для закрытия Sell.



Рис.1. Торговые критерии к эксперту Грааль_1.

    Жизнь подчас готовит нам удивительный, неожиданный опыт. Всего за 3 дня новоиспечённый программист создал свой первый "грааль".
extern int TP=100; extern int SL=100; extern int lim=1; extern int prodvig=3;
extern double  Prots= 10;
int   total, bb=0,ss=0; double max,min,lmax,lmin,Lot; 
int start(){
total=OrdersTotal(); if (total==0){bb=0;ss=0;}
if (max<Bid) max=Bid;if (min>Ask) min=Ask;
if (((max-Bid)>=lim*Point)&&(Bid>lmax )) { for (int i=total;i>=0;i--) { 
if (OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==true && OrderType()==OP_BUY)
{OrderClose(OrderTicket(),OrderLots(),Bid,3,CLR_NONE); bb=0;}} Strateg(1); } 
if (((Ask-min)>=lim*Point)&&(lmin>Ask )) { for (i=total;i>=0;i--) {
if (OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==true && OrderType()==OP_SELL)
{OrderClose(OrderTicket(),OrderLots(),Ask,3,CLR_NONE);ss=0;}}  Strateg(2);}return;}
void Strateg (int vv)                                    
{if (vv==1 && ss==0)                                   
{OrderSend(Symbol(),OP_SELL,Lots(),Bid,3,Bid+SL*Point,Bid-TP*Point,"",0,0,Red); ss=1;}
if (vv==2 && bb==0)                                   
{OrderSend(Symbol(),OP_BUY, Lots(),Ask,3,Ask-SL*Point,Ask+TP*Point,"",0,0,Blue);bb=1;}
lmax=Ask+prodvig*Point; lmin=Bid-prodvig*Point;   return;  }
double Lots(){ Lot=NormalizeDouble(AccountEquity()*Prots/100/1000,1);
double Min_Lot = MarketInfo(Symbol(), MODE_MINLOT);   
if (Lot==0 ) Lot=Min_Lot; return(Lot);  }

   Что получилось, то получилось. Не будем судить строго - это ведь первый опыт начинающего программиста, у которого ещё не сложился свой стиль. И будем относиться уважительно, потому что этот эксперт работает и  показывает действительно фантастический результат.

    Давайте представим код в удобочитаемом виде и попробуем разобраться, что здесь к чему. После некоторого редактирования этот же эксперт может выглядеть так:

//-------------------------------------------------------------------------------------------
// Грааль_1.mq4.
// Используется в качестве примера в статье "Мой первый Грааль".
// Сергей Ковалёв, Днепропетровск, sk@mail.dnepr.net, ICQ 64015987, http://autograf.dp.ua/.
//-------------------------------------------------------------------------------------------
extern int     TP = 100;                                 // ТэйкПрофит ордера
extern int     SL = 100;                                 // СтопЛосс ордера
extern int     lim=   1;                                 // Дистанция возврата курса
extern int     prodvig=3;                                // Дистанция продвижения курса
extern double  Prots= 10;                                 // Процент от свободных средств
//--------------------------------------------------------------------------------------------
int
   total,                                                // Количество лотов
   bb=0,                                                 // 1 = факт налиия ордера Buy
   ss=0;                                                 // 1 = факт налиия ордера Sell 
//--------------------------------------------------------------------------------------------
double 
   max,                                                  // Максимальная цена на горке (абс)
   min,                                                  // Минимальная цена во впадине(абс)
   lmax,                                                 // Пороговая цена, после преодоления
                                                         // которой рассматриваем продажу(абс)
   lmin,                                                 // То же для покупки
   Lot;                                                  // Количество лотов
//-------------------------------------------------------------------------------------------
int start()
   {   
//-------------------------------------------------------------------------------------------
   total=OrdersTotal();                                  // Количество лотов
   if (total==0)                                         // Если ордеров нет, ..
      {
      bb=0;                                              // .. то нет баёв
      ss=0;                                              // .. то нет селов
      }
   if (max<Bid) max=Bid;                                 // Считаем максим цену на горке 
   if (min>Ask) min=Ask;                                 // Считаем миним цену во впадине
//------------------------------------------------------------- Цена разворачивается вниз ----
   if (((max-Bid)>=lim*Point)&&(Bid>lmax ))              // Разворот на высовком уровне
      {
      for (int i=total;i>=0;i--)                         // По всем ордерам
         {                                               
         if (OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==true && OrderType()==OP_BUY)
            {
            OrderClose(OrderTicket(),OrderLots(),Bid,3,CLR_NONE);// Закрываем бай
            bb=0;                                        // Баёв больше нет
            }
         }   
      Strateg(1);                                        // Открывающая функция
      }             
//------------------------------------------------------------ Цена разворачивается вверх ----
   if (((Ask-min)>=lim*Point)&&(lmin>Ask ))              // Разворот глубоко внизу
      {
      for (i=total;i>=0;i--)                             // По всем ордерам
         {
         if (OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==true && OrderType()==OP_SELL)
            {         
            OrderClose(OrderTicket(),OrderLots(),Ask,3,CLR_NONE);// Закрываем селл   
            ss=0;                                        // Селлов больше нет
            }
         }
      Strateg(2);                                        // Открывающая функция
      }
//-------------------------------------------------------------------------------------------
   return;
   } 
//-------------------------------------------------------------------------------------------
void Strateg (int vv)                                    // Открывающая функция
   {
//-------------------------------------------------------------------------------------------
   if (vv==1 && ss==0)                                   // Селловая ситуация и селлов нет
      {
      OrderSend(Symbol(),OP_SELL,Lots(),Bid,3,Bid+SL*Point,Bid-TP*Point,"",0,0,Red);// Откр
      ss=1;                                              // Теперь есть селл
      }
//--------------------------------------------------------------------------------------------
   if (vv==2 && bb==0)                                   // Баёвая ситуация и баёв нет
      {
      OrderSend(Symbol(),OP_BUY, Lots(),Ask,3,Ask-SL*Point,Ask+TP*Point,"",0,0,Blue);// Откр
      bb=1;                                              // Теперь есть бай
      }      
//--------------------------------------------------------------------------------------------
   lmax=Ask+prodvig*Point;                               // Переопределяем новые пороговые ..
   lmin=Bid-prodvig*Point;                               // .. уровни для откр и закр 
//-------------------------------------------------------------------------------------------
   return;
   }
//-------------------------------------------------------------------------------------------
double Lots()                                            // Вычисление лотов
   {
//-------------------------------------------------------------------------------------------
   Lot=NormalizeDouble(AccountEquity()*Prots/100/1000,1);// Вычисляем колич. лотов  
   double Min_Lot = MarketInfo(Symbol(), MODE_MINLOT);   // Минимально допустимая стоим. лотов
   if (Lot == 0 ) Lot = Min_Lot;                         // Для теста на постоян. миним. лотах
//-------------------------------------------------------------------------------------------
   return(Lot);
   }
//-------------------------------------------------------------------------------------------

  В целом содержание советника вполне понятно.

  В верхней части до функции start() собраны переменные. В функции start() сначала вычисляется текущее положение курса на склоне горки (впадины), а после осуществляется анализ ситуации на срабатывание торговых критериев. И если критерии срабатывают, то производится закрытие уже отработавших ордеров. В эксперте используются также 2 функции - Strateg() для открытия новых ордеров и Lots() для определения количества лотов. Для учёта уже открытых ордеров используются переменные ss и bb.

    Очень важным инструментом из состава клиентского терминала MetaTrader 4 является тестер стратегий. Наш герой произвёл тщательное тестирование с целью оптимизации параметров и одним из лучших результатов получил такой.


 

Рис. 2. Классический "грааль". Результат получен при тестировании эксперта Грааль_1.mq4 на истории с марта 2005 по июнь 2006 года в масштабе М1 EURUSD на торговых условиях демо-сервера компании MetaQuotes Software Corp.

    Легко представить себе радость программиста, собственными руками сотворившего это произведение искусства. Новенький, вполне осязаемый - вот он, сверкающий гранями совершенства, с профитом, достигающим поднебесья, его первый "грааль"! И он легко пришёл к выводу - "Только я!"

    Какая бы то ни было формальная стратегия совсем не обязательно должна заканчиваться созданием "грааля", но в данном случае получилось именно так. Рассмотрим более подробно, что именно привело к такому результату.
 

1.2. Прогрессивное вложение средств.

    Наш герой не остановился на достигнутом и решил попробовать предельно увеличить стоимость ордеров. Он довёл стоимость ордеров при тестировании до 70% (значение extern-переменной Prots=70) от суммы свободных средств, но результат показал неэффективность такого подхода:

 

Рис. 3. Агрессивное инвестирование средств может привести к неоправданным потерям.


    Наш герой был немало удивлён таким результатом. Он рассуждал так: "Если эксперт прибыльный,  то чем больше в него вкладываешь, тем больше получаешь!". Но проверка на тестере снова и снова доказывала, что это далеко не всегда так.

    Рассмотрим более подробно причины такого результата.
 

1.2.1. Геометрическая прогрессия.

   Обратим внимание на технологию расчёта стоимости ордеров. В соответствии с представленным кодом стоимость ордеров наращивается пропорционально сумме свободных денежных средств:
   Lot=NormalizeDouble(AccountEquity()*Prots/100/1000,1);// Вычисляем колич. лотов  
   double Min_Lot = MarketInfo(Symbol(), MODE_MINLOT);   // Минимально допустимая стоим. лотов
   if (Lot == 0 ) Lot = Min_Lot;                         // Для теста на постоян. миним. лотах

    Этот алгоритм являет собой геометрическую прогрессию. Переменная Prots (процент) позволяет регулировать стоимость ордера в зависимости от текущего количества свободных средств. Такой способ вычисления стоимости ордеров является не вполне корректным, т.к. не учитывает маржинальные требования при работе с конкретным дилером. Вместе с тем, представленный фрагмент кода позволяет решить основную задачу - пропорционального вложения средств. Таким образом, стоимость каждого последующего ордера будет пропорционально зависеть от достигнутых результатов: после каждой прибыльной операции стоимость будет повышаться, а после убыточной - понижаться. В представленном коде эксперта Грааль_1.mq4 стоимость ордера составляет 10% от свободных средств.

    Сама по себе прогрессия вовсе не является атрибутом "грааля". По этой технологии может быть построен любой другой эксперт из разряда нормальных. Однако,  для более удобного рассмотрения состава "грааля" геометрическую прогрессию следует полностью исключить и ориентироваться на результаты тестирования, проведенного при постоянной минимальной стоимости ордеров (равной 0,1 лота, если это возможно). В этом случае мы можем легко судить о количестве заработанных пунктов.

    Установим значение переменной Prots равным 0 и в соответствии с этим кодом
   if (Lot == 0 ) Lot = Min_Lot;                         // Для теста на постоян. миним. лотах

проведём тестирование при постоянной стоимости ордеров.

    В этом случае результат будет таким:
 
 

               Рис. 4. Результаты тестирования эксперта  Грааль_1.mq4 при постоянной стоимости лотов.
 

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

1.2.2. Агрессивное реинвестирование.

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

    Например, вполне приемлемым отношением прибыли и убытка можно считать отношение 3 : 1. То есть, на 100 последовательно открытых и закрытых ордеров должно приходиться 75 прибыльных и 25 убыточных. Вместе  с тем, никогда нельзя сказать наперёд как будут распределены убыточные ордера среди прибыльных. Это распределение в значительной степени носит случайный характер.

    А. Идеальным случаем последовательности Прибылей и Убытков является равномерное их распределение на протяжении всей торговой истории:

П П П У П П П У П П П У П П П У П П П У П П П У П П П У ...

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


    В.
Один из вероятных случаев неравномерного распределения прибыльных и убыточных торговых операций в реальной торговле:

П П П П П П П П П П П П П П П  УУУУУ П П П У П П П У ...


    Здесь показана серия из 5 последовательных убытков, хотя не исключена возможность и более длинной серии потерь. (обратите внимание, что в данном случае отношение прибыльных и убыточных ордеров сохраняется в пределах  3 : 1 ).

   К чему может привести эта закономерная случайность? Ответ на этот вопрос зависит от качества торговой стратегии, которой придерживается трейдер. Одно дело, если трейдер ведёт осмотрительную торговлю, соблюдая разумные меры предосторожности,  а именно - суммарная стоимость его ордеров не превышает 10-15%; и совсем другое дело, если трейдер допускает агрессивное реинвестирование,  а именно вкладывает в торговлю большую долю своего депозита, причём не один раз, а всякий раз после получения очередной прибыли.

    В случае агрессивного реинвестирования история  торгового счёта будет развиваться непредсказуемо. Какой-нибудь редкий "везунчик" может быть и ускользнёт от неприятностей. Но в ряде случаев негативный результат неизбежен. Для иллюстрации возможных сценариев работы эксперта  Грааль_1.mq4  проанализируем два варианта инвестиций при прочих равных условиях (один и тот же исторический период, одинаковые настраиваемые параметры).
  • Вариант 1. Стоимость ордера равна 10% свободных средств ( Prots=10;). Тестирование при этих условиях даёт возможность эксперту всё больше и больше набирать обороты, неспешно, но постоянно увеличивая баланс. Результат развития баланса для этого варианта вы можете посмотреть на Рис.2.  На рисунке видно, что эксперт продолжает свою работу на протяжении длительного периода (около 10 000 ордеров)
  • Вариант 2. Стоимость ордера равна 70% от суммы свободных средств ( Prots=70;). Результаты тестирования представлены на Рис.3. Всего две-три серии из нескольких убыточных ордеров привели к тому, что не успел эксперт совершить и 400 торговых операций, как депозит полностью опустошился.

    Обратите внимание на отчёт по результатам тестирования: максимальное количество непрерывных проигрышей (серия убыточных ордеров) для эксперта  Грааль_1.mq4  составляет всего 10. В течение всего тестируемого периода были и другие убыточные серии, но каждая из них содержала не более 10-и убыточных ордеров. Эти серии убытков не оказали заметного влияния на общий результат торговли в первом варианте и сыграли роковую роль во втором.

    Таким образом,  небольшая серия убытков при неправильном инвестировании может привести к полному краху. Для правильного управления капиталом разработана эффективная система Money Management, в соответствии с которой стоимость одного ордера не должна превышать 15% от общей суммы баланса, а общая сумма вложенных средств не должна превышать 50%.  Эта система содержит и другие полезные правила, с которыми познакомился наш герой.

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

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

    Наш герой был очень раздосадован. Он стал выяснять причину такой несправедливости у дилеров, но при этом гневно отвергал любые объяснения. Он недоумевал,  какие ещё могут быть "технические ограничения" и причём тут "проскальзывание"?! В результате такого первого опыта реальной торговли наш герой пришёл к выводу, что это "плохой ДЦ", что его просто обманули и разорвал свой договор с дилинговым центром.


  Однако, отчаиваться он не стал, а решил выяснить доподлинно, что здесь к чему, и обратился за помощью к более опытным коллегам на специализированном форуме MQL4, где ему первым делом предложили показать код. Показывать код было жалко, но в конце концов он согласился, ведь всё равно эксперт "сливал в реале".. В результате обсуждения на форуме наш герой выяснил для себя следующее.


1.3. Ошибки.

    Редактор Meta Editor постоянно свидетельствовал об отсутствии ошибок,
 
 
 
Рис. 5.  Отсутствие ошибок при компиляции.

    но они, тем не менее, были.

1.3.1. Отступление от формальной стратегии.


    Несмотря на то, что у истоков создания  эксперта стояла вполне осмысленная стратегия, в процессе работы над кодом и оптимизации настраиваемых переменных она претерпела существенные изменения и в составе "грааля" исходная идея уже присутствует в очень усечённом виде. В самом деле, предполагалось зарабатывать на волнах размером 15 - 20 пунктов, а в "граале" этот параметр принимает мизерное значение, равное 3. Какие уж тут горки и впадины..

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

    Обратите внимание на то, как в переменных ss и bb ведётся учет ордеров. Такой способ учёта ордеров является некорректным. Ордер учитывается как уже открытый  на этапе формирования  торгового приказа. В этот момент ещё нельзя сказать наверняка будет ли открыт ордер. Чтобы точно ответить на этот вопрос, необходимо дождаться ответа сервера и проанализировать наличие ордера по факту его присутствия в терминале.  Для реальной торговли код в этой части следовало бы переписать, хотя представленный вариант и будет работать в большинстве случаев, в том числе в тестере стратегий (см. статью Учёт ордеров в большой программе ).
 

1.4. Технические ограничения.


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

    Если для моделирования истории бара большого масштаба уточнением является баровая история меньшего масштаба, то в отношении минутного бара такого уточнения не существует, потому что тиковая история в клиентском терминале MetaTrader 4 не сохраняется и не используется. (см. Strategy Tester: режимы моделирования при тестировании).

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

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

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


Рис. 6.  Результат тестирования эксперта Грааль_1.mq4 на истории с 4 апреля 2006 по 26 июля 2006 года в масштабе М1 валютного инструмента EURUSD на торговых условиях демо-сервера  Интеграл-банка  при постоянной стоимости ордеров.

   Наличие глубоких просадок, неустойчивое развитие прибыли свидетельствует о балансировании советника на грани убытка.

   Как правило, разница курсов при рекотировании составляет 1-2 пункта (хотя бывает и гораздо больше на быстром рынке). Это явление не может оказать существенного влияния на успешность работы советника, у которого матожидание выигрыша значительно выше, например, 10 - 15. Но советник, у которого матожидание выигрыша мало, в особенности советник, имеющий матожидание менее 2, не может всерьёз рассчитывать на общий успех, потому что он находится в большой зависимости от характера поставляемых котировок и связанного с этим рекотирования.
    
   Будет или не будет выдан реквот на текущем тике конкретным дилером - это отдельный вопрос, решить который может единственно дилер. Но приступая к работе на реальном рынке следует исходить из убеждения, что реквот - это нормальное, естественное явление, и торговая технология работающего на реальном счёте советника должна это учитывать.



1.5. Экономический фактор.

  • Малая стоимость ордеров.

   Наиболее распространена ситуация, при которой внимание трейдера приковано только к своему торговому счёту и событиям вокруг него, в том числе стратегиям, экспертам и прочему. Однако, кроме заинтересованности трейдера существует ещё и экономическая заинтересованность дилера.

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

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

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

    Технология работы дилера в целом достаточна проста. Дилер собирает заявки трейдеров на покупку и продажу и вступает в отношения с банком или другой финансовой организацией на разницу в их стоимости. Рассмотрим простой пример. Предположим,  дилер обслуживает всего 100 трейдеров, 60 из которых купили по 1 лоту евро против  доллара, а 40 - продали по 1 лоту той же валютной пары. В этом случае дилер должен купить 20 лотов (разница между 60 и 40) у банка. При этом дилеру безразлично, куда пойдёт курс. В любом случае он получит полный спред по 80 лотам (40+40) и часть спреда от 20 лотов (часть уйдёт в пользу банка).

   В этом случае источником прибыли/убытка для 40 трейдеров являются убыток/прибыль других 40 трейдеров. Источником прибыли/убытка для оставшихся 20 трейдеров является банк, а в конечном счёте - юридические лица, обслуживающиеся в банке, продающие/покупающие валюту для экспортно-импортных операций. Это - обычный, "нормальный" порядок взаимоотношений между участниками валютного рынка.

    Но есть и другая подробность отношения дилера с банком или финансовой организацией. Дело в том, что  дилер не вступает в отношения с банком при каждой покупке или продаже трейдера на незначительную сумму. Торговые операции между дилером и банком осуществляются несколько реже, чем клики трейдеров в торговом терминале МТ4. Обычно минимальная сумма в отношениях дилера с банком составляет не более $50 000, что в переводе на стоимость ордера с кредитным плечом 1:100 составляет 0,5 лота. Поэтому, если трейдер всё время работает с маленькой суммой, то, в сущности, он торгует с дилером. В этом случае источником прибыли трейдера являются  денежные средства дилера!

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

    Обратите внимание, что сложившийся на сегодняшний день порядок взаимоотношений на валютном рынке - вовсе не прихоть и не злая воля дилера. Его отношения с банками тоже находятся "в рамках" договорных условий. Дилер стремится честно заработать свой спред (обсуждение злоупотреблений со стороны дилера выходит за рамки темы статьи). Может быть он и был бы рад "зеркально" ежесекундно торговать с банком, но на сегодняшний день такой возможности у него нет.

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

  • Большая стоимость ордеров.

   Существует ещё одно ограничение, отличающее реальную торговлю от тестирования и опробования на демо-счёте. Это - максимальное значение стоимости лотов. Каждый дилинговый центр, будь то банк, букмейкерская контора или финансовая организация, не имеют практической возможности оперировать очень крупными суммами.

   При стоимости ордеров до 10 - 20 тыс. долларов (обратите внимание, что существует кредитное плечо, как правило, 100) дилинговый центр фактически продаёт/покупает сумму 1 млн долларов. Это - не маленькая сумма, но с такими суммами денежных средств дилеры научились работать и успешно работают.
Некоторые трудности у финансовой организации средней величины могут возникнуть, если стоимость ордера достигает 50 - 100 тыс. долларов. В этом случае на бежбанковском рынке фигурирует сумма 5 - 10 млн. долларов. Такую сумму уже совсем не просто продать/купить одномоментно одним движением.

   Серьёзные затруднения могут возникнуть при стоимости ордеров более 300 -500 тысяч долларов. В этом случае дилинговый центр безусловно будет вести индивидуальную работу с трейдером. 50 млн долларов - это солидная сумма для любой организации.

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

   По этой причине пипсующие советники, оперирующие миллионами, -  свидетельство глубокого заблуждения трейдера и с реальной жизнью не имеют ничего общего.

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

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

    Судите сами. Каковы (при этих технических  и экономических подробностях) могут быть результаты реальной работы эксперта, открывающего в течение года более 20 000 ордеров?

 

Рис.7.  Отчёт по тестированию эксперта Грааль_1.


    От всех этих тиков, баров, пунктов, спредов, дилеров и банков голова у нашего героя пошла кругом, поэтому он не стал углубляться в детали и прояснять ещё и "отдельные вопросы". Ему было достаточно его собственного понимания - "тестер с реалом не совпадает". И он решил, что построит подходящую технологию на отложенных ордерах. "Вот тут уже они меня не достанут!",- решил он.

 

2. Второй "грааль".

 

2.1 Стратегия.

    Мысль построить эксперт на отложенных ордерах пришла нашему герою после того, как он рассматривал характер изменения кросс-курсов EURGBP и EURCHF в минутном масштабе.


 

Рис. 8. Участки истории кросс-курсов со значительными отклонениями цены (выбросы).

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

В соответствии с формальной логикой достаточно несколько ниже и несколько выше основного потока выставить отложенные ордера типа Limit и часть из них будет открыта с довольно большой вероятностью успеха. А чтобы адекватно реагировать на изменения основного движения курса, отложенные ордера необходимо "таскать" вдоль некоторой средней линии, всё время поддерживая их на некотором заданном расстоянии от средней МА (Moving Average). Для осуществления задуманного наш герой написал эксперт:
//-------------------------------------------------------------------------------------------
// Грааль_2.mq4.
// Используется в качестве примера в статье "Мой первый Грааль".
// Сергей Ковалёв, Днепропетровск, sk@mail.dnepr.net, ICQ 64015987, http://autograf.dp.ua/.
//-------------------------------------------------------------------------------------------
extern int TakeProfit=5;                                 // TakeProfit ордера
extern int StopLoss= 29;                                 // StopLoss ордера
extern int Distan   = 2;                                 // Дистанция от средней МА
extern int Cls      = 2;                                 // Закрыть при ** пнктов прибыли
extern int period_MA=16;                                 // Период средней МА 
//extern int Time_1   = 0;                               // Время начала работы 
//extern int Time_2   = 0;                               // Время окончания работы
extern int Prots    = 0;                                 // Процент от свободных средств
 
//--------------------------------------------------------------------------------------------
int
   Nom_bl,                                               // Номер ордера BuyLimit
   Nom_sl,                                               // Номер ордера SellLimit
   total,                                                // Количество лотов
   bl = 0,                                               // 1 = факт налиия ордера BuyLimit
   sl = 0,                                               // 1 = факт налиия ордера SellLimit
   b  = 0,                                               // 1 = факт налиия ордера Buy
   s  = 0;                                               // 1 = факт налиия ордера Sell 
//--------------------------------------------------------------------------------------------
double 
   OP,                                                   // OpenPrice (абсолютн. пунктов)
   SL,                                                   // StopLoss   ордера (относит.пункт.)
   TP,                                                   // TakeProfit ордера (относит.пункт.)
   dist,                                                 // Дистанция от средней МА(отн.пун.)
   Level,                                                // Миним. допуст дистанция отл.орд
   OP_bl,                                                // OpenPrice BuyLimit (абс. пунктов)
   OP_sl,                                                // OpenPrice SellLimit(абс. пунктов)
   cls,                                                  // Закрыть при ** прибыли (абс. пун.)
   MA,                                                   // Значение МА (курс)
   spred,                                                // Спред (абс. пунктов)
   Lot;                                                  // Количество лотов
//-------------------------------------------------------------------------------------------
int init()
   {   
   Level=MarketInfo(Symbol(),MODE_STOPLEVEL);            // Выясним что нам даёт сервер
   Level=(Level+1)*Point;                                // ?:)
   SL=StopLoss*Point;                                    // StopLoss   ордера (относит.пункт.)
   TP=TakeProfit*Point;                                  // TakeProfit ордера (относит.пункт.)
   dist=Distan*Point;                                    // Дистанция от средней МА(отн.пун.)
   cls=Cls*Point;                                        // Закрыть при ** прибыли (абс. пун.)
   spred=Ask-Bid;                                        // Спред (абс. пунктов)
   return;
   } 
//-------------------------------------------------------------------------------------------
int start()
   {   
//-------------------------------------------------------------------------------------------
   total=OrdersTotal();                                  // Количество лотов
   bl=0;                                                 // Обнулимся в начале прохода
   sl=0;                                                 // Обнулимся в начале прохода
   b=0;                                                  // Обнулимся в начале прохода
   s=0;                                                  // Обнулимся в начале прохода
//--------------------------------------------------------------------------------------------
   for (int i=total; i>=0; i--)                          // По всем ордерам
      {                                               
      if (OrderSelect(i,SELECT_BY_POS)==true &&         // Выделим ордер
         OrderSymbol()==Symbol())
         {
      
//--------------------------------------------------------------------------------------------
         if (OrderType()==OP_BUY)                        // Ордер Buy
            {
            b =1;                                        // Есть такой ордер
            Close_B(OrderTicket(),OrderLots());          // Закрыть ордер (надо ли решит ф-ия)
            }
//--------------------------------------------------------------------------------------------
         if (OrderType()==OP_SELL)                        // Ордер Sell
            {
            s =1;                                        // Есть такой ордер
            Close_S(OrderTicket(),OrderLots());          // Закрыть ордер (если надо)
            }
//--------------------------------------------------------------------------------------------
         if (OrderType()==OP_BUYLIMIT)                   // Ордер BuyLimit
            {
            OP_bl=NormalizeDouble(OrderOpenPrice(),Digits);//OpenPrice BuyLimit(абс. пунктов)
            Nom_bl=OrderTicket();
            bl=1;                                        // Есть такой ордер
            }
//--------------------------------------------------------------------------------------------
         if (OrderType()==OP_SELLLIMIT)                  // ОрдерSellLimit
            {
            OP_sl=NormalizeDouble(OrderOpenPrice(),Digits);//OpenPrice SellLimit(абс.пунктов)
            Nom_sl=OrderTicket();
            sl=1;                                        // Есть такой ордер
            }
//--------------------------------------------------------------------------------------------
         }
      }   
//--------------------------------------------------------------------------------------------
   MA = iMA(NULL,0, period_MA, 0,MODE_LWMA, PRICE_TYPICAL, 0);// Текущее значение МА
   Modify_order();                                       // Активизируем модификацию
   Open_order() ;                                        // Активизируем открытие
//-------------------------------------------------------------------------------------------
   return;
   } 
//-------------------------------------------------------------------------------------------
void Close_B(int Nomber, double lots)                    // Закрытие ордеров Buy
   {
//-------------------------------------------------------------------------------------------
   if (NormalizeDouble(Bid-OrderOpenPrice(),Digits)>=cls)// Если достигнут заданный профит
      {
      OrderClose( Nomber, lots, Bid, 1, Yellow);         // Закрываемся
      b = 0;                                             // И больше нет бая
      }
//-------------------------------------------------------------------------------------------
   return;
   }
//-------------------------------------------------------------------------------------------
void Close_S(int Nomber, double lots)                    // Закрытие ордеров Sell
   {
//-------------------------------------------------------------------------------------------
   if (NormalizeDouble(OrderOpenPrice()-Ask,Digits)>=cls)// Если достигнут заданный профит
      {
      OrderClose( Nomber, lots, Ask, 1, Yellow);         // Закрываемся
      s = 0;                                             // И больше нет селла
      }
//-------------------------------------------------------------------------------------------
   return;
   }
//-------------------------------------------------------------------------------------------
void Modify_order()                                      // Модификация ордеров
   {
//-------------------------------------------------------------------------------------------
   if (bl==1)                                            // Если есть БайЛимит
      {
      OP=MA-dist;                                        // Он должен стоять здесь
      if (MathAbs(OP_bl-OP)>0.5*Point)                   // А если он здесь не стоит
         {
         OrderModify(Nom_bl,OP,OP-SL,OP+TP,0,DeepSkyBlue);// Модификация ордера 
         }
      }
//--------------------------------------------------------------------------------------------
   if (sl==1)                                            // Если есть СеллЛимит
      {
      OP=MA+spred+dist;                                  // Он должен стоять здесь
      if (MathAbs(OP_sl-OP)>0.5*Point)                   // А если он здесь не стоит
         {
         OrderModify( Nom_sl, OP, OP+SL, OP-TP, 0, Pink);// Модификация ордера 
         }
      }
//-------------------------------------------------------------------------------------------
   return;
   }
//-------------------------------------------------------------------------------------------
void Open_order()                                        // Открывающая функция
   {
//   int Tek_Time=TimeHour(CurTime());                   // Для тестирования по времени
//   if (Tek_Time>Time_2 && Tek_Time<Time_1) return;     // Для тестирования по времени
//-------------------------------------------------------------------------------------------
   if (b==0 && bl==0)                                    // Нет никаких баёв, открываем bl
      {
      OP=MA-dist;                                        // Курс открытия ордера bl      
      if(OP>Ask-Level) OP=Ask-Level;                     // Уточнение ОР в соотв. с допуском
      OP=NormalizeDouble(OP,Digits);                     // Нормализация (МА даёт 5й знак) 
      OrderSend(Symbol(),OP_BUYLIMIT, Lots(),OP,3,OP-SL,OP+TP,"",0,0,Blue);// Открываемся
      bl=1;                                              // Теперь есть бай
      }      
//--------------------------------------------------------------------------------------------
   if (s==0 && sl==0)                                    // Нет никаких селов, открываем sl
      {
      OP=MA+spred+dist;                                  // Курс открытия ордера sl     
      if(OP<Bid+Level) OP=Bid+Level;                     // Уточнение ОР в соотв. с допуском
      OP=NormalizeDouble(OP,Digits);                     // Нормализация (МА даёт 5й знак) 
      OrderSend(Symbol(),OP_SELLLIMIT,Lots(),OP,3,OP+SL,OP-TP,"",0,0,Red);// Открываемся
      sl=1;                                              // Теперь есть sl
      }
///-------------------------------------------------------------------------------------------
   return;
   }
//-------------------------------------------------------------------------------------------
double Lots()                                            // Вычисление лотов
   {
//-------------------------------------------------------------------------------------------
   Lot=NormalizeDouble(AccountEquity()*Prots/100/1000,1);// Вычисляем колич. лотов  
   double Min_Lot = MarketInfo(Symbol(), MODE_MINLOT);   // Минимально допустимая стоим. лотов
   if (Lot == 0 ) Lot = Min_Lot;                         // Для теста на постоян. миним. лотах
//-------------------------------------------------------------------------------------------
   return(Lot);
   }
//-------------------------------------------------------------------------------------------

    В качестве настраиваемых переменных выбраны StopLoss, TakeProfit, Distan (дистанция, на которой ордер держится "вдоль" основного курса), Cls (минимальное количество заработанных по ордеру пунктов, при которых ордер необходимо закрыть), period_MA (период МА, которая олицетворяет среднюю линию курсов последней истории) и Prots (процент от свободных средств, стоимость ордера). Этого вполне достаточно чтобы осуществить задуманное.
  • В специальной функции start() производится некое подобие анализа ордеров (о методах учёта ордеров можно прочесть здесь) и попутно принимается решение о судьбе некоторых из них. Если в терминале есть любой открытый ордер, то сразу же вызывается соответствующая  функция Close_*(), в которой производится анализ необходимости закрытия и осуществляется закрытие ордера.
  • В конце функции start() вызываются функции Modify_order() и Open_order() соответственно для модификации и открытия отложенных ордеров типа Limit.  Открытие явных ордеров не предусмотрено.
  • В функции Modify_order() вычисляется место, где должен стоять ордер (на некотором расстоянии от средней), и, если он стоит не на месте, то он передвигается на заданное место.
  • В функции Open_order() тоже определяется желаемое место открытия отложенного ордера, при этом учитываются ограничения сервера, и, если ордера нет, то он выставляется. 
    По ходу написания программы наш герой заметил, что её можно совершенствовать до бесконечности, и решил, что "закончить" программу нельзя, её нужно просто "прекратить"! Тестирование эксперта на EURCHF дало следующий результат: (рассмотрим только этот вариант, хотя подобный результат можно получить и для EURGBP).
 
 

Рис. 9. Результат тестирования эксперта Грааль_2.mq4 на истории с марта 2005 по июнь 2006 года в масштабе М1 EURUSD на торговых условиях демо-сервера фирмы MIG Investments.


2.2. Геометрическая прогрессия.

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

Рис. 10.  Результат тестирования эксперта Грааль_2.mq4 на истории с марта 2005 по июнь 2006 года в масштабе М1 валютного инструмента EURUSD на торговых условиях демо-сервера фирмы MIG Investments при постоянной стоимости ордеров.

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

2.3. Ошибки.

    В представленном варианте эксперта имеется масса недоработок и некорректного программирования. В Modify_order() не учитывается минимально допустимая дистанция для отложенных ордеров, неправильно поставлен учёт открытых ордеров, не предпринимается отдельная модификация TakeProfit с целью "подогнать" профит под Cls, аналитическая часть эксперта "размазана" по всему коду и прочее.

    Некорректное и тем более небрежное программирование часто приводит к незаметным ошибкам, а иногда и к тиражированию ошибок при их интеграции в другие эксперты.

2.4. Технические ограничения (выбросы).

    Любые ограничения, технического или организационного характера, как правило в конечном счёте оборачиваются  экономическими потерями. Вопрос лишь в том, за чей счёт эти потери будут покрываться.

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

    Тиковый трафик - тоже предмет купли-продажи. Он является результатом работы некоторой обрабатывающей программы, отфильтровывающей лишние и неестественные ценовые тики. А они ("ненормальные" тики)  появляются. Это бывает, когда в каком-то конкретном банке какой-то конкретный субъект по каким-то своим причинам (мало ли, может срочно нужно) купил или продал некоторую сумму по цене, далёкой от рыночной.  Информация об этом событии воспринимается обрабатывающей программой и если это разовая сделка, то это событие не учитывается при формировании конечного вида трафика. В ряде случаев программе трудно понять какая цена является "нормальной", а какая - нет. По этой причине иногда на графике появляются единичные свечи различной высоты (в зависимости от алгоритма обрабатывающей программы). Это и есть выбросы. В том или ином виде проявляющиеся, они не отражают истинное положение дел на валютном рынке.

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

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

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

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

 

3. Третий "грааль".

 

3.1 Формальная стратегия.

    Усердно и внимательно рассматривая график валютного инструмента EUR|USD в масштабе Н1, применяя для анализа различные индикаторы, наш герой  обнаружил очень привлекательную закономерность. Он заметил, что если МА (Moving Average) меньшего периода пересекает МА большего периода, то рынок, как правило, движется в том  направлении , куда стремится МА меньшего периода.
 
 
      Рис. 11.  Графическое представление стратегии, основанной на пересечении МА разных периодов.
 

    Герой также заметил,  что выявленная зависимость проявляется на всех таймфреймах, но твёрдо решил работать только на больших. Остаётся только переложить с трудом добытое знание на язык MQL4, чтобы сообщить эксперту в какую сторону и при каких условиях нужно открываться. А когда эксперт будет готов, необходимо оптимизировать параметры - подобрать наиболее эффективные размерности МА и правильно расставить StopLoss и TakeProfit.

    На этот раз он произвёл на свет вот такой эксперт:

//-------------------------------------------------------------------------------------------
// Грааль_3.mq4.
// Используется в качестве примера в статье "Мой первый Грааль".
// Сергей Ковалёв, Днепропетровск, sk@mail.dnepr.net, ICQ 64015987, http://autograf.dp.ua/.
//-------------------------------------------------------------------------------------------
//
//
//-------------------------------------------------------------------------------------------
extern int    MA1 = 11;                                  // Период 1-й МА
extern int    MA2 = 23;                                  // Период 2-й МА
extern double TP =  50;                                  // ТэйкПрофит ордера
extern double SL =  15;                                  // СтопЛосс ордера
extern double Prots= 0;                                  // Процент от свободных средств
//--------------------------------------------------------------------------------------------
int
   ret,                                                  // Направление пересечения
   total;                                                // Крличество открытых ордеров
//--------------------------------------------------------------------------------------------
double 
   Lot,                                                  // Количество лотов
   Pred,                                                 // Предыдущее значение 1-й МА (розов)
   Tek,                                                  // Текущее    значение 1-й МА (розов)
   Golub;                                                // Текущее    значение 2-й МА(голубая)
//-------------------------------------------------------------------------------------------
int init()  
   {
//-------------------------------------------------------------------------------------------
   SL = SL*Point;                                        // СтопЛосс в пунктах
   TP = TP*Point;                                        // ТэйкПрофит в пунктах
   return;
//-------------------------------------------------------------------------------------------
   }  
//-------------------------------------------------------------------------------------------
int start()  
   {
//-------------------------------------------------------------------------------------------
   total=OrdersTotal();                                  // Общее количество ордеров
   if (total==2)return;                                  // Уже открыты оба ордера
//--------------------------------------------------------------------------------------------
   Tek  =iMA(NULL,0, MA1, 0,MODE_LWMA, PRICE_TYPICAL, 0);// Текущее    значение 1-й МА
   Pred =iMA(NULL,0, MA1, 0,MODE_LWMA, PRICE_TYPICAL, 1);// Предыдущее значение 2-й МА
   Golub=iMA(NULL,0, MA2, 0,MODE_LWMA, PRICE_TYPICAL, 0);// Текущее    значение 2-й МА
//--------------------------------------------------------------------------------------------
   if (Peresechenie()==1) Open_Buy();                    // Движение снизу вверх = откр. Buy
   if (Peresechenie()==2) Open_Sell();                   // Движение сверху вниз = откр. Sell
//-------------------------------------------------------------------------------------------
   return;
   }  
//-------------------------------------------------------------------------------------------
int Peresechenie()                                       // Функция определения пересечения
   {
//-------------------------------------------------------------------------------------------
   if ((Pred<=Golub && Tek> Golub) ||
       (Pred< Golub && Tek>=Golub)  ) ret=1;             // Пересечение снизу вверх       
//--------------------------------------------------------------------------------------------
   if ((Pred>=Golub && Tek< Golub) ||
       (Pred> Golub && Tek<=Golub)  ) ret=2;             // Пересечение сверху вниз
//-------------------------------------------------------------------------------------------
   return(ret);                                          // Возвращаем направление пересечен.
   }
//-------------------------------------------------------------------------------------------
int Open_Buy()                                           // Функция открытия Buy
   {
//-------------------------------------------------------------------------------------------
   if (total==1)                                         // Если ордер всего один..
      {                                                  // .. значит можно открыть другой
      OrderSelect(0, SELECT_BY_POS);                     // Выделим ордер
      if (OrderType()==0)return;                         // Если он buy, то не открываемся
      }
   OrderSend(Symbol(),0, Lots(), Ask, 0, Ask-SL, Ask+TP, "", 0, 0, Blue);// Открываемся
//-------------------------------------------------------------------------------------------
   return;
   }
//-------------------------------------------------------------------------------------------
int Open_Sell()                                          // Функция открытия Sell
   {
//-------------------------------------------------------------------------------------------
   if (total==1)                                         // Если ордер всего один..
      {                                                  // .. значит можно открыть другой
      OrderSelect(0, SELECT_BY_POS);                     // Выделим ордер
      if (OrderType()==1)return;                         // Если он sell, то не открываемся
      }
   OrderSend(Symbol(),1, Lots(), Bid, 0, Bid+SL, Bid-TP, "", 0, 0, Red);// Открываемся
//-------------------------------------------------------------------------------------------
   return;
   }
//-------------------------------------------------------------------------------------------
double Lots()                                            // Вычисление лотов
   {
//-------------------------------------------------------------------------------------------
   Lot=NormalizeDouble(AccountEquity()*Prots/100/1000,1);// Вычисляем колич. лотов  
   double Min_Lot = MarketInfo(Symbol(), MODE_MINLOT);   // Минимально допустимая стоим. лотов
   if (Lot == 0 ) Lot = Min_Lot;                         // Для теста на постоян. миним. лотах
//-------------------------------------------------------------------------------------------
   return(Lot);
   }
//-------------------------------------------------------------------------------------------

    Этот эксперт, как и предыдущие, тоже оказался незамысловатым..

    В начале эксперта содержится блок описания переменных. В функции start() определяется общее количество ордеров, а также значения МА большего и меньшего периода. В случае, если пересечение МА случилось, вызывается соответствующая функция открытия ордеров Open_*(). Для определения факта пересечения МА используется функция Peresechenie(), а для определения стоимости лотов - функция Lots().

    Наш герой просмотрел ещё раз свой эксперт, не нашёл к чему придраться ("Простенько и со вкусом!") и поставил эксперт на тестирование. После оптимизации переменных он смог получить замечательный результат:
 
 

Рис. 12. Результат тестирования эксперта Грааль_3.mq4 на истории с марта 2005 по июль 2005 года в масштабе Н1 валютного инструмента EURUSD на торговых условиях демо-сервера компании MetaQuotes Software Corp.

    Три миллиона за пять месяцев. Замечательно! Но что-то в этом графике баланса напоминало нашему герою горечь предыдущего опыта. И он решил не торопиться.
 

3.2. Прогрессия.

    После тестирования этого же эксперта при постоянной стоимости ордеров он получил вполне приличный график изменения баланса:


 

Рис. 13. Результат тестирования эксперта Грааль_3.mq4 на истории с марта 2005 по июль 2005 года в масштабе Н1 валютного инструмента EURUSD на торговых условиях демо-сервера MetaQuotes Software Corp. при постоянной стоимости лотов.
 

    Шесть тысяч пунктов за пять месяцев - это вполне ощутимо. Более тысячи пунктов в месяц! Но наш герой сомневался - ставить или не ставить эксперт на "заработки"? Ведь дважды он уже обжёгся..

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

Рис. 14. Открытие ордеров экспертом Грааль_3.mq4 в ситуациях, не предусмотренных исходной стратегией.
 

3.3. Ошибки.

    "Вот это новости! Это наверное кривой MetaEditor!", - была его первая мысль. Но постепенно, настойчиво и последовательно разбирая свой собственный код по косточкам, он снова обнаружил ошибки. Посмотрим и  мы.

3.3.1  Отступление от исходной стратегии - алгоритмические ошибки.

    Обратите внимание на простую функцию:

//-------------------------------------------------------------------------------------------
int Peresechenie()                                       // Функция определения пересечения
   {
//-------------------------------------------------------------------------------------------
   if ((Pred<=Golub && Tek> Golub) ||
       (Pred< Golub && Tek>=Golub)  ) ret=1;             // Пересечение снизу вверх       
//--------------------------------------------------------------------------------------------
   if ((Pred>=Golub && Tek< Golub) ||
       (Pred> Golub && Tek<=Golub)  ) ret=2;             // Пересечение сверху вниз
//-------------------------------------------------------------------------------------------
   return(ret);                                          // Возвращаем направление пересечен.
   }
//-------------------------------------------------------------------------------------------
    Казалось бы, всё же очевидно, что тут можно найти? Но ошибка есть. Она состоит в том, что при использовании глобальной переменной ret её значение сохраняется равным последнему вычисленному, то есть, равно 1 или 2. По этой причине функция Peresechenie() независимо от сложившейся ситуации возвращает просто последнее вычисленное значение, поэтому  в функции start() постоянно отдаётся приказ на открытие:
   if (Peresechenie()==1) Open_Buy();                    // Движение снизу вверх = откр. Buy
   if (Peresechenie()==2) Open_Sell();                   // Движение сверху вниз = откр. Sell
    Чтобы внести исправления, достаточно в начале функции Peresechenie() обнулить переменную ret. Обрадовавшись, что нашёл алгоритмическую ошибку, наш герой быстро её исправил, а заодно написал фрагмент, позволяющий открытие только на одном баре.  Теперь его эксперт выглядел так:
//-------------------------------------------------------------------------------------------
// Грааль_31.mq4.
// Используется в качестве примера в статье "Мой первый Грааль".
// Сергей Ковалёв, Днепропетровск, sk@mail.dnepr.net, ICQ 64015987, http://autograf.dp.ua/.
//-------------------------------------------------------------------------------------------
//
//
//-------------------------------------------------------------------------------------------
extern int    MA1 = 11;                                  // Период 1-й МА
extern int    MA2 = 23;                                  // Период 2-й МА
extern double TP =  50;                                  // ТэйкПрофит ордера
extern double SL =  15;                                  // СтопЛосс ордера
extern double Prots= 0;                                  // Процент от свободных средств
//--------------------------------------------------------------------------------------------
int
   New_Bar,                                              // 0/1 Факт образования нового бара
   Time_0,                                               // Время начала нового бара
   ret,                                                  // Направление пересечения
   total;                                                // Крличество открытых ордеров
//--------------------------------------------------------------------------------------------
double 
   Lot,                                                  // Количество лотов
   Pred,                                                 // Предыдущее значение 1-й МА (розов)
   Tek,                                                  // Текущее    значение 1-й МА (розов)
   Golub;                                                // Текущее    значение 2-й МА(голубая)
//-------------------------------------------------------------------------------------------
int init()  
   {
//-------------------------------------------------------------------------------------------
   SL = SL*Point;                                        // SL в пунктах
   TP = TP*Point;                                        // ТР в пунктах
   return;
//-------------------------------------------------------------------------------------------
   }  
//-------------------------------------------------------------------------------------------
int start()  
   {
//-------------------------------------------------------------------------------------------
   total=OrdersTotal();                                  // Общее количество ордеров
   if (total==2)return;                                  // Уже открыты оба ордера
 
//----------------------------------------------------------------------------- Новый бар ----
   New_Bar=0;                                            // Для начала обнулимся
   if (Time_0 != Time[0])                                // Если уже другое время начала бара
      {
      New_Bar= 1;                                        // А вот и новый бар
      Time_0 = Time[0];                                  // Запомним время начала нового бара
      } 
      
//--------------------------------------------------------------------------------------------
   Tek  =iMA(NULL,0, MA1, 0,MODE_LWMA, PRICE_TYPICAL, 0);// Текущее    значение 1-й МА
   Pred =iMA(NULL,0, MA1, 0,MODE_LWMA, PRICE_TYPICAL, 1);// Предыдущее значение 2-й МА
   Golub=iMA(NULL,0, MA2, 0,MODE_LWMA, PRICE_TYPICAL, 0);// Текущее    значение 2-й МА
   
//--------------------------------------------------------------------------------------------
   if (Peresechenie()==1 && New_Bar==1) Open_Buy();      // Движение снизу вверх = откр. Buy
   if (Peresechenie()==2 && New_Bar==1) Open_Sell();     // Движение сверху вниз = откр. Sell
//-------------------------------------------------------------------------------------------
   return;
   }  
//-------------------------------------------------------------------------------------------
int Peresechenie()
   {
   ret=0;                                                // Вот где собака зарыта!!:)
//-------------------------------------------------------------------------------------------
   if ((Pred<=Golub && Tek> Golub) ||
       (Pred< Golub && Tek>=Golub)  ) ret=1;             // Пересечение снизу вверх       
//--------------------------------------------------------------------------------------------
   if ((Pred>=Golub && Tek< Golub) ||
       (Pred> Golub && Tek<=Golub)  ) ret=2;             // Пересечение сверху вниз
//-------------------------------------------------------------------------------------------
   return(ret);                                          // Возвращаем направление пересечен.
   }
//-------------------------------------------------------------------------------------------
int Open_Buy()  
   {
   if (total==1)
      {
      OrderSelect(0, SELECT_BY_POS);                     // Выделим ордер
      if (OrderType()==0)return;                         // Если он buy, то не открываемся
      }
   OrderSend(Symbol(),0, Lots(), Ask, 0, Ask-SL, Ask+TP, "", 0, 0, Blue);// Открываемся
   return;
   }
//-------------------------------------------------------------------------------------------
int Open_Sell() 
   {
   if (total==1)
      {
      OrderSelect(0, SELECT_BY_POS);                     // Выделим ордер
      if (OrderType()==1)return;                         // Если он sell, то не открываемся
      }
   OrderSend(Symbol(),1, Lots(), Bid, 0, Bid+SL, Bid-TP, "", 0, 0, Red);// Открываемся
   return;
   }
//-------------------------------------------------------------------------------------------
double Lots()
   {
   Lot = NormalizeDouble( AccountEquity()*Prots/100/1000, 1);// Вычисляем колич. лотов  
   if (Lot<0.1)Lot = 0.1;                                // Для теста на постоян. миним. лотах
   return(Lot);
   }
//-------------------------------------------------------------------------------------------

   Тестирование этого эксперта при постоянной стоимости лотов показало куда более скромный результат:
 
  

Рис. 15. Результат тестирования эксперта Грааль_31.mq4 на истории с марта 2005 по июль 2005 года в масштабе Н1 валютного инструмента  EURUSD на торговых условиях демо-сервера MetaQuotes Software Corp. при постоянной стоимости лотов.


    У нашего героя совсем опустились руки. Столько работы, и всё напрасно. Шло время, он продолжал искать свой "грааль". Через несколько месяцев он решил вернуться к эксперту Грааль_3.mq4. "Ну, была там ошибка, но ведь он давал хороший результат. А может быть, так и надо! Пусть эксперт открывает ордера где хочет, лишь бы была польза". И он снова начал тестирование.

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

Рис. 16. Результат тестирования эксперта Грааль_3.mq4 на истории с марта 2005 по июль 2006 года в масштабе Н1 валютного инструмента  EURUSD на торговых условиях демо-сервера MetaQuotes Software Corp. при постоянной стоимости лотов.
 

3.4. Подгонка результатов.

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

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

    Поиск оптимальных настроек для эксперта Грааль_3.mq4 ни к чему не привёл. Тестирование эксперта при разных значениях внешних переменных показывало либо убыток, либо приводило к существенно разным настройкам для  разных исторических периодов. По этой причине нашему герою не удалось выделить "основную жилу" в настройках и он пришёл к выводу, что эта стратегия не имеет универсальных настроек на все случаи жизни.

    Наш герой снова вернулся к эксперту Грааль_31.mq4 и предпринял последнее отчаянное усилие, чтобы достичь желанных результатов, но лучшее, что он смог получить, выглядело так:


 

Рис. 17. Результат тестирования эксперта Грааль_31.mq4 на истории с марта 2005 по июль 2006 года в масштабе Н1 валютного инструмента  EURUSD на торговых условиях демо-сервера MetaQuotes Software Corp. при постоянной стоимости лотов.


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

    Это вселяло в нашего героя надежду на успех.
 

4. Заключение.


    Расставаться с иллюзиями всегда тяжело, особенно если они такие радужные и сладкие. Но жизнь рано или поздно всё расставляет по своим местам. Потрепала жизнь и нашего героя - он испортил отношения с двумя дилинговыми центрами, потерял некоторую сумму своих кровных.. Обретя бесценный, потому что собственный, опыт программирования и торговли на рынке Forex, наш герой едва ли не пришёл к заключению "Только Моцарт..", но вовремя остановился и решил, что больше поспешных выводов он делать не станет.

    Вместо этого он взял чистый лист бумаги и сформулировал выводы, сделанные на основе усвоенных им уроков:

  • Прогрессивное инвестирование в реальной торговле - нормальное явление. Однако, не следует допускать чрезмерно большой стоимости ордеров.
  • Не отступать от исходной стратегии.
    Если в процессе работы над экспертом выясняется, что существенное изменение настраиваемых параметров приводит к существенному изменению результатов, значит исходная стратегия нивелируется. Вполне возможно, что случайно обнаруженная новая стратегия приводит к лучшим результатам. В этом случае её следует на некоторое время отложить и обратиться  к ней позже, внимательно рассмотрев причины внезапного успеха. Но прежде следует вернуться к изначальной идее и в полной мере исследовать её на работоспособность, допуская изменение параметров лишь в осмысленных пределах.
  • Внимательно следить за алгоритмическими ошибками.
    Наличие алгоритмических ошибок вполне естественное явление. Иногда для обнаружения такой ошибки может понадобиться немалый опыт. Вместе с тем, некоторые из них можно и нужно выявлять по конечному результату работы эксперта. Слишком частые открытия ордеров или открытия "невпопад" являются ярким свидетельством алгоритмической ошибки.
  • Правильно воспринимать результаты тестирования советника в тестере стратегий
    Между работой советника на реальном счёте и моделированием этой работы в тестере стратегий имеется разница, состоящая в том, что при моделировании не учитываются  возникающие в реальной работе отказы исполнения торговых приказов и реквот. Для советников с высоким матожиданием выигрыша это не имеет большого значения, а для советников с низким матожиданием результаты реальной работы могут существенно отличаться от результатов тестирования. В ряде случаев советник с низким матожиданием, показывающий на тестере фантастическую прибыль, в реальной работе может привести к убытку.
  • Не допускать агрессивного реинвестирования.
    Любая торговая стратегия обязательно должна учитывать наличие убыточных операций. При неограниченно большой стоимости ордеров даже небольшая серия убытков может нанести непоправимый ущерб общему балансу, а в некоторых случаях привести к полной потере депозита. Допускать бесконтрольное агрессивное реинвестирование - значит добровольно обманываться, заранее и гарантированно обрекая себя на банкротство.
  • Не пытаться заработать на случайных выбросах.
    Выбросы не отражают нормальные, естественные рыночные курсы. В случае успешной работы на выбросах источником прибыли для трейдера являются денежные средства дилера. Работа на выбросах рано или поздно приводит к обнаружению этого факта дилером со всеми вытекающими последствиями - аннулированию сделок, запрету работы советников, блокированию счёта.
  • Перепроверять результаты тестирования на разных участках истории.
    Любой эксперт на разных временных периодах показывает разные результаты. Для каждого исторического интервала могут быть найдены свои оптимальные настройки, при которых результат будет наилучшим. Следует внимательно относиться  к тестированию, не заниматься самообманом (подгонкой параметров эксперта), а перепроверять полученные результаты на максимально большом количестве исторических промежутков.

SK. Днепропетровск. 2006.
Прикрепленные файлы |
Grail_1.mq4 (5.99 KB)
Grail_2.mq4 (11.58 KB)
Grail_3.mq4 (6.38 KB)
Grail_31.mq4 (5.9 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (39)
[Удален] | 2 апр. 2013 в 16:08
Зачетная статейка, за оформление и декорации 5+!!!:))
Alex Afan
Alex Afan | 21 апр. 2013 в 19:31
anakoman:

Грааль существует - ОДНОЗНАЧНО!

Участие в конкурсе трейдеров это показало.

Условия конкурса: баланс 10 000$ срок - 2 ндели (в данном случае с  3 по 14 сентября 2012г.)

Баланс победителя примерно - 200 000$

Увеличить депозит в 20 раз за 2 недели, по моему мнению, БЕЗ СОВЕТНИКА - НЕ РЕАЛЬНО.

Мой вывод - ГРААЛЬ СУЩЕСТВУЕТ!!!!!!!!!!! 


Ага, грааль - это твой мозг или сознание, которое переплетается с опытом. 

В день нужно делать около 135% от депо, на демо счёте без вывода средств это вполне реально, т.к. психическое состояние находится в норме.

Хотя использование советника тоже не исключается, и то, который правильно настроен под определённый период времени и определённую валютную пару. 

Mihail Matkovskij
Mihail Matkovskij | 22 июл. 2015 в 11:09
Значит открытие сделок на выбросах не приветствуется, потому, что это якобы приносит убыток диллеру... А когда эти самые выбросы сбивают стоп-лоссы трейдера, и он теряет свои кровные, плюс, теряет успешные сделки, это значит нормально... ? Да это свинство, при чём самое грязное! Нужно ещё разобраться, в результате чего эти выбросы возникают... Одно я знаю точно, что диллер заблокирует авто торговлю трейдера, который пытаются заработать на выбросах, однако, сам будет сбивать его лосей этими же выбросами, без всякого угрызения совести!
Tj1
Tj1 | 25 апр. 2018 в 17:06

Спасибо за статью!!


Что мешает построить не эксперта отлавливающего выбросы, а индикатор??     И просто входить в позиции в ручную на выбросах где указывает индикатор.

Konstantin Erin
Konstantin Erin | 25 апр. 2018 в 17:13
Tj1:   Спасибо за статью!!   Что мешает построить не эксперта отлавливающего выбросы, а индикатор??     И просто входить в позиции в ручную на выбросах где указывает индикатор.

Это почти одно и то же. Расчеты из индикатора можно перенести в эксперт - многие так и делают. а Вы хотите наоборот...

Секреты клиентского терминала MetaTrader 4 Секреты клиентского терминала MetaTrader 4
21 способ облегчить себе жизнь: скрытые возможности терминала МетаТрейдер 4. Полноэкранный режим; горячие клавиши; строка быстрой навигации; миминимизация окон; избранное; уменьшение трафика; отключение новостной рассылки; наборы символов; окно котировок; шаблоны для тестовых и автономных графиков; профили; перекрестие; электронная линейка; пролистывание графика побарно; история счета на графике; типы отложенных ордеров; редактирование Стоп Лосс и Тэйк Профит; отмена удаления; печать графиков.
Многократный пересчет нулевого бара в некоторых индикаторах Многократный пересчет нулевого бара в некоторых индикаторах
Статья посвящена проблеме пересчета значения индикатора в клиентском терминале MetaTrader 4 при изменении нулевого бара. В ней излагается общая идея добавления в код индикатора дополнительных программных элементов, позволяющих восстанавливать состояние програмного кода, сохраненное до многократного пересчета.
Десять основных ошибок начинающего трейдера Десять основных ошибок начинающего трейдера
Десять основных ошибок начинающего трейдера: торговля на открытии рынка, поспешность в снятии прибыли, добавление к убыточной позиции, закрытие позиций начиная с лучшей, жажда мести, наличие особо предпочтительных позиций, торговля по приципу 'купил навсегда', закрытие прибыльной стратегической позиции в первый день, закрытие позиции по сигналу на открытие противоположной позиции, сомнения.
Использование крешлогов для отладки собственных dll Использование крешлогов для отладки собственных dll
25-30% всех крешлогов, поступающих от пользователей, возникают в результате ошибок выполнения функций, импортируемых из пользовательских dll.