MT5 et la vitesse en action - page 20

 
fxsaber:

Il y a peut-être une option plus rapide. Mais un pas vers la gauche dans la condition de ce qui doit être compté, et la logique peut devoir changer considérablement. Pas facile, en général.

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


Ce n'est pas de la mise en cache, c'est de l'indexation. Voici la mise en cache (partie du code) :

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);
}

Le code a été écrit à la hâte et il y a beaucoup de choses à affiner, étant donné la fréquence des ArrayResize, mais il met exactement à jour le cache, trié par Closed. Si vous souhaitez effectuer une recherche ultérieure, utilisez votre propre index. Mais vous ne devez mettre à jour qu'une petite partie à chaque fois.
Je ne me souviens pas pourquoi"12*3600" est là, je ne pense pas que tous les contrats m'ont été délivrés.

 
Andrey Pogoreltsev:

Ce n'est pas une mise en cache, c'est un index.

Veuillez lire attentivement.

Forum sur le trading, les systèmes de trading automatisé et les tests de stratégies de trading

MT5 et la vitesse en action

fxsaber, 2020.08.28 21:10

MQL5 pur est 100x plus lent que la mise en cache partielle (seulement HistorySelectByPosition).

L'architecture permet une mise en cache complète - il faut bien réfléchir. Les pièges sont nombreux.

Voici la mise en cache (partie du code) :

Le code a été écrit à la hâte et a quelques choses à améliorer compte tenu des ArrayResize fréquents, mais il met à jour le cache trié par Closed. Si vous souhaitez effectuer une recherche ultérieure, utilisez votre propre index. Mais vous ne devez mettre à jour qu'une petite partie à chaque fois.

Ce n'est qu'un exemple d'une sauvegarde de l'histoire sans les gadgets du monde réel. Même dans MT4Orders, la mise en cache partielle se fait avec une marge de cinq secondes...

Je ne me souviens pas pourquoi"12*3600" est là, je ne pense pas que tous les métiers m'aient été délivrés.

Toujours mettre INT_MAX.

 
fxsaber:

D'un point de vue architectural, la mise en cache complète demande beaucoup de réflexion. Il y a beaucoup de pièges.

Il n'y a vraiment rien de compliqué. Vous pouvez effectuer un échantillonnage en quelques heures, par exemple, pour toutes les commandes qui pourraient apparaître dans l'historique de manière rétroactive (ce qui est très étrange, d'ailleurs).

Ce n'est qu'un exemple de sauvegarde de l'histoire en direct, sans aucun artifice en temps réel. Même dans MT4Orders, la mise en cache partielle se fait avec une marge de cinq secondes...

Toujours mettre INT_MAX.

Je ne comprends pas le but de ce changement. Comme s'il existait un horodatage actuel, je veux me situer par rapport à celui-ci et il est étrange de devoir spécifier un temps qui n'existe pas. Je veux une explication logique.

Mon cache, d'ailleurs, fonctionne sur des comptes réels avec plus de 10 000 transactions.

Et les principaux freins dans le code jusqu'à présent sont les fonctions de réseau.

 
De passage, j'ai pensé résumer ce qui a déjà été évoqué ici. Si vous avez besoin de performances ultrarapides à tout prix, vous y parviendrez de manière traditionnelle - au détriment de la consommation d'autres ressources, par exemple la mémoire. En d'autres termes, il n'est pas nécessaire d'exécuter une boucle à travers tout l'historique à chaque fois qu'il est nécessaire de calculer une certaine valeur agrégée (qui peut ne pas être seulement la durée totale), et de tout calculer à l'avance dans le "tableau" (ou autre structure de données optimisée) et ensuite seulement d'augmenter. Et une demande de valeur par identifiant de position (ou tout autre paramètre, par lequel le découpage est effectué) dégénérera en référence à une cellule mémoire. Dans la forme la plus générale - c'est quelque chose comme OLAP (bien que dans ma mise en œuvre n'est pas fini mise à jour en ligne), mais pour un problème particulier peut être fait et plus simple.
 

Dans la dernière version bêta 2588, la fonction HistorySelect est très bien mise en cache et est presque toujours (sauf la première fois) gratuite.

Nous sommes susceptibles d'apporter un certain nombre d'autres améliorations d'ici la sortie de la version.



Comme je l'ai expliqué précédemment, dans MT5, il n'y a pas de coût supplémentaire pour créer automatiquement des instantanés du marché pour chaque EA avant chaque événement, comme cela était fait dans MT4. Cela réduit les délais et permet aux robots de fonctionner plus rapidement. Chaque développeur demande exactement ce dont il a besoin.

Par conséquent, vous devez clairement comprendre que l'approche consistant à "appeler HistorySelect sur l'ensemble de l'historique, puis à effectuer immédiatement une autre sélection de HistorySelectByPosition" tuera les caches de l'historique précédemment créés. C'est un tir dans le pied.


Après la sortie de la version, nous commencerons un travail important pour ajouter de nouvelles fonctions MQL5 plus efficaces et des structures de données natives ouvertes pour les ordres et les transactions, afin de simplifier et d'accélérer l'algotrading.

 
Renat Fatkhullin:

Dans la dernière version bêta de 2588, la fonction HistorySelect est très bien mise en cache et est presque toujours (sauf la première fois) gratuite.

#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));
}


Résultat.

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.


Sur chaque tique, il y a un problème.


ZY a installé Win10, LatencyMon montre que tout va bien.

 
Pour le moment, je constate que dans 99% des cas, seul HistorySelect(0, INT_MAX) devrait être utilisé. Essayez de ne pas utiliser d'autres options.
 
Renat Fatkhullin:

Après la sortie de la version, nous commencerons à travailler sur l'ajout de nouvelles fonctions MQL5 plus efficaces et sur l'ouverture des structures de données natives des ordres et des transactions, afin de simplifier et d'accélérer le trading algorithmique.

MqlDeal, MqlOrder et MqlPosition seraient parfaits. Cela pourrait même devenir plus simple.

 
fxsaber:
Pour l'instant, je constate que dans 99% des cas, nous ne devrions utiliser que HistorySelect(0, INT_MAX). Essayez de ne pas utiliser les autres options.

Si j'ai des centaines de milliers de commandes dans mon historique, cela sera-t-il aussi plus rapide que de prendre l'historique de dernière minute ?

Alors dois-je passer par toute cette histoire ? Cela n'a pas de sens.

 
Dmi3:

Si j'ai des centaines de milliers de commandes dans mon historique, est-ce aussi plus rapide que de prendre l'historique de dernière minute ?

Il ne reste que la variante 0-INT_MAX dans les robots de combat. J'ai arrêté de remarquer les décalages.