MQL #include - losing my externals - am I missing something or what ?...

 

OK,

Library development - its simple right ?...

I have a library file in my "include" folder, its not a header with function prototypes, its a proper in-line #include of a source file. Its a self-contained set of code. It goes like this...

#property library

// Pseudo constants
string MessageLineItemDelimiter = " : ";

// Pseudo global items
bool Error = false;
string ErrorText = "";

void PrintErrorText()
{
Print ( "PrintErrorText:Start" );
Print ( ErrorText );
Print ( "PrintErrorText:End" );
}

The compiler likes it, includes it in my EA no problem, and we're good to go.

However...

In my EA I now add some parameters using the "extern" declaration like this...

// Parameters

extern double P_OrderLotSize = 0.1;
extern int P_OrderMagic = 123888321;

My EA compiles & I can drag it onto a chart; BUT; there are no parameters available - all of those "extern" items are missing...

After much head scratching I comment out the include and find the parameters returned. So again after trial & error I discover that if I have ANY variables declared in my include then I lose all of my "extern" parameters to the EA. I can include the file so long as it doesn't have any variables declare within it.

The following causes it to work properly...

#property library

// Pseudo constants
// COMMENTED OUT string MessageLineItemDelimiter = " : ";

// Pseudo global items
// COMMENTED OUT bool Error = false;
// COMMENTED OUT string ErrorText = "";

void PrintErrorText()
{
Print ( "PrintErrorText:Start" );
// COMMENTED OUT Print ( ErrorText );
Print ( "PrintErrorText:End" );
}

How on earth do I get around this ?... I want to include a nice library of self-contained functions & variables that I can still access and use in my EA, but it seems this is not possible...

I've even placed the #include line AFTER my "extern" variables but still no joy.

SURELY this is not correct. If an "#include" directive replaces the line of code with the contents of the included file, then this should be simple. If I add variables to my EA this doesn't lose all of the extern items. I don't want to add the variables of my "libraray" file into my main EA - surely that's the whole purpose of an "included" file..

SO... Why am I losing my parameters when I include a file with modular scope variables ? (even if I declare them extern in the included file I still lose my parameters)

This is madness... HELP !...

 
TheForexDevMan wrote >>

Everything in the library are functions independent of each other; no global var for sharing across functions

 

Remove this line:

#property library
 
Rosh wrote >>

Remove this line:

Thanks Rosh,

I had Just now (5 minutes ago) figured out that the line caused the externals to be lost.

Cheers.

 
ronaldosim wrote >>

Everything in the library are functions independent of each other; no global var for sharing across functions

Not so. Any Library can contain global vars AND they can be shared across all functions.

To DevMan, and anyone else that wants to create libraries, here is some info to help you.


You can use mq4 files like a library, but as you see you can not use the "#property library" preprocessor directive for that.

The #include directive is for including only text files like mq4 or mqh (Function library header file). Also depending how you reference your file, the preprocessor will look

for the file either in the default location - the \experts\include folder or in the \experts folder. (Look in help on "#include" for more info)

For example:

#include <myFile.mqh> Only the \experts\include folder (default location) is searched.

#include "myFile.mqh" Only the current directory is searched, where the source ex4 file is. For experts, it would of course be the experts folder.

As for your libraries, the standard and recomended way to store and use them is this:

1) Create your library as a normal mq4 file with the "#property library" directive at the top. Then compile and place both mq4 and ex4 files in the \experts\libraries folder.

For example lets say 2 of your functions look like this: (This will be useful for seeing how the header file you will create relates to your library functions)

// MyLibrary.mq4

#property library

bool Timer_CheckIfWaiting(int waitType)
{
return(arrTimer[waitType][BOOL_WAITING]);
}

int Timer_GetTimer(int waitType)
{
return(arrTimer[waitType][INT_TIME_TO_WAIT]);
}

// more functions...

// end of file

2) Then you need to create .mqh header file that lists the full description of each function, as the help file states: For compiler to be able to form the imported function call and pass parameters in a proper way, the full description of functions is needed. This description is simply the full definition of each function ending in a semicolin like this:

bool Timer_CheckIfWaiting(int waitType);

int Timer_GetTimer(int waitType);

string Timer_GetCountDownString(int waitType);

etc....

3) To finish your header file you need to put an #import directive at the top of your file followed by the name of your compiled ex4 file in quotes like this:

#import "Timers.ex4"

You also need to put an 'emtpy' #import statement at the bottom of your file. So you header file should now look like this:

#import "Timers.ex4"

bool Timer_CheckIfWaiting(int waitType);

string Timer_GetCountDownString(int waitType);

int Timer_GetTimer(int waitType);

// more function definitions...

#import

Your header file is done, but it must be copied to the \experts\include folder.

To use your Library, all you need to do is use an #include directive near the top of your EA to include your library header file- like this:

#include <MyLibrary.mqh>

Now when you complile your EA the content of the header file will be included in your ex4 file, but it will not include the library itself.

Your compiled "MyLibrary.ex4" file must remain in the library folder as your EA looks for this file at runtime.

So if you wanted to share your new EA with someone you would give them both the EA .ex4 file and the library .ex4 file.

Just be sure that they put the library file in the libraries folder or your EA won't work. By the way, you don't need to give them the header file for the reason I gave above.

But as you add functions to your library, just remember to also add the new function definitions to your header file.


Hope this helps...

 
vangrosh wrote >>

Not so. Any Library can contain global vars AND they can be shared across all functions.

To DevMan, and anyone else that wants to create libraries, here is some info to help you.


You can use mq4 files like a library, but as you see you can not use the "#property library" preprocessor directive for that.

The #include directive is for including only text files like mq4 or mqh (Function library header file). Also depending how you reference your file, the preprocessor will look

for the file either in the default location - the \experts\include folder or in the \experts folder. (Look in help on "#include" for more info)

For example:

#include <myFile.mqh> Only the \experts\include folder (default location) is searched.

#include "myFile.mqh" Only the current directory is searched, where the source ex4 file is. For experts, it would of course be the experts folder.

As for your libraries, the standard and recomended way to store and use them is this:

1) Create your library as a normal mq4 file with the "#property library" directive at the top. Then compile and place both mq4 and ex4 files in the \experts\libraries folder.

For example lets say 2 of your functions look like this: (This will be useful for seeing how the header file you will create relates to your library functions)

// MyLibrary.mq4

#property library

bool Timer_CheckIfWaiting(int waitType)
{
return(arrTimer[waitType][BOOL_WAITING]);
}

int Timer_GetTimer(int waitType)
{
return(arrTimer[waitType][INT_TIME_TO_WAIT]);
}

// more functions...

// end of file

2) Then you need to create .mqh header file that lists the full description of each function, as the help file states: For compiler to be able to form the imported function call and pass parameters in a proper way, the full description of functions is needed. This description is simply the full definition of each function ending in a semicolin like this:

bool Timer_CheckIfWaiting(int waitType);

int Timer_GetTimer(int waitType);

string Timer_GetCountDownString(int waitType);

etc....

3) To finish your header file you need to put an #import directive at the top of your file followed by the name of your compiled ex4 file in quotes like this:

#import "Timers.ex4"

You also need to put an 'emtpy' #import statement at the bottom of your file. So you header file should now look like this:

#import "Timers.ex4"

bool Timer_CheckIfWaiting(int waitType);

string Timer_GetCountDownString(int waitType);

int Timer_GetTimer(int waitType);

// more function definitions...

#import

Your header file is done, but it must be copied to the \experts\include folder.

To use your Library, all you need to do is use an #include directive near the top of your EA to include your library header file- like this:

#include <MyLibrary.mqh>

Now when you complile your EA the content of the header file will be included in your ex4 file, but it will not include the library itself.

Your compiled "MyLibrary.ex4" file must remain in the library folder as your EA looks for this file at runtime.

So if you wanted to share your new EA with someone you would give them both the EA .ex4 file and the library .ex4 file.

Just be sure that they put the library file in the libraries folder or your EA won't work. By the way, you don't need to give them the header file for the reason I gave above.

But as you add functions to your library, just remember to also add the new function definitions to your header file.


Hope this helps...

Vangosh. thks for the insight; i have done up the libraries exactly as u have mentioned but minus global vars (except #define);

i am about to need a global var to handle fractional pips in all the lib ftns, so how do u do that? eg

#property library

int Pip;

//how do I change the value of Pip according to a bool switch that I will use eg if (UseFractionalPips) Pip=10; else Pip=1

bool Timer_CheckIfWaiting(int waitType)
{

//eg let's say we need to use global var Pip here
return(arrTimer[waitType][BOOL_WAITING]);
}

int Timer_GetTimer(int waitType)
{

//eg let's say we need to use global var Pip here
return(arrTimer[waitType][INT_TIME_TO_WAIT]);
}

 
ronaldosim wrote >>

Vangosh. thks for the insight; i have done up the libraries exactly as u have mentioned but minus global vars (except #define);

i am about to need a global var to handle fractional pips in all the lib ftns, so how do u do that? eg

#property library

int Pip;

//how do I change the value of Pip according to a bool switch that I will use eg if (UseFractionalPips) Pip=10; else Pip=1

bool Timer_CheckIfWaiting(int waitType)
{

//eg let's say we need to use global var Pip here
return(arrTimer[waitType][BOOL_WAITING]);
}

int Timer_GetTimer(int waitType)
{

//eg let's say we need to use global var Pip here
return(arrTimer[waitType][INT_TIME_TO_WAIT]);
}

You use the globar var just like any other. It doesn't matter if it's in your library or in your main EA. Any var outside of function scope is global no matter where you declare it. Here is some of my code so you can see:

#property library

// Array Rows shown just for reference. #defines are really put in main EA.
//#define WAIT_TYPE_ORDER1   0
//#define WAIT_TYPE_ORDER2   1
//#define WAIT_TYPE_FILTER   2
//#define WAIT_TYPE_ANOTHER  3
// add as many wait types (timers) as you want.

// Array Colums
#define BOOL_WAITING       0
#define INT_TIME_TO_WAIT   1

int arrTimer[25][2]; //Global array var that is used in the functions below. The amount of timers is hardcoded to 25 for now.

void Timer_Init()
{
ArrayInitialize(arrTimer,-1);
}

bool Timer_CheckIfWaiting(int waitType)
{
   return(arrTimer[waitType][BOOL_WAITING]);
}

string Timer_GetCountDownString(int waitType)
{
   int      secondsMod;
   int      waitCountSec;
   int      waitCountMin;      
   string   leadingZero;
   string   trailingZero;
   string   countDown;
   
   waitCountSec = TimeLocal() - Timer_GetTimer(waitType);         
   waitCountSec = -waitCountSec; 
   
   if (waitCountSec <= 0) return("0:00");
   
   waitCountMin = waitCountSec/60;
     
   // Modulo formula: a - n * floor(a / n): waitCountSec - 60 * MathFloor(waitCountSec / 60); 
   secondsMod = waitCountSec % 60;

   if (secondsMod == 0) trailingZero = "0";
      else trailingZero = "";            
   if (secondsMod > 0 && secondsMod < 10) leadingZero = "0";
      else leadingZero = "";
     
   countDown = StringConcatenate(waitCountMin, ":", leadingZero, secondsMod, trailingZero);
     
   return(countDown);
}

int Timer_GetTimer(int waitType)
{   
   return(arrTimer[waitType][INT_TIME_TO_WAIT]);
}

void Timer_StartWaiting(int minutes, int waitType)
{                 
   minutes*=60;       
   arrTimer[waitType][BOOL_WAITING] = true;
   arrTimer[waitType][INT_TIME_TO_WAIT] = TimeLocal() + minutes; 
}

Here is some of the main EA code that uses my Timers library. I included the list of externs just so you can get an idea of what is availible to do in my EA.

// Main EA

#include <Timers.mqh>

// Timer Wait Types. Can have up to 25 types.
#define TIMER_ORDER1   0
#define TIMER_ORDER2   1
#define TIMER_FILTER   2

// External vars
extern string  UserNotes;
extern bool    EnableTrading = false;
extern int     MaxOrders = 3;
extern int     OrderWaitTime = 2; //Used with Timers
extern double  Lots = 0.01;
extern int     TakeProfit = 20;
extern int     StopLoss = 70;
extern int     TrailingStop = 0;
extern bool    AutoLotIncrease = true;
extern double  LotIncreaseStepSize = 0.01;
extern bool    ResetLots = false;
extern int     MoneyManagement = true;
extern double  MM_MarginLevel = 2000;
extern string  TimePeriod_Note = "Use only: 5=M5, 15=M15, 30=M30, 60=H1";
extern int     TimePeriod = 15;
extern int     NumBars = 10;
extern int     FilterPeriod = 60;
extern int     FilterWaitTime = 10; //Used with Timers
extern int     FilterStochastic = false;
extern int     StochSlowing = 3;
extern int     FilterCCI = false;
extern int     PeriodCCI = 14;
extern int     FilterRSI = false;
extern int     PeriodRSI = 9;
extern int     HighRSI = 70;
extern int     LowRSI = 27;
extern int     FilterWPR = false;
extern int     PeriodWPR = 14;
extern int     HighWPR = -20;
extern int     LowWPR = -80;
extern int     FilterMomentum = true;
extern int     PeriodMomentum = 4;
extern int     FilterMA = true;
extern int     PeriodMASlow = 12;
extern int     PeriodMAFast = 3;
extern int     PPM_1 = 3;
extern int     PPM_5 = 5;
extern int     PPM_15 = 15;
extern int     PPM_X = 60;
extern int     MagicNumber = 5252;
extern bool    GV_DeleteAll = false;
 

Main EA continued:

// Some notable global vars
bool     orderWaiting = false;
bool     filterWaiting = false;
string   trendInfo;
string   comment1;
string   comment2;
string   comment3;
string   statusMode;

// MT Client Global Variable names
string   GV_OrderTicket;
string   GV_OrderOpen;
string   GV_LotSize;

void CheckWaiting() // Checks for running timers and will help with displaying timer info on screen
{   
   if (Timer_GetCountDownString(TIMER_ORDER1) != "0:00")
      comment2 = "\n>>  Order Pause: " + Timer_GetCountDownString(TIMER_ORDER1) + " minutes...";
   
   if (Timer_GetCountDownString(TIMER_ORDER1) == "0:00")
   {
      orderWaiting = false;
      comment2 = "";
   }
   
   if (Timer_GetCountDownString(TIMER_FILTER) != "0:00")
      comment3 = "\n>>  Order Pause: " + Timer_GetCountDownString(TIMER_FILTER) + " minutes...";
   
   if (Timer_GetCountDownString(TIMER_FILTER) == "0:00")
   {
      filterWaiting = false;
      comment3 = "";      
   }                          
}

// Example of how I do my filters (indicators)
bool RSIFilter(string orderType)
{
   if (FilterRSI == false) return(true);
   
   double rsiValue = iRSI(NULL,FilterPeriod,PeriodRSI,PRICE_CLOSE,0);      
   
   //Print(">> rsiValue: ", rsiValue);
   
   if (orderType == "Buy")
   {
      if (rsiValue > HighRSI) return(false);
         else return(true);
   }
   
   if (orderType == "Sell")
   {
      if (rsiValue < LowRSI) return(false);
         else return(true);
   }
}

// Main function
void Main()
{
   // Lots of code removed to show trend info and to handle displaying of Comments

   Comment(trendInfo, comment1, comment2, comment3);
   
   if (EnableTrading == false) return;
   if (GetOpenOrders() == MaxOrders) return; //Print(">> No more orders allowed");
   if (orderWaiting == true || filterWaiting == true) return;
   
   GoForTrade();

} // end of Main

// GoForTrade() function
void GoForTrade()
{ 

// custom entry logic removed
// then I add my filter checks as needed

               if (MAFilter("Buy") == false)
               {
                  comment1 = "\n>>  Buy Order prevented by MA Filter at " + timeDate + " " + timeMinutes;
                  Print(comment1);
                                 
                  Timer_StartWaiting(FilterWaitTime,TIMER_FILTER);
                  filterWaiting = true;
                  return;
               }  
                              
               if (MomentumFilter("Buy") == false)
               {
                  comment1 = "\n>>  Buy Order prevented by Momentum Filter at " + timeDate + " " + timeMinutes;
                  Print(comment1);
                                 
                  Timer_StartWaiting(FilterWaitTime,TIMER_FILTER);
                  filterWaiting = true;
                  return;
               } 
  
               // several more filter checks...
               // if everything checks out, it will open an order.

               BuyOrder(TakeProfit, StopLoss);

// Same code for selling here...

}
 

Main EA Continued...

// Buy order with much code removed...
void BuyOrder(int takeProfitPips, int stopLossPips)
{
   double dblStopLoss;
   double objStopLoss;
   double dblTakeProfit;
   string strStopLoss;
   string name;
   string description;
   string order_Comment;
   int    order_Ticket;      
   
   MoneyManagementCheck();     
         
   if (IsTradeAllowed())
      order_Ticket = OrderSend(); 
   else return;
   
   // if order success..
          
      Print(">> Buy Order # ", order_Ticket, " Sent at ", timeMinutes," ", order_Comment);
      comment1 = "\n>>  Buy Order # " + order_Ticket + " Sent at " + timeMinutes + "\n>>  " + order_Comment;      
      
      SaveOrderTicket(order_Ticket);
      
      MoneyManagementCheck();            

      Lots = GlobalVariableGet(Symbol() + "_LotSize");
      Timer_StartWaiting(OrderWaitTime,TIMER_ORDER1); // Call to Timer library
      orderWaiting = true;  // Used for Timers

   }
}

//Not related to timers but I manage multible orders by saving info from each order to Client GlobalVariables
void SaveOrderTicket(int order_Ticket)
{   
   string leadingZero;
   
   for (int i=1; i<MaxOrders+1; i++)
   {     
      if (i > 0 && i < 10) leadingZero = "0";
         else leadingZero = "";
            
      if (GlobalVariableGet(Symbol() + "_OrderOpen_" + leadingZero + i) == 0)
      {
         GlobalVariableSet(Symbol() + "_OrderOpen_" + leadingZero + i, 1);
         GlobalVariableSet(Symbol() + "_OrderTicket_" + leadingZero + i, order_Ticket);
         break;      
      }      
   }
}

// Then you can work with your open orders like this
void CheckIfOpenOrder_Closed()
{
   int      order_Ticket;
   datetime closeTime;
   string   leadingZero;
   double   stopLoss = 0;
   double   takeProfit = 0;
   double   pips;
   double   profit;
   string   order_Type;
   int      i;
     
   for (i=1; i<MaxOrders+1; i++)
   {
      if (i > 0 && i < 10) leadingZero = "0";
         else leadingZero = "";

      if (GlobalVariableGet(Symbol() + "_OrderOpen_" + leadingZero + i) == 1)
      {
         order_Ticket = GlobalVariableGet(Symbol() + "_OrderTicket_" + leadingZero + i);
         if (OrderSelect(order_Ticket, SELECT_BY_TICKET) == true)
            if (OrderCloseTime() > 0) break;
      }
   }

   // now that you have the ticket of the latest order closed you can do whatever you want here...
   // display some info about closed order

      Print(">> ", order_Type, " Order # ", order_Ticket, " is closed at ", TimeToStr(closeTime,TIME_MINUTES));
      Print(">> Margin Level = ", GetMarginLevel(), "%,  MM_MarginLevel = ", MM_MarginLevel, "%");
      comment1 = StringConcatenate("\n>>  ", order_Type, " Order # ", order_Ticket, " is closed at ",
      TimeToStr(closeTime,TIME_MINUTES), "\n>>  Profit = " + DoubleToStr(profit,2),
      " - Pips = " + DoubleToStr(pips,0));      
                  
      // make this order slot available for new orders
      GlobalVariableSet(Symbol() + "_OrderOpen_" + leadingZero + i, 0); // now no open order in this slot
      GlobalVariableSet(Symbol() + "_OrderTicket_" + leadingZero + i, -1); // order ticket for this slot is -1... no ticket...
   
      MoneyManagementCheck();
}
 

Last bit of EA code relating to Timer library...

void init()
{
   Timer_Init(); // this is a good place to do this...

// Create GlobalVariable Names here
// Lots of other init stuff

}

// if you don't do this your backtesting will be messed up (if you use GV's)
void deinit()
{      
   if (IsTesting())
   {
      // Delete all GlobalVariables and start fresh for next pass
      GlobalVariablesDeleteAll();
   }
}

// This is all of my start function
void start()
{                      
   if (validTimePeriod == false) return;
   if (validFilterPeriod == false) return;
   if (validTrailingStop == false) return;
        
   //---- Start Main program ----
   Main();   
   
   if (TrailingStop > 0) CheckTrailingStop(GetOpenOrderTickets, TrailingStop);               
           
   CheckWaiting();
   
   GetOpenTradeLots();   
      
   CheckIfOpenOrder_Closed();
}
 
vangrosh wrote >>

Last bit of EA code relating to Timer library...

thks!

Reason: