Получение ID последней закрытой позиции

 

Доброго времени суток!

Столкнулся с багом в своей программе. Грешу на функцию получения ID последней закрытой позиции. Прошу помощи, что не так?

По сути мне необходим только ID, остальное второстепенно. 

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

      //+------------------------------------------------------------------+
      //| Запрашивает историю за последние дни и вернет false при неудаче  |
      //+------------------------------------------------------------------+
      bool getTradeHistory(int days)
      {
         //--- зададим недельный период времени для запроса торговой истории
         datetime to = TimeCurrent();
         datetime from = to - days * PeriodSeconds(PERIOD_D1);
         ResetLastError();
         //--- сделаем запрос и проверим результат
         if(!HistorySelect(from, to)) {
            Print(__FUNCTION__, " Невозможно получить историю закрытых ордеров. Error code=", GetLastError());
            return false;
         }
         //--- история получена успешно
         return true;
      }

      //+------------------------------------------------------------------+
      //| Получение последней закрытой позиции                             |
      //+------------------------------------------------------------------+
      bool getLastClosedPosition(int days)
      {
         // очистим массив
         ArrayFree(pp);
         // сбросим предыдущие ошибки
         ResetLastError();
         if (getTradeHistory(7)) {
            // получаем количество сделок в истории
            int total = HistoryDealsTotal();
            // уменьшим на единицу для правильного обращения к тикету последней сделки
            total--;
            // берём тикет последней сделки
            long ticket = (long)HistoryDealGetTicket(total);
            ArrayResize(pp,1);
            // если получили тикет, то получаем данные по нему
            if(ticket > 0) {
               pp[0].symbol             =HistoryDealGetString(ticket,DEAL_SYMBOL);// символ
               pp[0].id                 =HistoryDealGetInteger(ticket,DEAL_POSITION_ID);// id
               pp[0].ticket             =pp[0].id;// тикет (присваиваем id т.к. тикет закрытого ордера не равен ордеру открытой позиции и можно будет запутаться в общем файле для записи)
               pp[0].opening_time       =HistoryDealGetInteger(ticket,DEAL_TIME);// время закрытия
               pp[0].type               ="POSITION_CLOSED";//тип
               pp[0].volume             =HistoryDealGetDouble(ticket,DEAL_VOLUME);// объём
               pp[0].opening_price      =HistoryDealGetDouble(ticket,DEAL_PRICE);// цена открытия
               pp[0].stop_loss          =0;// S/L
               pp[0].take_profit        =0;// T/P
               pp[0].magic              =pp[0].id;// назначим MagicNumber по номеру тикета
            } else {
               return false;
            }
            //PrintFormat("Символ %s | Тикет %d | ID %d | Тип %s | MN %d",
            //               pp[0].symbol,pp[0].ticket,pp[0].id,pp[0].type,pp[0].magic);
         }
         return true;
      }


 

 
Dmitriy Svechnikov:

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

Вот более точное описание:

Каждой записи в файле об открытии позиции должна соответствовать своя запись о закрытии. Как проверяю - сравниваю номера (id позиции), 2,3 и последний столбики, в которые я пишу эти самые id (немного некрасиво и избыточно, но в будущем думаю переделать). Чтобы не сбиваться и было наглядно я эти самые записи закрашиваю одинаковым цветом.

Красными рамками выделил проблемные места, а именно - дублирование закрывающихся записей, из-за чего отсутствует ещё одна необходимая закрывающая для белых записей (4 и 10 линии). Такое ощущение будто сдвиг какой по индексам при обращении к спискам в истории.

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

Отсюда я их вызываю:

void OnTradeTransaction(const MqlTradeTransaction& trans,
                        const MqlTradeRequest& request,
                        const MqlTradeResult& result)
  {
     if (CopierMode == MASTER) {
        if(trans.type == TRADE_TRANSACTION_DEAL_ADD) {
           if(!HistoryDealSelect(trans.deal))
              Print(GetLastError());
              if(HistoryDealGetInteger(trans.deal, DEAL_ENTRY) == DEAL_ENTRY_IN) {
                 Print("Позиция открылась!");
                 if (CT.getLastOpenPosition()) CT.WriteFile();
              }
              if(HistoryDealGetInteger(trans.deal, DEAL_ENTRY) == DEAL_ENTRY_OUT) {
                 Print("Позиция закрылась!");
                 if (CT.getLastClosedPosition(7)) CT.WriteFile();
              }
        }
     }
  }

Уже минимум неделю ломаю голову. Не знаю в чём может быть дело. 

 

а что с остальным кодом? вы всегда только последнюю сделку смотрите? Не самое удачное решение. Вы её откуда вызываете, из ОнТрейда наверное. Две записи потому что две сделки (точно не помню, в случае СЛ или ТП) - одна по размещению ордера закрывающего, вторая - исполнение этого ордера.

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

 
Aleksey Mavrin:

а что с остальным кодом? вы всегда только последнюю сделку смотрите? Не самое удачное решение. Вы её откуда вызываете, из ОнТрейда наверное. Две записи потому что две сделки (точно не помню, в случае СЛ или ТП) - одна по размещению ордера закрывающего, вторая - исполнение этого ордера.

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

Это весь код в рамках данного вопроса. Метод getLastClosedPosition(int days) должен получать id последней закрытой позиции из истории и заталкивать в структуру (её свойства можно увидеть как раз в данном методе). Метод CT.WriteFile() записывает данные из структуры в файл. На этом вся логика. 

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

Пишу свой копировальщик сделок между двумя терминалами MT5. Смотрю только последнюю сделку чтобы понять какая позиция была закрыта на Мастере чтобы передать данные для закрытия дублирующей позиции в Слейве.

 

Вроде исправил. 3 теста прогнал, всё идеально. Завтра утром на свежую голову ещё несколько прогонов тестовых сделаю. 

Добавил такую конструкцию из примера в документации:

if (total > 0) {
   if (total > 1){
      ticket = (long)HistoryDealGetTicket((int)total - 1);
   } else {
      ticket = (long)HistoryDealGetTicket(0);  
   }
}

Вместо этого куска кода:

// уменьшим на единицу для правильного обращения к тикету последней сделки
total--;
// берём тикет последней сделки
long ticket = (long)HistoryDealGetTicket(total);
 
Dmitriy Svechnikov:

Вроде исправил. 3 теста прогнал, всё идеально. Завтра утром на свежую голову ещё несколько прогонов тестовых сделаю. 

Добавил такую конструкцию из примера в документации:

Вместо этого куска кода:

А если total будет равен 1, разве total-1 не будет равен 0?

if (total > 0) {
   if (total > 1){
      ticket = (long)HistoryDealGetTicket((int)total - 1);
   } else {
      ticket = (long)HistoryDealGetTicket(0);  
   }
}

Зачем такой заворот кишок?

 
Alexey Viktorov:

А если total будет равен 1, разве total-1 не будет равен 0?

Зачем такой заворот кишок?

Я сам ничего не понял. Но считаю аналогично. Вчера вечером подумал, что голова уже не варит. Но благодаря вашему комментарию я понимаю, что был прав)) Однако творится что-то непонятное. Ох уж эта магия MQL5. 

Сейчас сделал ещё один прогон - всё пишется зеркально без всяких ошибок. 

 

А вот теперь кажется понял, почему работало криво.

void OnTradeTransaction(const MqlTradeTransaction& trans,
                        const MqlTradeRequest& request,
                        const MqlTradeResult& result)
  {
     if (CopierMode == MASTER) {
        if(trans.type == TRADE_TRANSACTION_DEAL_ADD) {
           if(!HistoryDealSelect(trans.deal))
              Print(GetLastError());
              if(HistoryDealGetInteger(trans.deal, DEAL_ENTRY) == DEAL_ENTRY_IN) {
                 Print("Позиция открылась!");
                 if (CT.getLastOpenPosition()) CT.WriteFile();
              }
              if(HistoryDealGetInteger(trans.deal, DEAL_ENTRY) == DEAL_ENTRY_OUT) {
                 Sleep(1000);
                 Print("Позиция закрылась!");
                 if (CT.getLastClosedPosition(7)) CT.WriteFile();
              }
        }
     }
  }

Разгадка тайны кроется в Sleep(1000);

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

 

Секундная пауза? Ничего себе костыль.

Лучше ждать, пока сделка не появится в истории в цикле со Sleep(1).

 

Хм, а разве ОнТрейд как раз и не предназначен для контроля списка. ОТТ генерируется при транзакции на сервере, а ОТ генерится при изменении списков, в т.ч. исторических. Ну дело ваше, кому какой костыль милее, здоровых мало осталось)

 
Aleksey Mavrin:

Хм, а разве ОнТрейд как раз и не предназначен для контроля списка. ОТТ генерируется при транзакции на сервере, а ОТ генерится при изменении списков, в т.ч. исторических. Ну дело ваше, кому какой костыль милее, здоровых мало осталось)

Событие Trade генерируется при завершении торговой операции на торговом сервере.

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

Документация по MQL5: Программы MQL5 / События клиентского терминала
Документация по MQL5: Программы MQL5 / События клиентского терминала
  • www.mql5.com
Сразу же после того, как клиентский терминал загрузит программу (эксперт или пользовательский индикатор) и запустит процесс инициализации глобальных переменных, будет послано событие Init, которое обрабатывается функцией OnInit(), если она есть. Это событие также генерируется после смены финансового инструмента и/или периода графика, после...
Причина обращения: