Почему не работает конструкция if(Volume()==1) для работы «по ценам открытия». Исследование.

 
Краткая история.
Занимаясь разработкой, я для себя сделал вывод, что наиболее оптимально оценивать работу эксперта и проводить его оптимизацию «по ценам открытия».
 Это, конечно, не охватывает весь спектр возможных стратегий, но:
- позволяет в конечные сроки выработать стратегию при недостаточном количестве входных данных;
- позволяет быстро оптимизировать стратегию при большом количестве изменяемых параметров;
- позволяет в конечное время выбрать лучшую стратегию и определить критерии качества для их оценки;
- позволяет контролировать выполнение эксперта при помощи параллельного прохождения в тестере стратегий;
- позволяет быстро проводить дооптимизацию при необходимости.

Исследование.
Наиболее простая и очевидная конструкция контроля «по ценам открытия».

int start()
{
   if(Volume[0]==1)
   {
	//обработка события, выставление ордеров, другая логика … 
   }
}



Она идеально работает в тестере стратегий.



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



Сначала я грешил, что это происходит из-за реквот или отсутствия связи и потратил много времени для улучшения процедур выставления ордеров. Но это практически не помогло. Часть ордеров, по-прежнему, не выставлялась.



Тогда я добавил отладочную операцию.



int start()
{
   if(Volume[0]==1)
   {
	Print(TimeHour(TimeCurrent());//я работаю с интервалами не мене часа
	//обработка события, выставление ордеров, другая логика … 
   }
}



и увидел, что виной оказалась конструкция if(Volume[0]==1)

Это меня несколько удивило, и я написал маленький эксперт,



void start() 
{
Print("Volume = ",Volume[0]);
}

который и подтвердил мою догадку:

- значения Volume, которые получает терминал на каждом тике, не всегда начинаются с 1;

- значения Volume содержат разрывы.

То есть, последовательность Volume с начала бара может иметь такой вид: 2,5,6,7,10,11,12…

Что же, не беда большинство пользуются другой конструкцией:





datetime	LastTime=0;
int start()
{
   if(Time[0] != LastTime) 
   {
      LastTime = Time[0]; 
      Print(TimeHour(TimeCurrent());
   }
}

И действительно эксперт перестал пропускать начало бара. Ура!



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

Что же, не будем унывать. Простейший способ – запускать терминал и перекомпилировать советник приблизительно на начале нового бара или … сделать еще одну небольшую доработку.



datetime	LastTime=0;
int init()
{
   LastTime = Time[0]; 
}
int start()
{
   if(Time[0] != LastTime) 
   {
      LastTime = Time[0]; 
      Print(TimeHour(TimeCurrent());
   }
}

Так я и сделал и приступил к новому этапу тестирования.

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

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

Ну, раз причина ясна, – приступим к ее устранению.

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



IsTradeContextBusy()

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

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





external int StartTick=5;
datetime	LastTime=0;
int init()
{
   LastTime = Time[0]; 
}
int start()
{
   if(Time[0] != LastTime &&   Volume[0]>=StartTick) 
   {
      LastTime = Time[0]; 
      Print(TimeHour(TimeCurrent());
   }
}

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

Например.

Expert1:

external int StartTick=1;

Expert2:

external int StartTick=5;

Expert3:

external int StartTick=10;





Зачем же тогда external, спросите вы, достаточно int StartTick=5.

Можно и так, но тогда мы не сможем протестировать советник в режиме
«по ценам открытия», ведь тестер генерирует только Voleme[0]=1. Соответственно для тестирования эту переменную нужно выставить в окне «Свойства эксперта» в закладке «входные параметры» в StartTick=1. Или дополнить код:



external int StartTick=5;
datetime	LastTime=0;
int init()
{
   LastTime = Time[0]; 
}
int start()
{
   if(IsTesting()==true) StartTick=1;
   if(Time[0] != LastTime &&   Volume[0]>=StartTick) 
   {
      LastTime = Time[0]; 
      Print(TimeHour(TimeCurrent());
   }
}

Вот собственно и все. Тестируется уже более месяца. Проблем нет. Идентичность с тестером стратегий почти полная.



И в заключении, несколько слов о функциях выставления ордеров. Очень сложные и подробные функции есть у KimIV, но я пользуюсь более простыми.

Например:




int OrderBUY()
{  
   for(int i=0; i<10; i++)
   {
      for(int slp=0; slp<600; slp++) if(IsTradeContextBusy() == true)Sleep(100);else break;
      RefreshRates();
      int OrdRet=   OrderSend(Symbol(),OP_BUY,Lots,  Ask, Slip,  Ask-(InitialStop *Point),
                    Ask+(TakeProfit*Point), "ExpertName",MAGIC_NUMBER,0,Green);
      if(OrdRet<0)Print("ERROR = ",GetLastError());else break;
   }
}
Цель этой функции не выставить ордер, во что бы то ни стало, а уменьшить влияние реквот и кратковременного пропадания связи не зайдя при этом в бесконечный цикл, не делая длительных пауз и не завесив терминал. 

Функции для SELL и отложенных ордеров аналогичны.




 
thecore >>:
и увидел, что виной оказалась конструкция

А что непонятно? За время работы функции Start пришло несколько тиков -- все поломалось. Тема уже избита.

Насчет Ваших проблем:

datetime LastTime;
bool IsFirstRun;

int init()
{
   LastTime = 0;
   IsFirstRun = true;
}

int start()
{
   if(Time[0] == LastTime) 
   {
       return(0);
   }

   LastTime = Time[0]; 

   if(IsFirstRun)
   {
       IsFirstRun = false;
       return(0);
   }

   Print(TimeHour(TimeCurrent());
   // нужная обработка
}

Мой бай -- что-то непонятно?

void OpenBuy(int MN, int Target, int Loss, double Lot)
{
   int count = 0;
   while (count < TimesToRepeat)
   {
      WaitForContext();
      RefreshRates();
   
      double TP = DoubleIf(Target > 0, Ask + Target*Point, 0);
      double SL = DoubleIf(Loss > 0, Ask - Loss*Point, 0);
   
      double LotsToBid = DoubleIf(Lot == 0, GetLotsToBid(RiskPercentage), Lot);
   
      int res = OrderSend(Symbol(), OP_BUY, LotsToBid, Ask, Slippage, SL, TP, NULL, MN, 0, Blue);
      
      if (res > 0) return;
   }
}
 
для безопасного программировнаия следует избегать жестких равенств, которые могут повлечь пропуск события например из за неодинаковых данных отразных источников
т.е. безопасно
Volume[0]>=1
 
Korey >>:
для безопасного программировнаия следует избегать жестких равенств, которые могут повлечь пропуск события например из за неодинаковых данных отразных источников
т.е. безопасно
Volume[0]>=1

перезагрузите пост. Я просто не успел все вставить и нажал сохранить.

 

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

 
TheXpert >>:

А что непонятно? За время работы функции Start пришло несколько тиков -- все поломалось. Тема уже избита.

Насчет Ваших проблем:

Мой бай -- что-то непонятно?

datetime LastTime;
bool IsFirstRun;

int init()
{
   LastTime = 0;
   IsFirstRun = true;
}

int start()
{
   if(Time[0] == LastTime) 
   {
       return(0);
   }

   LastTime = Time[0]; 

   if(IsFirstRun)
   {
       IsFirstRun = false;
       return(0);
   }

   Print(TimeHour(TimeCurrent());
   // нужная обработка
}

Эта конструкция, как я и писал, не работает если в терминале стоит несколько советников на одном инструменте.

Если эти советники попытаются одновременно выставить ордера терминал виснет (привет Metaquotes).

 
TheXpert >>:

А что непонятно? За время работы функции Start пришло несколько тиков -- все поломалось. Тема уже избита.

Нет не "За время работы функции Start пришло несколько тиков". 

Я проверял и на быстром рынке и на медленно.

Тиков с определенными номерами вообще НЕБЫЛО.

Не то что бы их не было в природе, они, возможно, были у ДЦ,  просто они не попали в мой терминал.

Кстати, я не вижу в этом ничего плохого или странного.

Я просто провел исследование и предложил выход из положения.

Естественно один из возможных выходов.

 

Как вариант, можно предусмотреть контроль и выставление флага в глобальных переменных. Первый успел, захватил флаг. Закончил обработку, очистил. Следующий так же поступает.

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

 
Vinin >>:

Как вариант, можно предусмотреть контроль и выставление флага в глобальных переменных. Первый успел, захватил флаг. Закончил обработку, очистил. Следующий так же поступает.

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

Можно, но тогда нужно провести еще одно исследование относительно скорости

обработки глобальных переменных терминалом в привязке с одновременно выполняющимися

советниками. Ведь они выполняются "параллельно" в разных потоках. И тут уже вылазят 

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

скорость процессора и т.п.

Все было бы проще, если бы советники выполнялись по очереди.

Пришел тик - выполнился первый советник, затем второй и т.д.

Тогда и этой темы не было бы.

Но старую добрую архитектуру Big Loop сейчас программисты не уважают.

Им подавай видимость "реального" параллелизма. Но параллелизм то у нас 

очень условный, даже если стоит N процессоров. Поэтому и появляются зависания

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

(Извините, это у меня отголоски моей основной работы.)


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

 
thecore >>:

Эта конструкция, как я и писал, не работает если в терминале стоит несколько советников на одном инструменте.

Если эти советники попытаются одновременно выставить ордера терминал виснет (привет Metaquotes).

 WaitForContext();

Ничо не знаю, у мну по 10 советников тестируется без проблем.

 
TheXpert >>:

Ничо не знаю, у мну по 10 советников тестируется без проблем.

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

Но после того, как пару раз съездил в другой город к заказчику, уволился и со временем завязал с программированием.

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