Хранение истории ордеров

 

Здравствуйте. Для хранения истории ордеров использую следующую конструкцию, логику которой когда-то подсмотрел здесь на форуме.

int lastOrdersHistTotal;

int OnInit()
  {
   lastOrdersHistTotal = 0; // Пускай будет
   
   return(INIT_SUCCEEDED);
  }

bool updateOrdersHistory() // Сортировка по цене открытия
// Должна вызываться с каждым тиком
  {
   for(int index = lastOrdersHistTotal; index < OrdersHistoryTotal(); index++)
     {
      // Выбираем ордер, обновляем lastOrdersHistTotal
      if(!OrderSelect(index, SELECT_BY_POS, MODE_HISTORY))
        {
         PrintFormat("%sНе удалось выбрать закрытый или удаленный ордер. Ошибка %i. Строка %i.", NOTIF_LABEL, GetLastError(), __LINE__);
         
         return(false);
        }
      
      else lastOrdersHistTotal = index + 1;
      
      // Это наш ордер
      if(OrderSymbol() != Symbol() || OrderMagicNumber() != magic)
         continue;
      
      // Определяем индекс, на котором будет расположен этот ордер
      int arrayIndex;
      
      for(arrayIndex = ArraySize(ordersHist) - 1; arrayIndex >= 0; arrayIndex--)
         if(OrderOpenTime() >= ordersHist[arrayIndex].opTime)
            break;
      
      arrayIndex++;
      
      // Добавляем новый элемент в массив
      ArrayResize(ordersHist, ArraySize(ordersHist) + 1, ORDERS_HIST_ARR_RES_SIZE);
      
      // Если индекс занят, сдвигаем на 1 вправо элементы, начиная с этого индекса
      for(int i = ArraySize(ordersHist) - 1; i > arrayIndex; i--)
        {
         ordersHist[i] = ordersHist[i - 1];
         ordersHist[i] = ordersHist[i - 1];
        }
      
      // Записываем свойства добавляемого ордера
      // ...
     }
   
   return(true);
  }

Я вот о чем подумал. У пользователя стоял период истории счета "вся история". Допустим, в истории было 1000 ордеров. Потом ему захотелось поставить период истории счета "за последнюю неделю". После чего в истории стало 100 ордеров.

Когда было 1000 ордеров, соответственно значение переменной lastOrdersHistTotal равнялось 1000. Теперь OrdersHistoryTotal() вернет 100. Условие цикла index < OrdersHistoryTotal() больше не выполняется, новые ордера в массив не добавляются.

При смене периода истории счета пользователем в терминале, события init и deinit не генерируются.

Как избежать такой ситуации?

 
Vladislav Boyko:

Здравствуйте. Для хранения истории ордеров использую следующую конструкцию, логику которой когда-то подсмотрел здесь на форуме.

Я вот о чем подумал. У пользователя стоял период истории счета "вся история". Допустим, в истории было 1000 ордеров. Потом ему захотелось поставить период истории счета "за последнюю неделю". После чего в истории стало 100 ордеров.

Когда было 1000 ордеров, соответственно значение переменной lastOrdersHistTotal равнялось 1000. Теперь OrdersHistoryTotal() вернет 100. Условие цикла index < OrdersHistoryTotal() больше не выполняется, новые ордера в массив не добавляются.

При смене периода истории счета пользователем в терминале, события init и deinit не генерируются.

Как избежать такой ситуации?

Выходов много: 1) очищать, обнулять, сбрасывать хранилище каждый раз  2) отказаться от хранения истории, потому что а) историю легко добыть из истории б) ну какая разница для будущей торговли что пол-месяца назад была совершена прибыльная или убыточная сделка? Трейдер набрался опыта, робот оптимизирован и получил другие параметры   3) смотреть дату последней сделки в хранилище и добавлять свежие, ориентируясь по дате вместо количества
 
Дед пенсионер:
Выходов много: 1) очищать, обнулять, сбрасывать хранилище каждый раз  2) отказаться от хранения истории, потому что а) историю легко добыть из истории б) ну какая разница для будущей торговли что пол-месяца назад была совершена прибыльная или убыточная сделка? Трейдер набрался опыта, робот оптимизирован и получил другие параметры   3) смотреть дату последней сделки в хранилище и добавлять свежие, ориентируясь по дате вместо количества

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

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

2.б) В моем случае разница есть.

3) Дату открытия или закрытия? Как узнать какие ордера являются свежими, используя дату последнего ордера в хранилище? Ордера в истории ордеров (account history) сортируются (могут быть отсортированы) неизвестным образом. 

 
Vladislav Boyko:

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

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

2.б) В моем случае разница есть.

3) Дату открытия или закрытия? Как узнать какие ордера являются свежими, используя дату последнего ордера в хранилище? Ордера в истории ордеров (account history) сортируются (могут быть отсортированы) неизвестным образом. 

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

 
Очень просто. Добавлять в свою БД ордера из истории счета, которые имеют время закрытия, большее, чем время закрытия последнего ордера в БД. 
 
Ihor Herasko:
Очень просто. Добавлять в свою БД ордера из истории счета, которые имеют время закрытия, большее, чем время закрытия последнего ордера в БД. 

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

Vitaly Muzichenko:

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

Спасибо. Есть похожая идея. Подумаю, потестирую и напишу в эту ветку.

 
Vladislav Boyko:

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

Спасибо. Есть похожая идея. Подумаю, потестирую и напишу в эту ветку.

У меня это сделано немного по-другому, Я не вызываю на каждом тике, а только при изменении количества в истории/текущие. Если изменилось количество, то вызываем функцию, иначе нет смысла дергать на каждом тике

Примерно так:

 int HT=OrdersHistoryTotal();

  if(HT != TotalHist) { // Если изменилось количество
    TotalHist=HT; // Запомним новое значение
    GetHistory(); // Функция для заполнения
  }
 
Vitaly Muzichenko:

У меня это сделано немного по-другому, Я не вызываю на каждом тике, а только при изменении количества в истории/текущие. Если изменилось количество, то вызываем функцию, иначе нет смысла дергать на каждом тике

Примерно так:

В GetHistory() каждый раз заново история заполняется, или только добавляются новые ордера?

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

 
Vladislav Boyko:

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

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

О затратах времени на пробег по истории.

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

2018.02.28 00:00:11 Перед HistorySelect(0,TimeCurrent()) мс=0
2018.02.28 00:00:11 Перед ArrayResize(CB,i+1) мс=0
2018.02.28 00:00:11 Перед циклом занесения 428 тикетов мс=15
2018.02.28 00:00:11 После цикла занесения и упорядочивания 428 тикетов, мс=15
2018.02.28 00:00:11 После цикла заполнения всех свойств 428 тикетов, мс=31
2018.02.28 00:00:11 Перед плавающей прибылью мс=31
2018.02.28 00:00:11 Все, стейтмент записан на диск мс=78

2018.03.01 00:00:13 Перед HistorySelect(0,TimeCurrent()) мс=0
2018.03.01 00:00:13 Перед ArrayResize(CB,i+1) мс=0
2018.03.01 00:00:13 Перед циклом занесения 428 тикетов мс=0
2018.03.01 00:00:13 После цикла занесения и упорядочивания 428 тикетов, мс=16
2018.03.01 00:00:13 После цикла заполнения всех свойств 428 тикетов, мс=32
2018.03.01 00:00:13 Перед плавающей прибылью мс=32
2018.03.01 00:00:13 Все, стейтмент записан на диск мс=32

В первом случае видно, что работа скрипта приостанавливалась принудительно планировщиком потоков. Помню, ставил эксперименты по генерации стейтментов для 36 тысяч сделок, вышло намного меньше секунды (350 мс, кажется). Цифры 15-16, 32 характеризуют шаг системного таймера.

 
Vladislav Boyko:

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

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

Отображение в закладке "История счета" влияет только на количество ордеров, которое будет получено в программе, но не на порядок их следования.

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