MT5 и скорость в боевом исполнении - страница 20

 
fxsaber:

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

CHashMap<ulong, ulong> DealsIn;  // По PositionID возвращает DealIn.


Это не кэширование, а индекс. Вот кэшировани (часть кода):

class DealInfo {
public:
  datetime Closed;
  ulong Ticket;
  ulong Position;
  string Symbol;
  long Type;
  long Reason;
  double Volume;
  double Price;
  double Profit;
  double Swap;
  long Magic;
};

class DealsComparer : public IComparer<DealInfo*> {
  int Compare(DealInfo* x, DealInfo* y) {
    int res = (int)(x.Closed - y.Closed);
    if (res == 0) {
      res = (int)(x.Ticket - y.Ticket);
    }
    
    return res;
  }
};

CArrayList<DealInfo*> dealsHistory;

inline bool UpdateDeals(CArrayList<DealInfo*> &deals, datetime &lastUpdated) {
  DealInfo* dealsToAdd[];
  DealsComparer comparer;
  int toAdd = 0;
  DealInfo* deal;
  
  if (!HistorySelect(lastUpdated, TimeLocal() + 12*3600)) {
    return false;
  }

  for (int i = 0, total = HistoryDealsTotal(); i < total; i++) {
    DealInfo tmp;
    ulong ticket = HistoryDealGetTicket(i);
    if (ticket == 0) continue;
    
    datetime dt = (datetime)HistoryDealGetInteger(ticket, DEAL_TIME);
    if (lastUpdated < dt) {
      lastUpdated = dt;
    }

    if (HistoryDealGetInteger(ticket, DEAL_ENTRY) != DEAL_ENTRY_OUT) continue;

    ulong reason = HistoryDealGetInteger(ticket, DEAL_REASON);

    tmp.Ticket = ticket;
    tmp.Closed = dt;
    int idx = deals.BinarySearch(&tmp, &comparer);
    if (idx >= 0 && deals.TryGetValue(idx, deal) && deal != nullptr && deal.Ticket == ticket)
      continue;

    deal = new DealInfo;
    deal.Ticket = ticket;
    deal.Closed = dt;
    deal.Position = HistoryDealGetInteger(ticket, DEAL_POSITION_ID);
    deal.Symbol = HistoryDealGetString(ticket, DEAL_SYMBOL);
    deal.Type = HistoryDealGetInteger(ticket, DEAL_TYPE);
    deal.Reason = HistoryDealGetInteger(ticket, DEAL_REASON);
    deal.Volume = HistoryDealGetDouble(ticket, DEAL_VOLUME);
    deal.Price = HistoryDealGetDouble(ticket, DEAL_PRICE);
    deal.Swap = HistoryDealGetDouble(ticket, DEAL_SWAP);
    deal.Profit = HistoryDealGetDouble(ticket, DEAL_PROFIT);
    deal.Magic = HistoryDealGetInteger(ticket, DEAL_MAGIC);
    
    ArrayResize(dealsToAdd, toAdd + 1, total);
    dealsToAdd[toAdd++] = deal;
  }
  
  if (toAdd > 0) {
    deals.AddRange(dealsToAdd);
    deals.Sort(&comparer);
  }
  
  return (toAdd > 0);
}

Код писался на скорую руку и есть что доработать, учитывая частые ArrayResize, но он именно обновляет кэш, отсортированный по Closed. Хотите потом искать - используйте свой индекс. Но обновлять вам придется только немногую часть каждый раз.
Я не помню, зачем там "12*3600", вроде не все сделки выдавались мне.

 
Andrey Pogoreltsev:

Это не кэширование, а индекс.

Читайте внимательнее.

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

MT5 и скорость в боевом исполнении

fxsaber, 2020.08.28 21:10

Чистый MQL5 в 100 раз медленнее частичного (только HistorySelectByPosition) кеширования.

Архитектурно делать полное кеширование - надо хорошо думать. Подводных камней много.

Вот кэшировани (часть кода):

Код писался на скорую руку и есть что доработать, учитывая частые ArrayResize, но он именно обновляет кэш, отсортированный по Closed. Хотите потом искать - используйте свой индекс. Но обновлять вам придется только немногую часть каждый раз.

Это как раз пример лобового сохранения истории без приколов реала. Даже в MT4Orders частичное кеширование делается с пятисекундным запасом...

Я не помню, зачем там "12*3600", вроде не все сделки выдавались мне.

Ставьте всегда INT_MAX.

 
fxsaber:

Архитектурно делать полное кеширование - надо хорошо думать. Подводных камней много.

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

Это как раз пример лобового сохранения истории без приколов реала. Даже в MT4Orders частичное кеширование делается с пятисекундным запасом...

Ставьте всегда INT_MAX.

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

Мой кэш, кстати, работает на реальных счетах с 10к+ сделок.

И основные тормоза в коде пока что - это сетевые функции.

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

В последней бете 2588 функция HistorySelect очень хорошо кешируется и почти всегда(кроме первого раза) обходится бесплатно.

Скорее всего к релизу внесем еще ряд улучшений.



Как я объяснял ранее, в МТ5 нет дополнительных расходов на автоматическом создании снепшотов рынка для каждого эксперта перед каждым эвентом, как это делалось в МТ4. Это снижает задержки и позволяет роботам работать быстрее. Каждый разработчик запрашивает ровно то, что ему нужно.

Поэтому нужно четко понимать, что подход "вызову HistorySelect на всю историю, а потом сразу еще сделаю выборку HistorySelectByPosition" будет убивать ранее созданные кеши истории. Это выстрел себе в ногу.


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

 
Renat Fatkhullin:

В последней бете 2588 функция HistorySelect очень хорошо кешируется и почти всегда(кроме первого раза) обходится бесплатно.

#include <fxsaber\Benchmark.mqh> // https://c.mql5.com/3/321/Benchmark.mqh

input int inAlertTime = 1; // Нижний порог в миллисекундах

#define _B2(A) _B(A, inAlertTime)
#define ALERT(A) Alert(#A + " = " + (string)(A))

void OnInit()
{
  if (HistorySelect(0, INT_MAX))
  {
    ALERT(HistoryDealsTotal());
    ALERT(HistoryOrdersTotal());
  }
}

void OnTick()
{
  static int i = 0;
  
  ALERT(i++);
  
  _B2(HistorySelect(TimeCurrent(), INT_MAX));
  _B2(HistorySelect(0, INT_MAX));
}


Результат.

2020.09.01 22:56:46.089 Test6 (EURAUD,M1)       Alert: HistoryDealsTotal() = 9435
2020.09.01 22:56:46.089 Test6 (EURAUD,M1)       Alert: HistoryOrdersTotal() = 12529
2020.09.01 22:56:46.575 Test6 (EURAUD,M1)       Alert: i++ = 0
2020.09.01 22:56:46.579 Test6 (EURAUD,M1)       Alert: Time[Test6.mq5 25: HistorySelect(0,INT_MAX)] = 3 ms.
2020.09.01 22:56:47.424 Test6 (EURAUD,M1)       Alert: i++ = 1
2020.09.01 22:56:47.428 Test6 (EURAUD,M1)       Alert: Time[Test6.mq5 25: HistorySelect(0,INT_MAX)] = 3 ms.
2020.09.01 22:56:47.765 Test6 (EURAUD,M1)       Alert: i++ = 2
2020.09.01 22:56:47.768 Test6 (EURAUD,M1)       Alert: Time[Test6.mq5 25: HistorySelect(0,INT_MAX)] = 3 ms.
2020.09.01 22:56:47.902 Test6 (EURAUD,M1)       Alert: i++ = 3
2020.09.01 22:56:47.906 Test6 (EURAUD,M1)       Alert: Time[Test6.mq5 25: HistorySelect(0,INT_MAX)] = 3 ms.
2020.09.01 22:56:48.453 Test6 (EURAUD,M1)       Alert: i++ = 4
2020.09.01 22:56:48.456 Test6 (EURAUD,M1)       Alert: Time[Test6.mq5 25: HistorySelect(0,INT_MAX)] = 3 ms.
2020.09.01 22:56:48.516 Test6 (EURAUD,M1)       Alert: i++ = 5
2020.09.01 22:56:48.521 Test6 (EURAUD,M1)       Alert: Time[Test6.mq5 25: HistorySelect(0,INT_MAX)] = 4 ms.


На каждом тике проблема.


ЗЫ Установил Win10, LatencyMon показывает, что все отлично.

 
На данный момент вижу, что в 99% случаев нужно использовать только HistorySelect(0, INT_MAX). Остальные варианты стараться не использовать.
 
Renat Fatkhullin:

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

MqlDeal, MqlOrder и MqlPosition - было бы замечательно. Возможно, даже станет проще.

 
fxsaber:
На данный момент вижу, что в 99% случаев нужно использовать только HistorySelect(0, INT_MAX). Остальные варианты стараться не использовать.

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

Ну и потом что, мне всю это историю перебирать что-ли? Бред какой то.

 
Dmi3:

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

В боевых роботах оставил только 0-INT_MAX-вариант. Тормоза прекратил замечать.

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