How NOT to Hack MQL4

 

Having been directed to an old post about making MT4 do what you really want,
https://www.mql5.com/en/forum/124688

the code below was my first effort. Since it is a weekend (with no incoming ticks) I thought I would put the code in the EA init function. What is interesting about this code is that by changing the chart timeframe from within the EA, the EA gets re-initialized and loops continuously. And by continuously I mean you can’t shut it down. This is LOL funny. It was a bit less funny when I shut down MT4 using windows, restarted it, and the EA was still running in an infinite loop, despite the EA running being disabled.

Nevertheless it’s still pretty funny. I figured I would have to hack some start up file somewhere. Then I realized all I had to do was comment out the offending code in the EA, recompile it, and then run MT4. Easier than I thought. It’s too funny not to share. (NOTE: This one is an EA, the one several posts below is a script).

// ******** WARNING *********

// DO NOT RUN THIS CODE
// It will hang up your system PERMANENTLY

#include <WinUser32.mqh>
#include <stdlib.mqh>

#import "user32.dll"
   int GetAncestor (int hwnd, int gaFlags);
#import


#define SCRIPTNAME "Refresher - To Update Chart Data"

#define WMCMD_PERIOD_M1       33137          // 0x8171
#define WMCMD_PERIOD_M5       33138          // 0x8172
#define WMCMD_PERIOD_M15      33139          // 0x8173
#define WMCMD_PERIOD_M30      33140          // 0x8174

#define WMCMD_PERIOD_H4       33136          // 0x8170
#define WMCMD_PERIOD_D1       33134          // 0x816E
#define WMCMD_PERIOD_W1       33141          // 0x8175

int timeFrame[]= {WMCMD_PERIOD_M1, WMCMD_PERIOD_M5, WMCMD_PERIOD_M15,
                  WMCMD_PERIOD_M30,WMCMD_PERIOD_H4, WMCMD_PERIOD_D1,  WMCMD_PERIOD_W1};

/* You can't conveniently write this code as a script because changing the timeframe causes
   the script termination dialog box to pop up, halting the automatic execution of the steps. */

int init(){

   Print( "REFRESHER started" );
   
   if( !IsDllsAllowed() ){
      MessageBox( "If you want to update the chart you must check\nthe box\n\n                    ALLOW DLL IMPORTS\n\n"
                + "which can be found in the TOOLS|OPTIONS menu", SCRIPTNAME, MB_OK );
      return( 0 );
   } 
   
   int hWnd   = WindowHandle( Symbol(),0 );
   int mainHwnd = GetAncestor(hWnd,2);
   int length = ArraySize(timeFrame);
   
   for( int n=0; n<length; n++ ){
      Comment(n + " : " + Period() );
      Print(  n + " : " + Period() );
      PostMessageA( mainHwnd, WM_COMMAND, timeFrame[n], 0 );
      Sleep(5000);
      PlaySound("tick.wav");
   }
   
   return(0);
}

int start(){
   return( 0 );
}


 
I'm just wondering, did you go through all this just to download some bars for different time-frames?
 
ubzen:
I'm just wondering, did you go through all this just to download some bars for different time-frames?
Yup :-)
 
Interesting, I think I better test a few things out then. Will report my findings (here) later.
 
I try to access the bars for the relevant timeframe and check for an Error 4066 "Requested history data in updating state" (thanks to WHRoeder) :-)
 

Ok, so I attached the below EA to M5-Chart (EURUSD), deleted all my .hst files, launched mt4 again. And got the following results. Does the Win32 get more bars or something, what am I missing?

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int i, Period_Ray[9]={1, 5, 15, 30, 60, 240, 1440, 10080, 43200};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void init(){
    //~~~~~~~~~~~~~~~~~~~~
    for(i=ArraySize(Period_Ray)-1; i>=0; i--){
        iCustom(Symbol(),Period_Ray[i],"iShow",0,1);
        Sleep(5000);
    }
    //~~~~~~~~~~~~~~~~~~~~
    for(i=ArraySize(Period_Ray)-1; i>=0; i--){
        Print( Symbol()+"_Bars_Period_"+Period_Ray[i]+"="+iBars(Symbol(),Period_Ray[i]) );
    }
    //~~~~~~~~~~~~~~~~~~~~
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void start(){}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 
ubzen:
I'm just wondering, did you go through all this just to download some bars for different time-frames?

No, although that is all that script was trying to do. No, the idea is to access the deeper functionality that is not otherwise available. Things like self-terminating an EA, refreshing data, ...

Now chipdude mentioned in a recent post

https://www.mql5.com/en/forum/137188

that you can refresh data using ArrayCopyRates(). So I tried it (see below).

#define SCRIPTNAME "Refresher - To Update Chart Data"

// NOTE: these auto-adjust so you can freely change the number of timeFrames and Symbols without breaking the code 
int timeFrames[]= {PERIOD_M1, PERIOD_M5, PERIOD_M15, PERIOD_M30, PERIOD_H1, PERIOD_H4, PERIOD_D1, PERIOD_W1};
string symbols[]= {"AUDUSD", "EURUSD", "CADJPY", "CADCHF"}; 

double rates[][6];

int start(){

   Print( "REFRESHER started" );
   
   // NOTE: these auto-adjust so you can freely change the number of timeFrames and Symbols without breaking the code 
   int times = ArraySize(timeFrames);
   int pairs = ArraySize(symbols);
   
   for( int t=0; t<times; t++ ){
      for( int p=0; p<pairs; p++ ){
         string str = "Updating " + symbols[p] + " on " + timeFrames[t] + " minutes";
         
         for( int n=0; n<20; n++ ){
         
            // This next function apparently REQUESTs the data to be updated if it is not current
            int result = ArrayCopyRates( rates, symbols[p], timeFrames[t] );
         
            Comment( str + " : Try " + n );
            Print(   str + " : Try " + n );
            if( result > -1 )
               break;
            Sleep(1000);
         }
      }
   }
   
   return(0);
}


int deinit(){
   Comment("");   // just to be tidy and not leave the screen messy
}

That's not bad. It does update the HST files (by actual test) with today's date and the file size is increased. HOWEVER, I tried it with a pair that was last updated in May of last year. Sure enough the file size increased from 592kB to 946kB but when I actually opened that exact chart and did a right mouse click REFRESH on the chart the Journal showed 22,000 bars imported! So the script above is of some use but still not really getting me the full quality of data that I want.

 
  1. ArrayCopyRates takes time. Open EACH pair as a seperate global array in init so there'll be data by the time you get a new tick in start. Doing it once will not keep the other charts updated.
  2. You can NOT use the message box in init as init can NOT wait. The postmessage code must be in start. Send ONE message and exit. You'll go through a deinit/init cycle, then on the next tick you can switch to another.
 
WHRoeder:
  1. ArrayCopyRates takes time. Open EACH pair as a seperate global array in init so there'll be data by the time you get a new tick in start. Doing it once will not keep the other charts updated.
  2. You can NOT use the message box in init as init can NOT wait. The postmessage code must be in start. Send ONE message and exit. You'll go through a deinit/init cycle, then on the next tick you can switch to another.

I imagine that having once called ArrayCopyRates() the fact that I then overwrite the pointer to the data is irrelevant (in my script two posts above). It will still have posted a message in the queue to update the data and the data will arrive eventually. I ran the above script on one of my accounts and then manually went through all 8 charts (and 9 timeframes) with a right click Refresh to really get the full data. I fully appreciate that the data will only be updated to that point in time.

I will try using ArrayCopyRates and not immediately overwriting the pointer (although I don't hold out much hope that it will actually give me any more data than the current method).

Thank you for your post.

 

So I did the test with the script below ...

double rateA[][6];
double rateB[][6];

int start(){

   // This next function REQUESTs the data to be updated if it is not current
   int result = ArrayCopyRates( rateA, "USDCHF", PERIOD_M15 );
   Sleep(20000);
   PlaySound("tada.wav");
   result = ArrayCopyRates( rateB, "EURCAD", PERIOD_M5 );
   Sleep(20000);
   PlaySound("tada.wav");
      
   return(0);
}


int deinit(){
   Comment("");   // just to be tidy and not leave the screen messy
}

As before this does update the data but when you open the actual chart and timeframe and do a right click Refresh then more bars are downloaded (as seen in the journal). I interpret this to mean that the ArrayCopyRates function does request a download of data, but probably just enough to fill a visible chart window. The Refresh command, on the other hand possibly downloads as much broker data as exists, subject to the limitation of the chart size (bars in history limit). I of course want the maximum data as supplied by the Refresh command.

 
ubzen:

Ok, so I attached the below EA to M5-Chart (EURUSD), deleted all my .hst files, launched mt4 again. And got the following results. Does the Win32 get more bars or something, what am I missing?

The original post doesn't work anyway. The ArrayCopyRates method is the best so far but still not as good as a Refresh. I tried your method but of course I fell flat at the iCustom command for iShow. Is there anything special about your custom indicator or will pretty much any old custom indicator do? I thought perhaps that a large moving average might require a larger history to get itself ready to be visible.

I am currently trying the old Visual C++ version 6 Spy++ to see if I can capture a command sequence to emulate a Refresh. There are just so many Windows messages ...