crear un número mágico

 
/**
* 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:
[ ... función hash ... ]

Para que conste, fbj hizo una vez algo similar utilizando la conocida función hash djb2: https://www.mql5.com/en/forum/120034/page2

 
Inicialmente me inspiré en este http://www.cs.hmc.edu/~geoff/classes/hmc.cs070.200101/homework10/hashfuncs.html, especialmente el etiquetado como variante CRC (que es muy similar a djb2). Este hash por sí solo, aunque no era capaz de producir colisiones fácilmente, no me daba suficiente confianza, a veces sólo unos pocos bits diferían entre los hashes de dos cadenas similares. Así que creé tres variantes del mismo con una rotación variable de h y añadí los 3 sub-hashes juntos. En caso de que uno de ellos colisione, todavía hay otros dos calculados de forma totalmente diferente. Ahora cada bit cambiado en la cadena de entrada cambia más de la mitad de todos los bits del hash y todos los bits parecen completamente aleatorios.


el djb2 mencionado en tu enlace anterior podría escribirse sin todos los cientos de líneas que lo rodean simplemente como:
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:
el djb2 mencionado en tu enlace anterior podría escribirse sin todos los cientos de líneas que lo rodean simplemente como: [...]

No soy un experto en algoritmos hash, y mucho menos en djb2 en particular, pero creo recordar que la inicialización del valor hash (la variable h en tu versión) a 5381 se considera significativa, aunque nadie sabe exactamente por qué.

 
*    magic = makeMagicNumber(name+ Symbol() + Period());
No quiero ser puntilloso, pero esto también debería poder usarse:
*    magic = makeMagicNumber(WindowExpertName() + Symbol() + Period());
Gracias por publicar el código (y el artículo de hash).

Pregunta - Estoy trabajando en un método para abrir y cerrar múltiples órdenes en el mismo gráfico, el mismo algoritmo, etc.

Estoy abordando en 2 pasos -

1) generar una base MN (que es lo que el código anterior parece hacer) como un número entero. La base sería siempre la misma para cada gráfico/símbolo/marco de tiempo
2) generar un sufijo específico expresado como un punto decimal para cada fin de orden específico, una vez que un sufijo queda sin usar, haciéndolo disponible de nuevo

Así, el MN sería XXXXXX.YYY donde X es la base e Y es el sufijo específico. Los sufijos comenzarían en 0,001 y se incrementarían en 0,001 para cada nuevo envío. En cada envío de órdenes, se asignaría el sufijo más bajo no utilizado actualmente. De esta manera, puedo recuperar el MN más tarde regenerando el MN base y recorriendo los sufijos

Parece un poco complicado. ¿Hay alguna forma mejor de hacer esto?

Voy a publicar lo que tengo cuando esté terminado.
 
Como NuB no estoy seguro de por qué querrías o necesitarías un MagicNumber 'encriptado'?
Sólo uso los primeros 5 números para la versión del EA # y los últimos 4 para el número de minutos que se está negociando en.
 
FourX:
Como NuB no estoy seguro de por qué querrías o necesitarías un MagicNumber 'encriptado' ?
Yo sólo uso los primeros 5 números para la versión del EA # y los últimos 4 para el número de minutos en que se está negociando.

¿Cómo es que en su ejemplo el Symbol() forma parte del MN? Usted tiene un número de EA y un número de Timeframe, pero ¿qué pasa con el símbolo?

Yo identifico mis órdenes sólo por MN, mis bucles sobre la lista de órdenes comparan sólo OrderMagicNumber(), el tuyo tendría que comprobar también el nombre del símbolo. Tengo algunos otros scripts independientes que hacen cosas con la lista de órdenes, por ejemplo trazar gráficos de equidad de los EAs o copiar las operaciones a otra plataforma, todos ellos necesitan sólo el número mágico para identificar las operaciones de un EA específico en un par específico y un marco de tiempo específico.

Yo no uso números de serie para mis diferentes EAs en absoluto, uso nombres cortos de 4 o 5 letras para todos mis EAs. Un EA llamado snowball.mq4, por ejemplo, tendría el nombre "snow". Este nombre está grabado en el código y nunca se cambia. También utilizo este nombre corto para los comentarios de las órdenes.

Así que tengo 3 cosas: nombre corto, Symbol y timeframe. La forma más conveniente de convertir esto en un MN es un hash. Podría dar números para mis EAs en lugar de nombres, pero todavía no habría una manera fácil de convertir el nombre del símbolo en un número. Un Hash simplemente resuelve todos estos problemas a la vez.

 
FourX:
Como NuB no estoy seguro de por qué querrías o necesitarías un MagicNumber 'encriptado' ?
Yo sólo uso los primeros 5 números para la versión del EA # y los últimos 4 para el número de minutos que se está negociando en.

También debería ver esto -> https://www.mql5.com/en/forum/120034


El problema que tengo con todo este enfoque es que a veces tengo idéntico experto/símbolo/marco de tiempo corriendo en la misma cuenta. Así que eventualmente todavía tendría que cambiar algo manualmente, por lo que prefiero sólo establecer la magia en sí manualmente.

 
gordon:
El problema que tengo con todo este enfoque es que a veces tengo idéntico experto/símbolo/marco de tiempo corriendo en la misma cuenta. Así que eventualmente todavía tendría que cambiar algo manualmente, por lo que prefiero sólo establecer la magia en sí manualmente.

¿Qué tal si utilizamos los segundos? TimeCurrent() devuelve un número que siempre será único - bueno, al menos fuera de ese lapso de segundos..

- Asigne un número de identificación GlobalVariable para su Experto. Devuélvalo con WindowExpertName().

- Concatene ese ID con un contador de incrementos (si adjunta el mismo experto) y TimeCurrent()

- Si el número devuelto por TimeCurrent() excede el tamaño permitido. Entonces descarta la cantidad de años y meses hasta que tengamos el módulo de días, horas, minutos y segundos.

 
cameofx:

¿Qué tal si utilizamos los segundos? TimeCurrent() devuelve un número que siempre será único - bueno, al menos fuera de ese lapso de segundos.

- Asigna un número de identificación para tu experto. Devuélvalo con WindowExpertName().

- Concatenar ese ID con un contador de incremento y TimeCurrent ()

- Si el número devuelto por TimeCurrent() excede el tamaño permitido. Entonces descarte la cantidad de años y meses hasta tener el módulo de días, horas, minutos y segundos.

Porque entonces tienes que mantener un nivel de persistencia para esa magia. ¿Qué pasa si tu Terminal se reinicia? La magia sería diferente...

 
gordon:

Porque entonces tienes que mantener un nivel de persistencia para esa magia. ¿Qué pasa si tu Terminal se reinicia? La magia sería diferente...

Dios, has superado mi velocidad de edición :)). Lo he editado. Olvidé mencionar que es una GlobalVariable.