Schaffung einer magischen Zahl

 
/**
* 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:
[ ... Hash-Funktion ... ]

Fürs Protokoll: fbj hat einmal etwas Ähnliches mit der bekannten djb2-Hash-Funktion gemacht: https://www.mql5.com/en/forum/120034/page2

 
Ursprünglich wurde ich durch diese http://www.cs.hmc.edu/~geoff/classes/hmc.cs070.200101/homework10/hashfuncs.html inspiriert, insbesondere durch die als CRC-Variante bezeichnete (die djb2 sehr ähnlich ist). Dieser Hash allein, obwohl ich nicht in der Lage war, leicht Kollisionen zu erzeugen, gab mir nicht genug Vertrauen, manchmal unterschieden sich nur sehr wenige Bits zwischen Hashes zweier ähnlicher Zeichenketten. Also habe ich drei Varianten davon mit unterschiedlicher Rotation von h erstellt und alle 3 Teil-Hashes zusammengefügt. Sollte einer von ihnen kollidieren, gibt es immer noch zwei andere, die auf ganz andere Weise berechnet werden. Jetzt ändert jedes geänderte Bit im Eingabestring mehr als die Hälfte aller Bits im Hash und alle Bits sehen völlig zufällig aus.


Der djb2, der in deinem Link oben erwähnt wird, könnte ohne all die hunderten von Zeilen drumherum einfach so geschrieben werden:
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:
Das djb2, das in deinem Link oben erwähnt wird, könnte ohne all die Hunderte von Zeilen drumherum einfach geschrieben werden als: [...]

Ich bin kein Experte für Hash-Algorithmen, geschweige denn für djb2 im Besonderen, aber ich glaube mich zu erinnern, dass die Initialisierung des Hash-Wertes (die h-Variable in Ihrer Version) auf 5381 als signifikant angesehen wird, obwohl niemand genau weiß, warum.

 
*    magic = makeMagicNumber(name+ Symbol() + Period());
Ich will nicht pingelig sein, aber Sie sollten in der Lage sein, dies auch zu verwenden:
*    magic = makeMagicNumber(WindowExpertName() + Symbol() + Period());
Vielen Dank für die Veröffentlichung des Codes (und des Hash-Artikels!).

Frage - Ich arbeite an einer Methode zum Öffnen und Schließen mehrerer Aufträge auf demselben Chart, demselben Algorithmus usw.

Ich gehe dabei in 2 Schritten vor -

1) Generierung einer Basis-MN (was der obige Code zu tun scheint) als Ganzzahl. Die Basis wäre für jeden Chart/Symbol/Zeitrahmen immer die gleiche
2) Generierung eines spezifischen Suffixes, ausgedrückt als Dezimalpunkt für jedes spezifische Orderende, sobald ein Suffix unbenutzt wird, wird es wieder verfügbar gemacht

Die MN würde also XXXXXX.YYY lauten, wobei X die Basis und Y das spezifische Suffix ist. Die Suffixe würden bei .001 beginnen und bei jeder neuen Sendung um .001 erhöht werden. Bei jedem Auftragsende würde das niedrigste derzeit unbenutzte Suffix zugewiesen. Auf diese Weise kann ich die MN später wiederherstellen, indem ich die Basis-MN neu generiere und die Suffixe durchlaufe.

Das scheint mir ein wenig zu kompliziert. Gibt es eine bessere Möglichkeit, dies zu tun?

Ich werde posten, was ich habe, wenn es fertig ist.
 
Als NuB bin ich mir nicht sicher, warum Sie eine "verschlüsselte" MagicNumber wollen oder brauchen?
Ich verwende einfach die ersten 5 Zahlen für die Version des EA # und die letzten 4 für die Anzahl der Minuten, in denen er gehandelt wird.
 
FourX:
Als NuB bin ich mir nicht sicher, warum Sie eine 'verschlüsselte' MagicNumber wollen oder brauchen?
Ich verwende einfach die ersten 5 Zahlen für die Version des EA # und die letzten 4 für die Anzahl der Minuten, in denen er gehandelt wird.

Wie würde in Ihrem Beispiel das Symbol() Teil des MN werden? Sie haben eine EA-Nummer und eine Timeframe-Nummer, aber was ist mit dem Symbol?

Ich identifiziere meine Aufträge nur nach MN, meine Schleifen über die Auftragsliste vergleichen nur OrderMagicNumber(), Ihr Skript müsste auch nach dem Symbolnamen suchen. Ich habe noch ein paar andere unabhängige Skripte, die mit der Orderliste arbeiten, z.B. Equity Plots von EAs oder Kopieren der Trades auf eine andere Plattform, sie alle benötigen nur die magische Nummer, um die Trades eines bestimmten EAs auf einem bestimmten Paar und einem bestimmten Timeframe zu identifizieren.

Ich verwende überhaupt keine Seriennummern für meine verschiedenen EAs, ich verwende kurze Namen mit 4 oder 5 Buchstaben für alle meine EAs. Ein EA mit dem Namen snowball.mq4 würde zum Beispiel den Namen "snow" erhalten. Dieser ist im Code fest verdrahtet und wird nie geändert. Ich verwende diesen Kurznamen auch für Auftragskommentare.

Ich habe also 3 Dinge: Kurzname, Symbol und Zeitrahmen. Der bequemste Weg, dies in eine MN umzuwandeln, ist ein Hash. Ich könnte meinen EAs auch Nummern statt Namen geben, aber es gäbe immer noch keine einfache Möglichkeit, den Symbolnamen in eine Nummer umzuwandeln. Ein Hash löst einfach alle diese Probleme auf einmal.

 
FourX:
Als NuB bin ich mir nicht sicher, warum Sie eine "verschlüsselte" MagicNumber wollen oder brauchen?
Ich verwende einfach die ersten 5 Zahlen für die Version des EA # und die letzten 4 für die Anzahl der Minuten, in denen er gehandelt wird.

Sie sollten auch dies sehen -> https://www.mql5.com/en/forum/120034


Das Problem, das ich mit diesem ganzen Ansatz habe, ist, dass ich manchmal identische Experten/Symbole/Zeitrahmen im selben Konto laufen habe. So schließlich würde ich immer noch etwas manuell ändern müssen, weshalb ich es vorziehe, nur die Magie selbst manuell einstellen.

 
gordon:
Das Problem, das ich mit diesem Ansatz habe, ist, dass ich manchmal identische Experten/Symbole/Zeitrahmen im selben Konto laufen habe. Ich müsste also letztendlich immer noch etwas manuell ändern, weshalb ich es vorziehe, die Magie selbst manuell einzustellen.

Wie wäre es mit der Verwendung von Sekunden? TimeCurrent() gibt eine Zahl zurück, die immer eindeutig sein wird - zumindest außerhalb dieser Sekundenspanne.

- Weisen Sie eine GlobalVariable ID-Nummer für Ihren Experten zu. Geben Sie diese mit WindowExpertName() zurück.

- Verknüpfen Sie diese ID mit einem Inkrementzähler (sollten Sie denselben Experten anhängen) und TimeCurrent()

- Wenn die von TimeCurrent() zurückgegebene Zahl die zulässige Größe überschreitet. Dann verwerfen Sie die Anzahl der Jahre und Monate, bis wir den Modulus der Tage, Stunden, Minuten und Sekunden haben.

 
cameofx:

Wie wäre es mit der Verwendung von Sekunden? TimeCurrent() gibt eine Zahl zurück, die immer eindeutig sein wird - zumindest außerhalb dieser Sekundenspanne.

- Weisen Sie eine ID-Nummer für Ihren Experten zu. Geben Sie diese mit WindowExpertName() zurück.

- Verknüpfen Sie diese ID mit einem Inkrementzähler und TimeCurrent ()

- Wenn die von TimeCurrent() zurückgegebene Zahl die zulässige Größe überschreitet. Dann verwerfen Sie die Anzahl der Jahre und Monate, bis wir den Modulus der Tage, Stunden, Minuten und Sekunden haben.

Denn dann müssen Sie eine Persistenzstufe für diese Magie behalten. Was passiert, wenn Ihr Terminal neu startet? Die Magie wäre dann anders...

 
gordon:

Denn dann müssen Sie eine Persistenzstufe für diese Magie behalten. Was passiert, wenn Ihr Terminal neu startet? Die Magie wäre dann eine andere...

Meine Güte, du hast meine Bearbeitungsgeschwindigkeit übertroffen :)). Ich habe es bearbeitet. Ich habe vergessen zu erwähnen, dass es eine GlobalVariable ist.