[Erreur dans la récupération de l'heure du senior TF dans le chronomètre ! - page 7

 
Ihor Herasko:

Oui, exactement. Dans OnInit(), vous appelez simplement les TF nécessaires sans vérifier le résultat (vous ne pouvez pas vous y fier), et dans OnCalculate, vous appelez la fonction IsTFD DataReady(). Une fois que la réponse est vraie pour toutes les TFs demandées, vous pouvez commencer à exécuter l'algorithme de l'indicateur.

Ok, nous avons réglé le problème. Mais nous devons clairement ajouter la documentation, sinon la minuterie rapide posera beaucoup de questions aux développeurs.

 
Ihor Herasko:

En général, quel type de problème est résolu pour lequel la présence d'une connexion terminale est si cruciale ? Si je comprends bien, l'indicateur est un outil de visualisation des données. Les données qui sont disponibles. Lorsque de nouvelles données arrivent, la visualisation est mise à jour. Il ne devrait pas être nécessaire de vérifier la pertinence des données. C'est la tâche du terminal.

La tâche consiste à obtenir les données de la TF senior le plus rapidement possible.Vitaly Gorbunov m'a rappelé l'existence de IsConnected().

 
Alexey Kozitsyn:

Oh, mec... Nous avons déjà dépassé ce stade. Voir votre propre journal :

Séquence. Tout d'abord, nous vérifions la connexion. Une fois la connexion établie, nous obtenons l'heure. Expliquez-moi, s'il vous plaît, pourquoi l'erreur 4066 est renvoyée en premier et ensuite elle n'est pas renvoyée ! Qu'est-ce qui a changé en 20ms depuis le dernier appel ?

L'erreur 4066 indique l'absence de données, une demande de mise à jour a été envoyée.

Une fois la demande envoyée, une autre demande n'est plus envoyée, d'où l'absence de déclenchement de l'erreur 4066. Ce point a déjà été discuté à de nombreuses reprises.

Pourquoi lancez-vous une minuterie dans l'indicateur ? C'est si petit. Vous devez comprendre que dans MT4, les indicateurs fonctionnent dans un fil d'interface. Le fil d'interface est l'endroit où vont tous les messages du vent.

 
Slava:

L'erreur 4066 indique qu'il n'y a pas de données, une demande de mise à jour a été envoyée.

Une fois qu'une demande a été envoyée, une autre demande n'est plus envoyée, de sorte que l'erreur 4066 n'est pas soulevée. Ce point a déjà été discuté à de nombreuses reprises.

Pourquoi lance-t-on un minuteur dans un indicateur ? C'est si petit. Vous devez comprendre que dans MT4 les indicateurs fonctionnent dans le fil de l'interface. Tous les messages de vent passent par le fil d'interface

Heureux que vous ayez rejoint la discussion.

Ce n'est pas mon premier jour sur le forum + plusieurs personnes ont commenté ici qui n'en sont pas non plus à leur premier jour sur le forum. Personne n'a rien dit à ce sujet :

Une fois qu'une demande a été envoyée, une autre demande n'est plus envoyée, de sorte que l'erreur 4066 ne se déclenche pas. Ce point a déjà été discuté à de nombreuses reprises.

Merci, nous le saurons. J'aimerais vraiment voir cela dans l'aide. Ainsi, l'erreur n'est sûrement "soulevée" que lorsque l'événement OnTick()/OnCalculate() se produit ?

Pourquoi lancez-vous le minuteur dans l'indicateur ? C'est si petit.

Vous devez obtenir des données à partir de quelques caractères. Aussi vite que possible. Malheureusement, ni MT4 ni MT5 n'ont implémenté la réception d'événements d'arrivée de cotations d'un symbole (il est impossible de s'abonner à une telle mise à jour), donc la seule solution (pour autant que je sache) est d'interroger les symboles requis dans le timer.

Vous devez comprendre que dans MT4 les indicateurs fonctionnent dans le fil de l'interface. Tous les messages de vent passent par le fil d'interface

OK, ils arrivent, mais après ? Pouvez-vous préciser en quoi cela est mauvais/bon/en quoi cela affecte le fonctionnement de la minuterie ?
 

On en a parlé à plusieurs reprises. 12 pages par demande "erreur 4066".

Et on vous a conseillé à juste titre d'envoyer la requête dans OnInit et de l'analyser dans OnCalculate.

Pourquoi avez-vous besoin d'une minuterie en millisecondes? Vous empêchez le terminal client de démarrer normalement. Ce ne sont pas les messages du vent qui interfèrent avec votre minuterie, c'est votre minuterie qui interfère avec tout le monde. Encore une fois : LES INDICATEURS DANS LE TERMINAL MT4 DU CLIENT TRAVAILLENT EN POTENTIEL D'INTERFACE.

 
Slava:

On en a parlé à plusieurs reprises. 12 pages sur "erreur 4066".

Lisez-le, merci. Seulement avec le travail dans OnCalculate() ou OnTick() il n'y a pas de problème, le problème est dans OnTimer(). Et sur la requête "error 4066 timer", je n'ai obtenu des résultats que de cette branche :(

Et on vous a correctement conseillé d'envoyer une requête à OnInit et de l'analyser dans OnCalculate.

J'ai écouté les conseils, mais cela ne change rien aux particularités de la gestion des minuteurs qui ne sont pas mentionnées dans la documentation.

Pourquoi avez-vous besoin d'une minuterie en millisecondes ? Vous empêchez le terminal client de se lever normalement par vos actions. Ce ne sont pas les messages du vent qui interfèrent avec votre minuterie, c'est votre minuterie qui interfère avec celle des autres. Encore une fois : LES INDICATEURS DU TERMINAL MT4 DU CLIENT FONCTIONNENT DANS LE POCHET D'INTERFACE.

Une fois de plus, le chronomètre à millisecondes sert à obtenir des informations de plusieurs symboles aussi vite que possible ! L'algorithme est le suivant : l'indicateur est chargé, il obtient les données des TFs élevés dès que possible, puis il surveille les bits des symboles nécessaires dans le timer milliseconde. Existe-t-il un autre moyen de résoudre le problème de surveillance que l'utilisation de la minuterie ?

Résumons tout ce qui a été écrit ici, corrigez-moi si je me trompe :

1. Tâche : obtenir des devis pour plusieurs symboles par le biais d'une minuterie :

Implémentation : Lorsque vous travaillez avec un timer haute fréquence, vous devez attendre que IsConnected() soit établi avec le serveur dans OnCalculate() lors du chargement du terminal, ce n'est qu'ensuite que vous pouvez accéder au timer ;

2. Objectif : obtenir les données des TFs supérieurs le plus tôt possible après le démarrage de l'indicateur (l'indicateur utilise une minuterie rapide) ;

Mise en œuvre : d'abord nous demandons les données nécessaires dans OnInit(), puis nous attendons la connexion IsConnected() dans OnCalculate(), ensuite nous obtenons les données des TFs supérieurs également dans OnCalculate() ;

3. Le lancement d'une minuterie à haute fréquence ralentit-il le fil de l'interface, et par conséquent l'ordinateur, et est-il préférable de ne pas la lancer du tout ? Comment résoudre la tâche n°1 alors ?

4. Objectif : charger les données réelles des anciennes TF.

Mise en œuvre : ne pas utiliser de minuterie haute fréquence pour cela, car les fonctions d'extraction de données ne sont pas conçues pour fonctionner dans une telle minuterie ? Nous utilisons uniquement OnCalculate() ?

5. Dans le cas où une erreur 4066 est reçue puis réinitialisée, est-ce que OnCalculate() l'arme à chaque tick ?

6. Est-ce que OnTimer() dans MT5 ne fonctionne pas dans le fil d'interface ?

@Slava, veuillez répondre également point par point ;

 

1. En général, l'état IsConnected est obtenu lors du deuxième appel à OnCalculate. Premier appel immédiatement après le démarrage du terminal, deuxième appel à l'arrivée des données historiques.

2. N'utilisez pas de minuterie rapide. Évaluez d'abord le calendrier qui vous convient. Elle peut être de 100 millisecondes ou de 500. Ce n'est pas un hasard si nous avons introduit à l'origine le timer des secondes, SetMillisecondsTimer n'a été introduit en cinq( !) que 3 ou 4 ans plus tard. Mais l'architecture est différente en cinq.

3.1 Procurez-vous un ordinateur puissant capable de gérer des files d'attente de messages instantanés.

3.2 Ne lancez pas le temporisateur de millisecondes immédiatement, mais au moins après le premier OnCalculate. Ou plutôt : dans le premier OnCalculate, lancez une deuxième minuterie (que faire s'il n'y a pas de connexion ou si c'est un jour de congé), afin de pouvoir analyser l'environnement. Puis, lorsque vous êtes sûr que toutes les données sont chargées, qu'il y a une connexion et que tout est OK, arrêtez le second minuteur et démarrez celui des millisecondes. Vous pourrez alors passer en toute sécurité par l'étroite porte d'entrée. Dans le meilleur des cas (et il y en aura 99 %), vous perdrez 2 à 5 secondes au départ.

4. Une minuterie est possible. Mais pas immédiatement (voir 3.2). Et je pense que 50 millisecondes, c'est suffisant. Vous ne fournissez pas de HFT, n'est-ce pas ?

5. 4066 n'apparaît que lors de la première demande de données sur le caractère d'époque de quelqu'un d'autre. Lors de la prochaine demande pour la même période de caractères, 4066 n'obtiendra plus rien.

6. Dans MT5, les indicateurs sont comptés dans un fil de traitement des symboles distinct. Ainsi, si vous avez plus d'un graphique sur ce symbole (ou s'il y a d'autres indicateurs sur ce symbole), vous pouvez les ralentir. Mais ce n'est toujours pas un fil d'interface

 
Slava:

1. En général, l'état IsConnected est obtenu lors du deuxième appel à OnCalculate. Premier appel immédiatement après le démarrage du terminal, deuxième appel à l'arrivée des données historiques.

2. N'utilisez pas de minuterie rapide. Évaluez d'abord le calendrier qui vous convient. Elle peut être de 100 millisecondes ou de 500. Ce n'est pas un hasard si nous avons initialement introduit le minuteur des secondes, SetMillisecondsTimer a été introduit en cinq( !) seulement 3 ou 4 ans plus tard. Mais l'architecture est différente en cinq.

3.1 Procurez-vous un ordinateur puissant capable de gérer des files d'attente de messages instantanés.

3.2 Ne lancez pas le temporisateur de millisecondes immédiatement, mais au moins après le premier OnCalculate. Ou plutôt : dans le premier OnCalculate, lancez une deuxième minuterie (que faire s'il n'y a pas de connexion ou si c'est un jour de congé), afin de pouvoir analyser l'environnement. Puis, lorsque vous êtes sûr que toutes les données sont chargées, qu'il y a une connexion et que tout est OK, arrêtez le deuxième minuteur et démarrez le minuteur de la milliseconde. Vous pourrez alors passer en toute sécurité par l'étroite porte d'entrée. Dans le meilleur des cas (et il y en aura 99 %), vous perdrez 2 à 5 secondes au départ.

4. Une minuterie est possible. Mais pas immédiatement (voir 3.2). Et je pense que 50 millisecondes, c'est suffisant. Vous ne fournissez pas de HFT, n'est-ce pas ?

5. 4066 n'apparaît que lors de la première demande de données sur le caractère d'époque de quelqu'un d'autre. Lors de la prochaine demande pour le même caractère de période, vous n'obtiendrez pas à nouveau 4066.

6. Dans MT5, les indicateurs sont comptés dans un fil de traitement des symboles distinct. Ainsi, si vous avez plus d'un graphique sur ce symbole (ou s'il y a d'autres indicateurs sur ce symbole), vous pouvez les ralentir. Mais ce n'est toujours pas un fil d'interface

1. C'est exactement comme ça que ça marche ;

2. C'est là que réside le problème. Plus c'est rapide, mieux c'est. Et l'évaluation a été faite. L'indicateur est écrit pour l'arbitrage (ou plutôt la recherche sur l'arbitrage), c'est-à-dire que chaque milliseconde est importante et que plus vite une cotation est reçue, mieux c'est ;

3.1. et maintenant le système est assez puissant : CPU 8600k, terminaux SSD, 16gb de RAM DDR4 ;

3.2. wow... Ok, j'ai pris note ;

4. La tâche d'arbitrage est susceptible de faire référence au HFT ;

5. Au départ, c'est ce qui me stressait. Si j'avais continué à recevoir l'erreur et que j'avais su que les données n'étaient pas encore prêtes, ce fil de discussion n'aurait pas eu lieu ;

6. Je vois.

Merci pour cette réponse élaborée.

 
Igor Makanu:

Si ce n'est pas trop difficile, voici le sujet du thème - chargement correct de l'historique à partir de l'ancien TF, voici l'indicateur : "Je dois dessiner la MA" de l'ancien TF sur les barres du plus jeune TF, je l'ai fait en 5 minutes, il fonctionnera pour 98% correctement, où dans ce code 2% de "pièges" qui causeront des bugs ?

Oui, juste sur le sujet du sujet. Et tout est réglé ici.

Tout d'abord, avant toute référence à des séries temporelles d'autres TFs/symboles, assurez-vous que les données sont disponibles (voir la fonction IsTFDataReady() ci-dessus). Dans le code ci-dessus, vous êtes uniquement guidé par le résultat de CopyClose. Mais il ne sait rien de la charge historique. Il faut donc d'abord s'assurer que les données sont disponibles et ensuite seulement les demander.

Deuxièmement, appeler une fonction comme argument d'une autre fonction n'est pas toujours justifié. Et dans le cas ci-dessus, c'est inacceptable en principe. Après tout, le résultat de l'appel à iBars doit également être vérifié. Ainsi, tout d'abord iBars est appelé, le résultat est mis en cache et vérifié, puis seule la valeur reçue est transférée à CopyClose().

Troisièmement, après l'appel de CopyClose, il n'y a pas de contrôle pour obtenir toutes les données demandées. Après tout, la fonction peut retourner 1 ou 2 barres, et il a été demandé, par exemple, 10. Je considérerais un tel résultat comme une erreur.

Quatrièmement, il y a une erreur dans l'idée même de l'approche. La boucle suppose qu'elle opère avec les barres d'un autre TF mais elle confond les calculs avec la variable rates_total dont la valeur se réfère au TF actuel. Ici, deux approches sont possibles, que j'utilise dans tel ou tel cas :

  1. Boucler les barres du TF actuel et convertir l'indice de la barre du TF actuel en indice de barre d'un autre TF avant la demande de données (cette approche est utilisée dans le code ci-dessous).
  2. Parcourir les barres d'un autre TF. Mais nous devons alors inclure une boucle supplémentaire pour le cas où le TF actuel est junior par rapport à l'autre. Car une barre de l'ancienne TF correspondra à plusieurs barres de la TF actuelle.

Je suis intéressé par le code correct pour MT4

A la lumière de ces quatre points, cela devrait ressembler à ceci (je ne l'ai pas vérifié, je l'ai fait à la main, mais le sens devrait être clair) :

   if (!IsTFDataReady(TimeFrame))
      return 0;

   int i,limit;

   static int nOldBars = 0;
   int nBars = iBars(_Symbol, TimeFrame);
   if (nBars == 0)
      return 0;
      
   if (nOldBars == 0 || nBars - nOldBars > 1)
   {
      if(nBars < MAPeriod)
      {
         Comment("Большой период МА!!!, в истории доступно ", nBars," баров");
         return 0;
      }
      
      limit = nBars - fmin(MAPeriod, nBars);
   }
   else
      limit = nBars - nOldBars;  // здесь всегда будет 0 или 1
   
   nOldBars = nBars;
   datetime dtTime = iTime(NULL, TimeFrame, limit);
   if (dtTime == 0)
      return 0;

   limit = iBarShift(NULL, PERIOD_CURRENT, dtTime);

// основной цикл расчета индикатора
   for(i = limit; i >= 0 && !IsStopped(); i--)
   {
      int nOtherTFBarIndex = iBarShift(NULL, TimeFrame, time[i]);
      if (nOtherTFBarIndex < 0 || nOtherTFBarIndex >= nBars)
         continue;
      
      BufMA[i] = iMA(_Symbol,TimeFrame,MAPeriod,0,MODE_SMA,PRICE_CLOSE,nOtherTFBarIndex);
   }
//---
   return rates_total;

Au fait, je l'ai vérifié :


 
Ihor Herasko:

Oui, c'est le sujet du fil de discussion. Et tout est déjà réglé ici.

Tout d'abord, avant toute référence à d'autres séries temporelles TF/symbole, assurez-vous que les données sont disponibles (voir la fonction IsTFDataReady() ci-dessus). Dans le code ci-dessus, vous êtes uniquement guidé par le résultat de CopyClose. Mais il ne sait rien de la charge historique. Il faut donc d'abord s'assurer que les données sont disponibles et ensuite seulement les demander.

Deuxièmement, appeler une fonction comme argument d'une autre fonction n'est pas toujours justifié. Et dans le cas ci-dessus, c'est inacceptable en principe. Après tout, le résultat de l'appel à iBars doit également être vérifié. Ainsi, tout d'abord iBars est appelé, le résultat est mis en cache et vérifié, puis seule la valeur reçue est transférée à CopyClose().

Troisièmement, après l'appel de CopyClose, il n'y a pas de contrôle pour obtenir toutes les données demandées. Après tout, la fonction peut retourner 1 ou 2 barres, et il a été demandé, par exemple, 10. Je considérerais un tel résultat comme une erreur.

Quatrièmement, il y a une erreur dans l'idée même de l'approche. La boucle suppose qu'elle opère avec les barres d'un autre TF mais elle confond les calculs avec la variable rates_total dont la valeur se réfère au TF actuel. Ici, deux approches sont possibles, que j'utilise dans tel ou tel cas :

  1. Boucler sur les barres du TF actuel et convertir l'indice de barre du TF actuel en indice de barre d'un autre TF avant de demander des données (cette approche est utilisée dans le code ci-dessous).
  2. Parcourir les barres d'un autre TF. Mais nous devons alors inclure une boucle supplémentaire pour le cas où le TF actuel est junior par rapport à l'autre. Après tout, une barre de l'ancienne TF correspondra à plusieurs barres de la TF actuelle.

A la lumière de ces quatre points, voici comment cela devrait être (je ne l'ai pas vérifié, je le faisais à la main, mais le sens devrait être clair) :

Au fait, je l'ai vérifié :


1. j'en ai tenu compte, merci !

2. des mots en or ! j'avais l'habitude d'écrire ainsi, mais avec le temps, en lisant les codes des autres, qui courent après la compacité... Je fais plus attention à "la brièveté est la sœur du talent" .... et je passe mon temps à chercher les bogues que j'ai rencontrés.

4. "la brièveté est la sœur du talent"... Vous avez raison !

3. point d'analyse intéressant, ce que retourne CopyClose(), je l'ai vérifié moi-même, s'il n'y a pas de fichier .hst pour le TF demandé, CopyClose() ne retourne jamais plus de 2048 - c'est à dire que c'est la valeur maximale qui peut être téléchargée ?