Организация цикла перебора ордеров - страница 9

 
fxsaber:

Изменил

Теперь без алертов.

Ответ напрашивается сам - если Ticket == PrevTicket ---> return WRONG_VALUE.

Если функция вернула значение меньше ноля, значит нужно её ещё раз вызвать.

 
Artyom Trishkin:

Ответ напрашивается сам - если Ticket == PrevTicket ---> return WRONG_VALUE.

Так это решение частной случая проблемы, который описал для наглядности в виде равенства соседних тикетов.

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

Альтернативу IsChange не нашел.

 
fxsaber:

Так это решение частной случая проблемы, который описал для наглядности в виде равенства соседних тикетов.

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

Альтернативу IsChange не нашел.

Создаём в таймере список и работаем с ним.

 
Artyom Trishkin:

Создаём в таймере список и работаем с ним.

Лучше кодом.

 
fxsaber:

Лучше кодом.

Кодом показать очень сложно - там целая библиотека взаимосвязанных классов.

Смысл такой - в таймере проходим по всем ордерам и позициям на счёте и заполняем список CArrayObj. Постоянно его обновляем для получения актуальной информации за один проход.

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

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

 
Artyom Trishkin:

Ответ напрашивается сам - если Ticket == PrevTicket ---> return WRONG_VALUE.

Если функция вернула значение меньше ноля, значит нужно её ещё раз вызвать.

Абсолютно ничего делать не надо. Достаточно пройти по логике с видом идиота.

1. Имеем 6 ордеров

  • 0 тикет 100
  • 1 тикет 101
  • 2 тикет 102
  • 3 тикет 103
  • 4 тикет 104
  • 5 тикет 105

Начинаем перебирать ордера для, скажем модификации,

Выбрали 5 тикет 105, проверили надо-ли модифицировать, модифицировали.

В это время шаловливые ручки удалили ордер 2 тикет 102 и список изменился. Теперь ордер с тикетом 105 стал в списке четвёртым, 4. И опять получается выбран для модификации. Но ведь без проверки не работаем... проверили надо-ли модифицировать, НО!!! НЕ надо... и что??? Кому стало плохо от того что ордер выбран повторно?

 
Artyom Trishkin:

Кодом показать очень сложно - там целая библиотека взаимосвязанных классов.

Смысл такой - в таймере проходим по всем ордерам и позициям на счёте и заполняем список CArrayObj. Постоянно его обновляем для получения актуальной информации за один проход.

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

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

Вот реализация этого

Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий

Организация цикла перебора ордеров

fxsaber, 2017.09.11 20:29

// Редкий, но правильный костяк модификации ордеров
for (int i = OrdersTotal() - 1; i >= 0; i--)
  if (OrderSelect(i, SELECT_BY_POS))
    if (OrderModify(OrderTicket(), Price, SL, TP, OrderExpiration()))     
    {
      i = OrdersTotal(); // Хотя бы так
      
      // А лучше так
//      OnTick(); break; // вместо строки выше лучше делать такой вызов (переполнения стека от рекурсивных вызовов быть не должно)
    }


После отправки торгового приказа меняется торговое окружение, поэтому желательно сразу после ответа торгового сервера выполнять с нуля всю торговую логику ТС.

Только она все равно требует IsChange. Таймер - это совсем не вариант. Даже Sleep(1) портит всю картину.

 
Alexey Viktorov:

Абсолютно ничего делать не надо. Достаточно пройти по логике с видом идиота.

Таки да, не проблема если проверяется закрытие/активация ордера.
Ну и если свой массив тикетов, то ордер просто пропустится.

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

 
fxsaber:

Вот реализация этого

Только она все равно требует IsChange. Таймер - это совсем не вариант. Даже Sleep(1) портит всю картину.

IsChange() как раз и реализован в таймере (пока не доделанная тестовая версия):

//+------------------------------------------------------------------+
//| Обновляет список ордеров                                         |
//+------------------------------------------------------------------+
int CMarketCollection::Refresh(void)
  {
   ::ZeroMemory(m_struct_market);
   int number_new=0, total=::OrdersTotal();
   m_list_all_orders.Clear();
   for(int i=0; i<total; i++){
      if(!::OrderSelect(i,SELECT_BY_POS)) continue;
      long ticket=::OrderTicket();
      m_struct_market.hash_sum_acc+=ticket;
      m_struct_market.total_volumes+=::OrderLots();
      ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)::OrderType();
      if(type==ORDER_TYPE_BUY || type==ORDER_TYPE_SELL){
         CMarketOrder* order=new CMarketOrder();
         if(order==NULL) continue;
         m_list_all_orders.InsertSort(order);
         m_struct_market.total_positions++;
         }
      else{
         CMarketPending* order=new CMarketPending();
         if(order==NULL) continue;
         m_list_all_orders.InsertSort(order);
         m_struct_market.total_pending++;
         }
      }
   //--- Первый запуск
   if(m_hash_sum_acc_prev==WRONG_VALUE){
      m_hash_sum_acc_prev=m_struct_market.hash_sum_acc;
      m_total_positions_prev=m_struct_market.total_positions;
      m_total_pending_prev=m_struct_market.total_pending;
      m_total_volume_prev=m_struct_market.total_volumes;
      }
   //---
   if(m_struct_market.hash_sum_acc!=m_hash_sum_acc_prev){
      number_new=(m_struct_market.total_pending+m_struct_market.total_positions)-(m_total_positions_prev+m_total_pending_prev);
      Print(FUNC,"Хэш-сумма всех ордеров и позиций изменилась");
      //--- Увеличисля общий объём
      if(::NormalizeDouble(m_struct_market.total_volumes-m_total_volume_prev,3)>0){
         Print(FUNC,"Общий объём увеличился");
         if(m_struct_market.total_positions>m_total_positions_prev) Print(FUNC,"Количество позиций увеличилось");
         if(m_struct_market.total_pending>m_total_pending_prev) Print(FUNC,"Количество ордеров увеличилось");
         //--- Отправка EVENT в управляющий класс CEngine
         // сделать!
         }
      //--- Уменьшился общий объём
      else if(::NormalizeDouble(m_struct_market.total_volumes-m_total_volume_prev,3)<0){
         Print(FUNC,"Общий объём уменьшился");
         if(m_struct_market.total_positions<m_total_positions_prev) Print(FUNC,"Количество позиций уменьшилось");
         if(m_struct_market.total_pending<m_total_pending_prev) Print(FUNC,"Количество ордеров уменьшилось");
         //--- Отправка EVENT в управляющий класс CEngine
         // сделать!
         }
      else{
         // что-то ещё, не пойму пока что именно - не было претендентов ещё
         }
      //---
      m_hash_sum_acc_prev=m_struct_market.hash_sum_acc;
      m_total_positions_prev=m_struct_market.total_positions;
      m_total_pending_prev=m_struct_market.total_pending;
      m_total_volume_prev=m_struct_market.total_volumes;
      }
   //---
   //---
   return number_new;
  }
//+------------------------------------------------------------------+

В управляющем классе можно поймать изменение либо по числу, возвращаемому из Refresh() обоих классов (в тестере):

//+------------------------------------------------------------------+
//| Таймер                                                           |
//+------------------------------------------------------------------+
void CEngine::OnTimer(void)
  {
   //--- Обновление списка исторических ордеров и позиций
   m_new_history=History.Refresh();
   if(m_new_history>0){
      Print(FUNC,"Изменение в исторических на ",m_new_history);
      //--- реакция
      }
   //--- Обновление списка рыночных ордеров и позиций
   m_new_market=Market.Refresh();
   if(m_new_market!=0){
      Print(FUNC,"Изменение в активных на ",m_new_market);
      //--- реакция
      }
   //---
   Sym.OnTimer();
  }
//+------------------------------------------------------------------+

либо в пользовательском событии, которые ещё не реализованы (демо, реал).

 
Artyom Trishkin:

IsChange() как раз и реализован в таймере (пока не доделанная тестовая версия):

Для чего, если IsChange - это пять строк?