creating a magic number

 
/**
* 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 function ... ]

For the record, fbj once did something similar using the well-known djb2 hash function: https://www.mql5.com/en/forum/120034/page2

 
I was initially inspired by this http://www.cs.hmc.edu/~geoff/classes/hmc.cs070.200101/homework10/hashfuncs.html, especially the one labeled as CRC variant (which is very similar to djb2). This hash alone, although i was not able to easily produce collisions, did not give me enough confidence, sometimes only very few bits differed between hashes of two similar strings. So i created three variants of it with varying rotation of h and added all 3 sub-hashes together. Should one of them collide then there are still two others calculated in an entirely different way. Now every changed bit in the input string changes more than half of all bits in the hash and all bits look completely random.


the djb2 mentioned in your link above could be written without all the hundreds of lines around it simply as:
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:
the djb2 mentioned in your link above could be written without all the hundreds of lines around it simply as: [...]

I'm not an expert on hash algorithms, let alone on djb2 in particular, but I seem to remember that the initialisation of the hash value (the h variable in your version) to 5381 is considered significant, though no one's exactly sure why.

 
*    magic = makeMagicNumber(name+ Symbol() + Period());
Don't mean to nitpick, but you should be able to use this as well:
*    magic = makeMagicNumber(WindowExpertName() + Symbol() + Period());
Thanks for posting the code (and the hash article!).

Question - I'm working on a method for opening and closing multiple orders on the same chart, same algorithm, etc.

I'm approaching it in 2 steps -

1) generating a base MN (which is what the above code seems to do) as an integer. The base would always be the same for each chart/symbol/timeframe
2) generating a specific suffix expressed as a decimal point for each specific ordersend, once a suffix becomes unused, making it available again

So the MN would be XXXXXX.YYY where X is the base and Y is the specific suffix. The suffixes would start at .001 and increment by .001 for each new send. At each ordersend, it would assign the lowest currently unused suffix. This way, I can retreive the MN later by regenerating the base MN and cycling through the suffixes

Seems a little over-complicated. Is there a better way to do this?

I'm post what I have when it's finished.
 
As a NuB I'm not sure why you would want or need a 'encrypted' MagicNumber ?
I just use the first 5 numbers for the version of the EA # and the last 4 for the number of minutes it is being traded on.
 
FourX:
As a NuB I'm not sure why you would want or need a 'encrypted' MagicNumber ?
I just use the first 5 numbers for the version of the EA # and the last 4 for the number of minutes it is being traded on.

How would in your example the Symbol() become part of the MN? You have an EA number and a Timeframe number, but what about the symbol?

I identify my orders by MN only, my loops over the order list compare only OrderMagicNumber(), yours would have to check for symbol name also. I have a few other independent scripts that do things with the order list, for example plot equity plots of EAs or copy the trades to another platform, they all need only the magic number to identify the trades of a specific EA on a specific Pair and a specific Timeframe.

I don't use serial numbers for my different EAs at all, I use short names of 4 or 5 letters for all my EAs. An EA named snowball.mq4 for example would get the name "snow". This is hard-wired in the code and never changed. I use this short name also for order comments.

So I have 3 things: short name, Symbol and timeframe. The most convenient way to convert this into a MN is a hash. I could give numbers for my EAs instead of names but there still would be no easy way to convert the symbol name into a number. A Hash simply solves all these problems at once.

 
FourX:
As a NuB I'm not sure why you would want or need a 'encrypted' MagicNumber ?
I just use the first 5 numbers for the version of the EA # and the last 4 for the number of minutes it is being traded on.

You should also see this -> https://www.mql5.com/en/forum/120034


The problem I have with this whole approach is that I sometimes have identical expert/symbol/timeframe running in same account. So eventually I would still have to change something manually, which is why I prefer to just set the magic itself manually.

 
gordon:
The problem I have with this whole approach is that I sometimes have identical expert/symbol/timeframe running in same account. So eventually I would still have to change something manually, which is why I prefer to just set the magic itself manually.

What about using seconds ? TimeCurrent() returns number which will always be unique - well, at least outside of that span of second..

- Assign a GlobalVariable ID Number for your Expert. Return it with WindowExpertName().

- Concatenate that ID with an increment counter(should you attach same expert) and TimeCurrent ()

- If number returned by TimeCurrent() exceed size allowed. Then discard the amount of years & months until we have the modulus of days, hours, minutes & seconds.

 
cameofx:

What about using seconds ? TimeCurrent() returns number which will always be unique - well, at least outside of that span of second..

- Assign an ID Number for your Expert. Return it with WindowExpertName().

- Concatenate that ID with an increment counter and TimeCurrent ()

- If number returned by TimeCurrent() exceed size allowed. Then discard the amount of years & months until we have the modulus of days, hours, minutes & seconds.

Because then u have to keep a persistence level for that magic. What happens if your Terminal restarts? The magic would be different...

 
gordon:

Because then u have to keep a persistence level for that magic. What happens if your Terminal restarts? The magic would be different...

Goodness, you beat my editing speed :)). I edited it. Forgot to mention It's a GlobalVariable.
Reason: