criando um 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:
[ ... função hash ... ]

Para registro, fbj fez algo semelhante usando a conhecida função de hash djb2: https://www.mql5.com/en/forum/120034/page2

 
Fui inicialmente inspirado por este http://www.cs.hmc.edu/~geoff/classes/hmc.cs070.200101/homework10/hashfuncs.html, especialmente aquele rotulado como variante CRC (que é muito semelhante ao djb2). Este hash sozinho, embora não tenha sido capaz de produzir facilmente colisões, não me deu confiança suficiente, às vezes apenas muito poucos pedaços diferiram entre hashes de duas cordas similares. Assim, criei três variantes dele com rotação variável de h e adicionei todas as três sub-hashes juntas. Se uma delas colidir, ainda há duas outras calculadas de forma totalmente diferente. Agora cada bit modificado na cadeia de entrada muda mais da metade de todos os bits no hash e todos os bits parecem completamente aleatórios.


O djb2 mencionado em seu link acima poderia ser escrito sem todas as centenas de linhas em torno dele simplesmente 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:
a djb2 mencionada em seu link acima poderia ser escrita sem todas as centenas de linhas em torno dela, simplesmente como: [...]

Não sou especialista em algoritmos de hash, muito menos em djb2 em particular, mas parece que me lembro que a inicialização do valor de hash (a variável h em sua versão) para 5381 é considerada significativa, embora ninguém esteja exatamente certo do motivo.

 
*    magic = makeMagicNumber(name+ Symbol() + Period());
Não é para fazer uma "nitpick", mas você deve ser capaz de usar isto também:
*    magic = makeMagicNumber(WindowExpertName() + Symbol() + Period());
Obrigado por postar o código (e o artigo de haxixe!).

Pergunta - Estou trabalhando em um método para abrir e fechar vários pedidos no mesmo gráfico, no mesmo algoritmo, etc.

Estou me aproximando em 2 passos -

1) gerando uma MN base (que é o que o código acima parece fazer) como um número inteiro. A base seria sempre a mesma para cada gráfico/símbolo/tempo
2) gerando um sufixo específico expresso como ponto decimal para cada fim de pedido específico, uma vez que um sufixo fica inutilizado, tornando-o disponível novamente

Assim, o MN seria XXXXXX.YYY onde X é a base e Y é o sufixo específico. Os sufixos começariam em 0,001 e aumentariam em 0,001 para cada novo envio. A cada final de pedido, ele atribuiria o sufixo mais baixo atualmente não utilizado. Desta forma, eu posso recuperar o MN posteriormente, regenerando a base MN e pedalando através dos sufixos

Parece um pouco complicado demais. Existe uma maneira melhor de fazer isto?

Estou postando o que tenho quando estiver terminado.
 
Como NuB, não tenho certeza porque você quer ou precisa de um MagicNumber 'criptografado' ?
Eu apenas uso os primeiros 5 números para a versão do EA # e os últimos 4 para o número de minutos em que ele está sendo negociado.
 
FourX:
Como um NuB, não tenho certeza porque você quer ou precisa de um MagicNumber 'criptografado'?
Eu apenas uso os primeiros 5 números para a versão do EA # e os últimos 4 para o número de minutos em que ele está sendo comercializado.

Como em seu exemplo o Symbol() se tornaria parte do MN? Você tem um número EA e um número de tempo, mas e quanto ao símbolo?

Eu identifico meus pedidos apenas por MN, meus loops sobre a lista de pedidos comparam apenas OrderMagicNumber(), o seu também teria que verificar o nome do símbolo. Tenho alguns outros scripts independentes que fazem coisas com a lista de ordens, por exemplo, gráficos de equidade de EAs ou copiar as ordens para outra plataforma, todos eles precisam apenas do número mágico para identificar as ordens de um EA específico em um Par específico e um Cronograma específico.

Eu não uso números de série para meus diferentes EAs, eu uso nomes curtos de 4 ou 5 letras para todos os meus EAs. Um EA chamado bola de neve.mq4, por exemplo, teria o nome "neve". Este é um código com fio rígido e nunca mudou. Eu uso este nome curto também para comentários de pedidos.

Então eu tenho 3 coisas: nome curto, Símbolo e Prazo. A maneira mais conveniente de converter isto em um MN é um hash. Eu poderia dar números para meus EAs ao invés de nomes, mas ainda não haveria uma maneira fácil de converter o nome do símbolo em um número. Um Hash simplesmente resolve todos estes problemas de uma só vez.

 
FourX:
Como NuB, não tenho certeza porque você quer ou precisa de um MagicNumber 'criptografado' ?
Eu apenas uso os primeiros 5 números para a versão do EA # e os últimos 4 para o número de minutos em que ele está sendo negociado.

Você também deve ver isto -> https://www.mql5.com/en/forum/120034


O problema que tenho com toda esta abordagem é que às vezes tenho especialistas/símbolos/tempo idênticos correndo na mesma conta. Portanto, eventualmente eu ainda teria que mudar algo manualmente, e é por isso que prefiro apenas definir a magia manualmente.

 
gordon:
O problema que tenho com toda esta abordagem é que às vezes tenho especialistas/símbolos/tempo idênticos correndo na mesma conta. Portanto, eventualmente eu ainda teria que mudar algo manualmente, e é por isso que prefiro apenas definir a magia manualmente.

E quanto ao uso de segundos? TimeCurrent() retorna um número que será sempre único - bem, pelo menos fora desse intervalo de segundos.

- Atribua um número de identificação GlobalVariable ao seu Expert. Devolva-o com WindowExpertName().

- Concatenar esse ID com um contador de incremento (caso você anexe o mesmo especialista) e TimeCurrent()

- Se o número devolvido pelo TimeCurrent() exceder o tamanho permitido. Então descarte a quantidade de anos e meses até que tenhamos o módulo de dias, horas, minutos e segundos.

 
cameofx:

E quanto ao uso de segundos? TimeCurrent() retorna um número que será sempre único - bem, pelo menos fora desse intervalo de segundos.

- Atribua um número de identificação para seu especialista. Devolva-o com WindowExpertName().

- Concatena esse ID com um contador de incremento e o TimeCurrent ()

- Se o número retornado por TimeCurrent() exceder o tamanho permitido. Então descarte a quantidade de anos e meses até que tenhamos o módulo de dias, horas, minutos e segundos.

Porque então você tem que manter um nível de persistência para essa magia. O que acontece se seu Terminal for reiniciado? A magia seria diferente...

 
gordon:

Porque então você tem que manter um nível de persistência para essa magia. O que acontece se seu Terminal for reiniciado? A magia seria diferente...

Meu Deus, você superou minha velocidade de edição :))). Eu a editei. Esqueci de mencionar que é uma GlobalVariable.
Razão: