Bibliothèque: OnTickMulti - page 5

 
Stanislav Korotky #:

Pour élaborer un scénario de test complet, j’aimerais comprendre le cas pratique : négocions-nous uniquement sur des ticks dont l’heure coïncide à la milliseconde près ?

Non. L'objectif est de toujours disposer d'un environnement à jour pour tous les symboles. Dans mon exemple, la condition d'actualité est toujours remplie dans OnTimer. L'ouverture d'une position n'est qu'une démonstration.
 
fxsaber #:
Non. L'objectif est de toujours disposer d'un environnement à jour pour tous les symboles. Dans mon exemple, la condition d'actualité est toujours remplie dans OnTimer. L'ouverture d'une position n'est qu'une démonstration.

D'accord.

Pour l'instant, on ne comprend pas bien où la mise en œuvre de l'événement OnTickMulti « perd » un tick ou, en d'autres termes, comment se fait-il que SymbolInfoTick renvoie des cours différents de ceux attendus (j’avais précédemment supposé qu’il y avait probablement un retard dû à la gestion des événements personnalisés de l’indicateur) — je testerais sans l’indicateur espion pour la pureté de l’expérience — simplement SymbolInfoTick/CopyTicks sur tous les symboles à partir de l’OnTick standard, ne serait-ce que pour le même horodatage.

Quant à OnTimer, cela me pose quelques questions (corrigez-moi si je me trompe) :

void OnTimer()
{
  if (TimeMsc++ == 1759280400081) // 01/10/2025 01:00:00.081
    PositionOpen();
}

Nous n’avons aucune garantie que le gestionnaire s’exécute en moins d’une milliseconde ; par conséquent, un simple incrément du compteur ne garantit pas le maintien de la synchronisation initiale, c’est-à-dire la synchronisation avec l’heure du serveur doit être effectuée correctement à la volée à chaque fois (après l’ouverture d’une position ou d’autres calculs plus complexes qu’un simple incrément, le cas échéant). En d’autres termes, l’approche raffinée présentée ici ne fonctionnera pas lorsqu’il faudra ouvrir de nombreuses positions.

De plus, la synchronisation initiale (initialisation du compteur) n’est pas parfaite à 100 %, à mon humble avis.

ulong TimeMsc = TimeTradeServer() * 1000 + (inTimer && EventSetMillisecondTimer(1));

Admettons que, dans le testeur, l’heure du serveur commence effectivement sans millisecondes, mais comment un tel code fonctionnera-t-il en ligne ? Et pourquoi ajoutons-nous 1 milliseconde ? Je préférerais tout de même récupérer l’heure du serveur à partir des ticks.

Ce ne sont pas des chicaneries, mais simplement des doutes quant au « respect systématique de la condition d’actualité ».

 
Stanislav Korotky #:

un simple incrément du compteur ne garantit pas le maintien de la synchronisation initiale

Dans le testeur, c'est garanti.

Mais comment un tel code fonctionnera-t-il en ligne ?

En ligne, ce problème ne se pose pas, car toutes les données qui arrivent au terminal sont indicatives : elles ne sont pas à jour en raison des décalages.

Et pourquoi ajoutons-nous 1 milliseconde ?

Parce que le tout premier OnTimer sera déclenché après un intervalle défini, et non immédiatement.

 
Stanislav Korotky #:

On ne sait pas encore où, dans l'implémentation de l'événement OnTickMulti, le « tick » est « perdu » ; en d'autres termes, comment se fait-il que SymbolInfoTick ne renvoie pas les cours attendus ?

OnTickMulti n’est certainement pas en cause, car la fonction SymbolInfoTick standard renvoie le tick correct pour l’un des symboles, mais pas pour les autres.


La cause réside uniquement dans l'ordre de transmission des ticks.

Forum consacré au trading, aux systèmes de trading automatisés et au test de stratégies de trading

Bibliothèques : OnTickMulti

fxsaber, 30/09/2025 09:24

Les ticks ayant le même horodatage n'arrivent pas simultanément. Ils se succèdent les uns après les autres. Et si un tick sur l'EURUSD avec un horodatage plus élevé est arrivé en premier, on ne sait alors rien, à ce moment-là, du tick ayant le même horodatage sur le GBPUSD, qui arrivera en deuxième. Par conséquent, au moment où le premier tick de l’EURUSD arrive, le deuxième tick du GBPUSD n’existe tout simplement pas encore ; on dispose uniquement des données du tick précédent du GBPUSD.
 
fxsaber #:

Car le tout premier OnTimer sera déclenché après un intervalle défini, et non pas immédiatement.

J’ai mis au point une méthode rapide pour actualiser toutes les données.

#include <fxsaber\OnTickMulti\OnTickMulti.mqh> // https://www.mql5.com/fr/code/47647

#include <MT4Orders.mqh> // https://www.mql5.com/fr/code/16006

void PositionOpen()
{
  for (uint i = ArraySize(OnTickMultiObject.Symbols); (bool)i--;)
  {
    const string SymbName = OnTickMultiObject.Symbols[i];
    
    OrderSend(SymbName, OP_BUY, 1, SymbolInfoDouble(SymbName, SYMBOL_ASK), 0, 0, 0);
  }
}

long CurrentTime = 0;

// OnTick multi-caractères.
void OnTickMulti( const string &Symb, const uint &Index )
{  
  MqlTick Tick;
  
  if (SymbolInfoTick(Symb, Tick) && (Tick.time_msc > CurrentTime))
  {
    CurrentTime = Tick.time_msc;
    
    EventSetMillisecondTimer(1);
  }  
}

void OnTimer()
{
  if (CurrentTime == 1759280400081) // 01/10/2025 01:00:00.081
    PositionOpen();
    
  EventKillTimer();
}

Ce mécanisme permet, même en mode standard (mode mono-devise sans OnTickMulti), de ne travailler qu’avec des données à jour. Par exemple, pour la paire EUR/USD, il existe plusieurs ticks avec la même heure. La mise à jour permet de travailler avec le tick le plus récent, c’est-à-dire le dernier de cette séquence.


P.S. C'est une raison supplémentaire de créer des symboles personnalisés: n'enregistrer dans l'historique que le dernier tick des séquences ayant la même heure. Dans ce cas, la mise à jour sera toujours respectée en mode mono-devise.

Библиотеки: TicksShort
Библиотеки: TicksShort
  • 2025.09.26
  • www.mql5.com
Статьи и техническая библиотека по автоматическому трейдингу: Библиотеки: TicksShort
 
Rorschach #:

Mais les cours d'ouverture sont identiques, bien que l'heure d'ouverture soit la même pour tous les titres.

Et en raison de ce comportement, il est impossible de tester correctement un certain nombre de systèmes.

Je me suis remémoré le fonctionnement du testeur en mode « cours d’ouverture ». Le truc, c’est que malgré son nom, le testeur génère dans ce mode 4 ticks OHLC, et non pas 1 comme on pourrait intuitivement s’y attendre d’après son nom. Parmi ces 4 points de contrôle, pour les experts, seul le premier (O) est pris en compte et la fonction OnTick est appelée ; quant aux indicateurs, en plus de HLC ou LHC (selon la direction de la barre), la fonction OnCalculate est appelée 3 fois avec les ticks correspondant aux prix respectifs. Les temps correspondant à ces trois points supplémentaires sont fixés artificiellement aux trois dernières secondes de la barre. Il en résulte que l’indicateur « espion » envoie plusieurs événements pour les symboles au lieu d’un seul. Il convient probablement d’en tenir compte pour ceux qui utilisent le mode basé sur les cours d’ouverture.

J’ai également observé un artefact (en ajoutant un débogage à mon indicateur « espion » similaire tiré du livre) : l’événement concernant le tick d’un symbole supplémentaire provenant de la barre précédente se répète pour une raison inconnue sur la nouvelle barre, ce n’est qu’ensuite que la fonction OnCalculate de l’indicateur-espion est appelée et qu’un événement concernant le nouveau tick du symbole supplémentaire, avec le cours actualisé, est déclenché. Par conséquent, pour disposer d’un cours à jour sur les symboles supplémentaires, il faut inclure un horodatage dans les événements eux-mêmes et ne pas traiter à nouveau les événements déjà traités. Je procède ainsi lors de l’envoi :

int OnCalculate(const int rates_total, const int prev_calculated, const int, const double &price[])
{
   MqlTick tick;
   SymbolInfoTick(_Symbol, tick);
   EventChartCustom(Chart, Message, Index, (double)tick.time_msc, NULL);
  
   return rates_total;
}

Et lors de la réception (l'exemple montre le cas d'un seul symbole supplémentaire ; pour plusieurs, il faut un tableau timestamp[] !) :

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
   if(id == CHARTEVENT_CUSTOM + Message)
   {
      static long timestamp;
      long event = (long)dparam;
      if(event > timestamp)
      {
         OnSymbolTick((int)lparam);
         timestamp = event;
      }
   }
}

Il convient de noter qu’en présence de ticks ayant le même temps en millisecondes, c’est le premier d’entre eux qui est exécuté, et non le dernier.

 
Rorschach #:
La méthode de synchronisation la plus simple
Je pensais que la synchronisation des séries chronologiques était un problème bien étudié et qu'il existait des algorithmes tout prêts, mais quand j'ai cherché sur Internet, je n'ai rien trouvé.