Creating random numbers in MQL4

 

Hi


I want to create 4 random numbers at every tick. These could be either 1,2,3,4,5. So at every tick  formula creates 1 random number e.g 1 etc.


How do you do this in MQL4?


Thanks.

 
prweza:

Hi


I want to create 4 random numbers at every tick. These could be either 1,2,3,4,5. So at every tick  formula creates 1 random number e.g 1 etc.


How do you do this in MQL4?


Thanks.


Do you have something to start with?

rand()%5+1 should do the job.

 
Laszlo Tormasi:

Do you have something to start with?

rand()%5+1 should do the job.

I suspect that there are going to be a lot of very geeky responses to this. I'll make a quick one, and then leave this thread because I think that your answer is actually fine.

rand()%5+1 is almost certainly going to be perfect for what the OP wants. But it won't return the numbers 1 to 5 with exactly equal probability, because rand()'s range is 0 to 32767. The numbers 4 and 5 are slightly less likely to be generated than the numbers 1, 2, and 3.

 
JC:

I suspect that there are going to be a lot of very geeky responses to this. I'll make a quick one, and then leave this thread because I think that your answer is actually fine.

rand()%5+1 is almost certainly going to be perfect for what the OP wants. But it won't return the numbers 1 to 5 with exactly equal probability, because rand()'s range is 0 to 32767. The numbers 4 and 5 are slightly less likely to be generated than the numbers 1, 2, and 3.

Would you explain why they are less probable?
 
Mohammad Hossein Sadeghi: Would you explain why they are less probable?

because rand()'s range is 0 to 32767. 32765%5+1=1, 32766%5+1=2 and 32767%5+1=3 so 1-3 will appear 3/32768=0.009% more often than 4-5.

 
whroeder1:

because rand()'s range is 0 to 32767. 32765%5+1=1, 32766%5+1=2 and 32767%5+1=3 so 1-3 will appear 3/32768=0.009% more often than 4-5.

Thank you.
 
Mohammad Hossein Sadeghi:
Would you explain why they are less probable?

... not produced the geeky responses, and alternatives to rand(), which I was expecting.

Another way of putting whroeder1's explanation is that rand() can have 32768 possible values (0-32767), which is not exactly divisible by 5. That means that there are the following number of ways of generating each of the outputs 1 to 5:

OutputWays of generating
16554
26554
36554
46553
56553
(Total)32768

An extreme example would be if you used rand() to generate a number between 1 and 32767 (or 0 and 32766; same thing).

The number 1 would then be produced if rand() was either 0 or 32767. Any other output from 2 to 32767 could only be produced from one value of rand(). Therefore, 1 would be twice as likely to appear in the results as any other number.

 
JC:

... not produced the geeky responses, and alternatives to rand(), which I was expecting.

Another way of putting whroeder1's explanation is that rand() can have 32768 possible values (0-32767), which is not exactly divisible by 5. That means that there are the following number of ways of generating each of the outputs 1 to 5:

Output Ways of generating
1 6554
2 6554
3 6554
4 6553
5 6553
(Total) 32768

An extreme example would be if you used rand() to generate a number between 1 and 32767 (or 0 and 32766; same thing).

The number 1 would then be produced if rand() was either 0 or 32767. Any other output from 2 to 32767 could only be produced from one value of rand(). Therefore, 1 would be twice as likely to appear in the results as any other number.

 I see, the only missing numbers which would lead to reduce the probablity of 4 and 5, are 32768 and 32769. Hence the probablity would decrease by (2/32768=0.00006103515625 about 0.0000006%) or 0.0000003% for each.

A workaround would be limit the output range of rand() to 0-32764, and repeat it if the returned value is outside the range.

 

What about

   GetTickCount()%5 + 1;

?

For %5 the a.m. probelm should be a lot smaller as uint ranges from 0 to 4 294 967 295.

 
Carl Schreiber:

What about

?

For %5 the a.m. probelm should be a lot smaller as uint ranges from 0 to 4 294 967 295.

I thought about it, it doesn't generate random numbers, it will lead to a series with different starting number instead.
 
Mohammad Hossein Sadeghi:

A workaround would be limit the output range of rand() to 0-32764, and repeat it if the returned value is outside the range.

Time for my own geeky suggestion...

Another option is to generate a larger range of random numbers. For example, combining multiple calls to rand() in order to generate a 64-bit (ulong) number. The output % 5 then still has bias, but becomes reduced to infinitesimal proportions. Also provides an answer if you need a range of random numbers larger than 32768.

// Combine 5 calls to MQL rand() in order to generate a 64-bit number.
// (Has to be 5 calls, because rand() is only 15 bits long. We need 4 whole
// calls to rand() plus one to fill up the remaining 4 bits)
ulong Get64bitRand()
{
   ulong r1 = (ulong)rand();
   ulong r2 = (ulong)rand();
   ulong r3 = (ulong)rand();
   ulong r4 = (ulong)rand();
   ulong r5 = (ulong)rand();
   return (r1 << 60) | (r2 << 45) | (r3 << 30) | (r4 << 15) | r5;
}

For the full geek effect, the following GetRandomNumber() will use Windows API calls if available, and only fall back to the above function if "Allow DLL imports" is turned off. Microsoft say that this is "far more random than the data generated by the typical random number generator such as the one shipped with your C compiler"; in other words, far more random than what MT4 will be using.

#define HANDLE32 uint
#define HANDLE64 ulong

#import "Advapi32.dll"
   // 32-bit imports, for MT4 or 32-bit MT5
   int CryptAcquireContextW(HANDLE32 &, int, int, int, int);
   int CryptReleaseContext(HANDLE32, int);
   int CryptGenRandom(HANDLE32, uint, ulong&[]);

   // 64-bit imports, for 64-bit MT5
   int CryptAcquireContextW(HANDLE64 &, int, int, int, int);
   int CryptReleaseContext(HANDLE64, int);
   int CryptGenRandom(HANDLE64, uint, ulong&[]);
#import

// Number of values to get from Windows at a time, to save
// repeated API calls
#define GET_RANDOM_VALUES_PER_BATCH 1024

// Storage of data provided by Windows
ulong glbRandomByteBuffer[];
int glbRandomBufferPointer = -1;

// Get a cache of random bytes from Windows, into glbRandomByteBuffer.
// Returns false if it fails, including if "Allow DLL imports" is turned off
bool GetWindowsCryptRandom()
{
   bool bRetval = false;
   if (TerminalInfoInteger(TERMINAL_DLLS_ALLOWED)) {
      if (TerminalInfoInteger(TERMINAL_X64)) {
         HANDLE64 handle;
         if (CryptAcquireContextW(handle, 0, 0, 1, 0)) {
            ArrayResize(glbRandomByteBuffer, GET_RANDOM_VALUES_PER_BATCH);
            if (CryptGenRandom(handle, GET_RANDOM_VALUES_PER_BATCH * 8, glbRandomByteBuffer)) {
               bRetval = true;
            }
            CryptReleaseContext(handle, 0);
         }
      } else {
         HANDLE32 handle;
         if (CryptAcquireContextW(handle, 0, 0, 1, 0)) {
            ArrayResize(glbRandomByteBuffer, GET_RANDOM_VALUES_PER_BATCH);
            if (CryptGenRandom(handle, GET_RANDOM_VALUES_PER_BATCH * 8, glbRandomByteBuffer)) {
               bRetval = true;
            }
            CryptReleaseContext(handle, 0);
         }
      }
   }
   return bRetval;
}

// Combine 5 calls to MQL rand() in order to generate a 64-bit number
// (Has to be 5 calls, because rand() is only 15 bits long. We need 4 whole
// calls to rand() plus one to fill up the remaining 4 bits)
ulong Get64bitRand()
{
   ulong r1 = (ulong)rand();
   ulong r2 = (ulong)rand();
   ulong r3 = (ulong)rand();
   ulong r4 = (ulong)rand();
   ulong r5 = (ulong)rand();
   return (r1 << 60) | (r2 << 45) | (r3 << 30) | (r4 << 15) | r5;
}

// Gets a 64-bit random number, using the Windows API if available, or 
// by combining calls to rand() if API calls are not available
ulong GetRandomNumber()
{
   ulong value;
   
   // Try using Windows API if this is the first call, or if we 
   // have run out of the random numbers previously requested
   if (glbRandomBufferPointer < 0 || glbRandomBufferPointer >= GET_RANDOM_VALUES_PER_BATCH) {
      if (GetWindowsCryptRandom()) {
         glbRandomBufferPointer = 0;
      } else {
         glbRandomBufferPointer = -1;      
      }
   }

   // If the Windows call succeeded, use up one of the values returned.
   // Failing that, generate a 64-bit number by combining calls to rand()
   if (glbRandomBufferPointer >= 0) {
      value = glbRandomByteBuffer[glbRandomBufferPointer];
      glbRandomBufferPointer++;
   } else {
      value = Get64bitRand();
   }
   return value;
}
Reason: