créer un numéro magique

 
/**
* create a positive integer for the use as a magic number.
*
* The function takes a string as argument and calculates
* an 31 bit hash value from it. The hash does certainly not 
* have the strength of a real cryptographic hash function 
* but it should be more than sufficient for generating a
* unique ID from a string and collissions should not occur.
*
* use it in your init() function like this: 
*    magic = makeMagicNumber(WindowExpertName() + Symbol() + Period());
*
* where name would be the name of your EA. Your EA will then
* get a unique magic number for each instrument and timeframe
* and this number will always be the same, whenever you put
* the same EA onto the same chart.
*
* Numbers generated during testing mode will differ from those
* numbers generated on a live chart.
*/
int makeMagicNumber(string key){
   int i, k;
   int h = 0;
   
   if (IsTesting()){
      key = "_" + key;
   }
   
   for (i=0; i<StringLen(key); i++){
      k = StringGetChar(key, i);
      h = h + k;
      h = bitRotate(h, 5); // rotate 5 bits
   }
   
   for (i=0; i<StringLen(key); i++){
      k = StringGetChar(key, i);
      h = h + k;
      // rotate depending on character value
      h = bitRotate(h, k & 0x0000000F);
   }
   
   // now we go backwards in our string
   for (i=StringLen(key); i>0; i--){   
      k = StringGetChar(key, i - 1);
      h = h + k;
      // rotate depending on the last 4 bits of h
      h = bitRotate(h, h & 0x0000000F); 
   }
   
   return(h & 0x7fffffff);
}

/**
* Rotate a 32 bit integer value bit-wise 
* the specified number of bits to the right.
* This function is needed for calculations
* in the hash function makeMacicNumber()
*/
int bitRotate(int value, int count){
   int i, tmp, mask;
   mask = (0x00000001 << count) - 1;
   tmp = value & mask;
   value = value >> count;
   value = value | (tmp << (32 - count));
   return(value);
}
 
7bit:
[ ... fonction de hachage ... ]

Pour mémoire, fbj a déjà fait quelque chose de similaire en utilisant la célèbre fonction de hachage djb2 : https://www.mql5.com/en/forum/120034/page2

 
J'ai été initialement inspiré par ce http://www.cs.hmc.edu/~geoff/classes/hmc.cs070.200101/homework10/hashfuncs.html, en particulier celui étiqueté comme variante CRC (qui est très similaire à djb2). Ce hachage seul, bien que je ne sois pas capable de produire facilement des collisions, ne me donnait pas assez de confiance, parfois seuls quelques bits différaient entre les hachages de deux chaînes similaires. J'ai donc créé trois variantes de ce hachage avec une rotation variable de h et j'ai ajouté les trois sous-hachages ensemble. Si l'un d'entre eux entre en collision, il en reste deux autres calculés d'une manière totalement différente. Maintenant, chaque bit modifié dans la chaîne d'entrée change plus de la moitié de tous les bits dans le hachage et tous les bits semblent complètement aléatoires.


le djb2 mentionné dans votre lien ci-dessus pourrait être écrit sans toutes les centaines de lignes qui l'entourent simplement comme :
int djb2(string key){
   int i, h, k;
   for (i=0; i<StringLen(key); i++){
      k = StringGetChar(key, i);
      h = (h << 5) + h + k;
   }
   return(h);
}
 
7bit:
le djb2 mentionné dans votre lien ci-dessus pourrait être écrit sans toutes les centaines de lignes qui l'entourent simplement comme : [...]

Je ne suis pas un expert en algorithmes de hachage, et encore moins en djb2 en particulier, mais je crois me souvenir que l'initialisation de la valeur de hachage (la variable h dans votre version) à 5381 est considérée comme significative, bien que personne ne sache exactement pourquoi.

 
*    magic = makeMagicNumber(name+ Symbol() + Period());
Je ne veux pas pinailler, mais vous devriez pouvoir l'utiliser également :
*    magic = makeMagicNumber(WindowExpertName() + Symbol() + Period());
Merci d'avoir posté le code (et l'article sur le hachage !).

Question - Je travaille sur une méthode pour ouvrir et fermer plusieurs ordres sur le même graphique, le même algorithme, etc.

Je l'aborde en 2 étapes

1) générer un MN de base (ce que le code ci-dessus semble faire) comme un entier. La base serait toujours la même pour chaque graphique/symbole/trame temporel.
2) générer un suffixe spécifique exprimé sous la forme d'un point décimal pour chaque ordre spécifique, une fois qu'un suffixe devient inutilisé, il devient à nouveau disponible.

Le MN serait donc XXXXXX.YYY où X est la base et Y le suffixe spécifique. Les suffixes commenceraient à .001 et seraient incrémentés de .001 pour chaque nouvel envoi. À chaque fin de commande, le suffixe le plus bas actuellement inutilisé serait attribué. De cette façon, je peux récupérer le MN plus tard en régénérant le MN de base et en passant en revue les suffixes.

Cela semble un peu trop compliqué. Existe-t-il une meilleure façon de procéder ?

Je posterai ce que j'ai quand ce sera terminé.
 
En tant que NuB, je ne vois pas pourquoi vous voudriez ou auriez besoin d'un MagicNumber "crypté".
J'utilise simplement les 5 premiers chiffres pour la version de l'EA # et les 4 derniers pour le nombre de minutes sur lequel il est négocié.
 
FourX:
En tant que NuB, je ne vois pas pourquoi vous voudriez ou auriez besoin d'un MagicNumber "crypté" ?
J'utilise simplement les 5 premiers chiffres pour la version de l'EA # et les 4 derniers pour le nombre de minutes sur lequel il est négocié.

Comment, dans votre exemple, le Symbol() peut-il faire partie du MN ? Vous avez un numéro d'EA et un numéro de Timeframe, mais qu'en est-il du symbole ?

J'identifie mes ordres par MN uniquement, mes boucles sur la liste des ordres ne comparent que OrderMagicNumber(), les vôtres devraient également vérifier le nom du symbole. J'ai quelques autres scripts indépendants qui font des choses avec la liste des ordres, par exemple tracer des graphiques d'équité des EA ou copier les transactions sur une autre plateforme, ils ont tous besoin uniquement du numéro magique pour identifier les transactions d'un EA spécifique sur une paire spécifique et une période spécifique.

Je n'utilise pas du tout de numéros de série pour mes différents EAs, j'utilise des noms courts de 4 ou 5 lettres pour tous mes EAs. Un EA nommé snowball.mq4 par exemple sera nommé "snow". Ce nom est câblé dans le code et n'est jamais modifié. J'utilise également ce nom court pour les commentaires d'ordre.

J'ai donc 3 éléments : le nom court, le symbole et la période. La façon la plus pratique de convertir cela en un MN est un hash. Je pourrais donner des numéros à mes EA au lieu de noms, mais il n'y aurait toujours pas de moyen facile de convertir le nom du symbole en un nombre. Un hash résout simplement tous ces problèmes à la fois.

 
FourX:
En tant que NuB, je ne vois pas pourquoi vous voudriez ou auriez besoin d'un MagicNumber 'crypté' ?
J'utilise simplement les 5 premiers chiffres pour la version de l'EA # et les 4 derniers pour le nombre de minutes sur lequel il est négocié.

Vous devriez aussi voir ceci -> https://www.mql5.com/en/forum/120034


Le problème que je rencontre avec cette approche est que j'ai parfois des experts/symboles/horaires identiques sur le même compte. C'est pourquoi je préfère régler la magie elle-même manuellement.

 
gordon:
Le problème que je rencontre avec cette approche est que j'ai parfois un expert/symbole/horaire identique sur le même compte. C'est pourquoi je préfère régler la magie elle-même manuellement.

Pourquoi ne pas utiliser les secondes ? TimeCurrent() renvoie un nombre qui sera toujours unique - du moins, en dehors de cette durée de seconde...

- Attribuez un numéro d'identification GlobalVariable à votre expert. Retournez-le avec WindowExpertName().

- Concaténer cet ID avec un compteur d'incrémentation (si vous attachez le même expert) et TimeCurrent().

- Si le nombre retourné par TimeCurrent() dépasse la taille autorisée. Il faut alors éliminer le nombre d'années et de mois jusqu'à ce que nous ayons le module de jours, heures, minutes et secondes.

 
cameofx:

Pourquoi ne pas utiliser les secondes ? TimeCurrent() renvoie un numéro qui sera toujours unique - du moins en dehors de cette seconde.

- Attribuez un numéro d'identification à votre expert. Renvoyez-le avec WindowExpertName().

- Concaténer cet ID avec un compteur d'incrémentation et TimeCurrent()

- Si le nombre retourné par TimeCurrent() dépasse la taille autorisée. Alors éliminez le nombre d'années et de mois jusqu'à ce que nous ayons le module de jours, heures, minutes et secondes.

Parce qu'alors vous devez garder un niveau de persistance pour cette magie. Que se passe-t-il si votre terminal redémarre ? La magie serait différente...

 
gordon:

Parce qu'alors vous devez garder un niveau de persistance pour cette magie. Que se passe-t-il si votre terminal redémarre ? La magie serait différente...

Mon Dieu, tu as battu ma vitesse d'édition :)). Je l'ai édité. J'ai oublié de préciser que c'est une GlobalVariable.
Raison: