MQL Plus Enhanced Debugging Support – library MetaTrader 5

Features

Features three domains of functionality. – Debugging, Tracing and Performance measurments

– Simple to use “Print any variable” to expert-journal with one command.

– Track and debug “If”, “for”, and “while” statement evaluation.

– Trace calls to MQL-Built-In functions

– Measure runtime of operations and functions.

– Easy to use “out of the box” experience.

– Highly customizable, if required.

– Temporary or permanent  integration into your projects.

– License is given under GPLv2. Mainly this means, changes you make need to be brought back to the original author. Anything else is up to you. 

Current version is 5.12

– Most recent build can be found in Cloud Storage as part of the Project “MQLplus” (MQLplus Include Library Collection)

– Bugfixes from v 5.11 to v5.12

– Fixed Multidimension Array SUpport in MQLAPI-Tracer

– Fixed DBG_MSG_EVAL_* Macros calling a function twice, if given as parameter to the macros.

– Bugfixes from v 5.1 to v5.11 

– MQLAPI-Tracer: Fixed Enums throwing compile error on Alert/Comment/Print/printf

– Fixed Multidimensional Array Support (might need more fixing for MQLAPI-Tracer)

– Updated printf-output system for the library.

– MQL API tracer included for MQL5 pure code, currently does not support MQL standard library.

– Most macros were rewritten and are now faster.

– Error-Code tracing is supported directly by the MQL-API tracer.

– Minor compatibility issues were solved.

– Type resolving has been enhanced, especially enumeration-resolving for built-in enumerations is now working correctly again.

This library is MQL4 and MQL5 compatible.

Credits

I would like to give credits to major cotributions to help go through this extensive project. Without the contribution of continuously testing and validating the code functions as well as the usability aspects, the quality would not be as high as it is right now. 

Thank you @amrali

In the end, a library that contributes a set of tools for developing code should and must meet very high standards, so you as a user can rely on the output and feedback the library gives you. – I myself have found various bugs within mql5 and was able to track down these errors with the help of this library. Our focus was on quality as much as possible, but with a system this size, errors are eminent, and even after extensive testing, I am confident, there are still some errors in. 

If you would like to contribute to this work, it would be a pleasure to invite you in.

All addons are freely configurable. All code inclusions have been done in preprocessor macros.

All macros are optional, so the code below shows some of available macros. A full list and detailed description is included in the header file.

A comprehensive example can be found in LibDebug.mq5

Following find some examples. The section is split into three parts reflecting the main function domains of the library.


Simple and fast debugging example

Following examples show a temporary usage of the library in your code to quickly get consistent data about what is going on, while you are debugging.

Output produced by this example:


Example LibDebug_Simple.mq5:

#property version   "1.01"
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//  Additional options for LIB_DEBUG
//
//      LIB_DEBUG_AUTOENABLE
//
//          This flag will make the library switch between runtime and debugging mode, depending on
//          the compiler target. (Automatically also defines LIB_DEBUG)
//
//          Runtime mode:
//              Any program that is compiled in MetaEditor and then launched in Terminal, rather than
//              using "Start/resume debugging" buttons from MetaEditor. (CTRL-F5 or F5)
//
//          Debugging mode:
//              When compiling the program in MEtaEditor and launching by one of the buttons
//              "Start/resume debugging" (CTRL-F5 or F5)
//
//          Compiler-Target:
//              Depending on where a program has been launched, the target is defined either as
//                  "debugging" or "runtime" environment.
//
//
//      LIB_DEBUG_LOGFILE
//
//          Debugging library supports output logging to file. This feature can be enabled by defining
//          "LIB_DEBUG_LOGFILE" as shown below. (Automatically also defines LIB_DEBUG)
//              Logs will be written to Common/* directory.
//
//              This feature supports debug logging within optimizer runs of the program.
//
//
//      LIB_DEBUG_NO_JOURNAL_OUTPUT
//
//          Additionally the expert-journal output can be surpressed, separating the logs into two
//          targets. - This way logging debug output and logging terminal output is separated.
//
//          Define "LIB_DEBUG_NO_JOURNAL_OUTPUT" to disable expert-journal output from this library.
//          (Can only be used if LIB_DEBUG_LOGFILE is defined)
//
//



/////////////////////////////////////////
//
// Include the library
//

// Debugging domain
#define LIB_DEBUG
// Additional options
//#define LIB_DEBUG_AUTOENABLE
//#define LIB_DEBUG_LOGFILE
//#define LIB_DEBUG_NO_JOURNAL_OUTPUT
//#define LIB_DEBUG_MQLAPI_TRACE


// Tracing domain
//#define LIB_MQLAPI_TRACE
// Additional options
//#define LIB_MQLAPI_TRACE_PERFORMANCE
//#define LIB_MQLAPI_TRACE_SHOW_PARAMS
//#define LIB_MQLAPI_TRACE_SHOW_RESULT

// Performance domain
//#define LIB_PERF_PROFILING

// File inclusion
//#include "lib_debug_CustomConfig.mqh"
#include <MQLplus/lib_debug.mqh>





///////////////////////////////////////////////////////////////////////
//
// In OnInit you can see a simple usage of the debugging macros.
//
//  We will only use some stand-alone macros for quick debugging.
//
//  DBG_MSG()
//  DBG_MSG_VAR()
//  DBG_MSG_BITS()
//  DBG_MSG_ERRCODE()
//  DBG_MSG_EVAL()
//
//  Check example below for some variations.
//
//  These macros support all built-in datatypes (also in form of arrays).
//      (unsigned) char, short, int, long
//      bool, datetime, color, complex, matrix(f/c), vector(f/c),
//      float, double,
//      string, enum, struct, class and interface as well as pointers.
//
//
//  And MQL structures:
//      MqlDateTime, MqlRates, MqlTick, MqlParam, MqlBookInfo,
//      MqlTradeRequest, MqlTradeCheckResult, MqlTradeResult,
//      MqlTradeTransaction,
//      MqlCalendarCountry, MqlCalendarEvent, MqlCalendarValue,
//      DXVector, DXVertexLayout
//

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    //////////////////////////////////////////////////////////////
    //
    // Simple output macros
    //

    // Some initial debug message
    DBG_MSG("Example how to use lib_debug.mqh simple and quick debugging");

    // Output details about current program
    //  Available in debugging and runtime
    printf("%s", DBG_STR_EX45_FILEINFO);

    // Keep this message also in runtime builds
    DBG_MSG_PERSIST("Persistent message in debug and runtime (Runtime is without debug details)");



    //////////////////////////////////////////////////////////////
    //
    // All basic output macros available
    //


    //////////////////////////////////////////////////////////////
    //
    // Direct value printing
    //

    int test_var = 12345;

    DBG_MSG("Test A");
    DBG_MSG_PERSIST("Some message");
    DBG_MSG_VAR(PRICE_CLOSE);
    DBG_MSG_VAR(1);
    DBG_MSG_VAR(true);
    DBG_MSG_VAR(TimeCurrent());
    DBG_MSG_VAR(clrBlue);
    DBG_MSG_VAR("Print Me");
    DBG_MSG_VAR(test_var);
    DBG_MSG_VAR_IF(true, test_var);
    DBG_MSG_VAR(_LastError);
    DBG_MSG_ERRCODE(5004);
    DBG_MSG_BITS((uint)123);
    DBG_MSG_BITS(test_var);



    //////////////////////////////////////////////////////////////
    //
    // The flexibility of DBG_MSG_VAR
    //

    // Output primitive variables
    enum custom_enum
    {
        SOME_ENUM_VAL1,
        SOME_ENUM_VAL2
    };
    custom_enum         my_enum     = SOME_ENUM_VAL2;
    ENUM_APPLIED_PRICE  e_val       = PRICE_CLOSE;
    char                c_val       = 1;
    uchar               uc_val      = 2;
    short               s_val       = 3;
    int                 i_val       = 4;
    long                l_val       = 5;
    bool                b_val       = true;
    datetime            d_val       = TimeCurrent();
    float               flt_val     = (float)1.61;
    double              dbl_val     = 3.14;
    color               clr_val     = clrAqua;
    string              str_val     = "Some string";

    DBG_MSG_VAR(my_enum);
    DBG_MSG_VAR(e_val);
    DBG_MSG_VAR(c_val);
    DBG_MSG_VAR(uc_val);
    DBG_MSG_VAR(s_val);
    DBG_MSG_VAR(i_val);
    DBG_MSG_VAR(l_val);
    DBG_MSG_VAR(b_val);
    DBG_MSG_VAR(d_val);
    DBG_MSG_VAR(flt_val);
    DBG_MSG_VAR(dbl_val);
    DBG_MSG_VAR(clr_val);
    DBG_MSG_VAR(str_val);

    #ifdef __MQL5__
        complex             cmplx_val   = { 0.1, 0.2 };
        vector              v_val       = { 0, 1, 2, 3 };
        vectorf             vf_val      = { (float)0.1, (float)1.1, (float)2.1, (float)3.1 };
        matrix              m_val       (1, 2);
        matrixf             mf_val      (1.3, 2.3);
        complex  c1 = { DBL_MIN, 0.2 };
        complex  c2 = { 0.3, DBL_MAX };
        complex  c3 = { 0.5, 0.6 };
        complex  c4 = { 0.6, 0.7 };
        matrixc             mc_val      {{c1, c2}, {c3, c4}};
        vectorc             vc_val      = { c1, c2, c3, c4 };

        DBG_MSG_VAR(cmplx_val);
        DBG_MSG_VAR(m_val);
        DBG_MSG_VAR(mf_val);
        DBG_MSG_VAR(mc_val);
        DBG_MSG_VAR(v_val);
        DBG_MSG_VAR(vf_val);
        DBG_MSG_VAR(vc_val);

    #endif



    //////////////////////////////////////////////////////////////
    //
    // Print unknown objects, see function at the end.
    //

    // Example of printing objects
    class CObj
    {
        public:
        int test;

        CObj() :
            test(NULL)
        { };

        CObj(const CObj& p_in)
        { test = p_in.test; };
    } test_obj;

    DBG_MSG_VAR(test_obj);
    DBG_MSG_VAR(GetPointer(test_obj));



    //////////////////////////////////////////////////////////////
    //
    // Conditional execution of DBG_MSG() and DBG_MSG_VAR() macro
    //
    //  Use DBG_MSG_IF() as shown below.
    //  Use DBG_MSG_VAR_IF() as shown below.
    //
    // NOTE: The fist statement is a boolean evaluation.
    //       It is encapuslated properly and therefore can
    //       be anything that evaluates to "true" or "false".
    //       Same rules apply as for an "if()" statement.

    bool printme = true;
    DBG_MSG_IF(printme, "This will only show, if a condition is true!");
    DBG_MSG_VAR_IF(printme, TimeLocal());



    //////////////////////////////////////////////////////////////
    //
    // Print arrays
    //

    // Output arrays
    int i_arr[50];
    for(int cnt = NULL; (cnt < 50) && !_StopFlag; cnt++)
    { i_arr[cnt] = MathRand(); }
    DBG_MSG_VAR(i_arr);

    string str_val_arr[3];
    DBG_MSG_VAR(str_val_arr);

    #ifdef __MQL5__
        matrixc mc_val1 {{c1, c2}, {c3, c4}};
        matrixc mc_val2 {{c2, c1}, {c3, c4}};
        matrixc mc_val3 {{c1, c2}, {c4, c3}};
        matrixc mc_val_arr[3];
        mc_val_arr[0]       = mc_val1;
        mc_val_arr[1]       = mc_val2;
        mc_val_arr[2]       = mc_val3;
        DBG_MSG_VAR(mc_val_arr);

    #endif

    // Output two arrays side by side
    double d_arr[50];
    for(int cnt = NULL; (cnt < 50) && !_StopFlag; cnt++)
    { d_arr[cnt] = MathRand(); }
    DBG_MSG_LISTDUMP(i_arr, d_arr);



    /////////////////////////////////////
    //
    // Access an array
    //
    //  Check if it is accessible
    //
    // These macros will be replaced
    //  for runtime environment to their
    //  normal usage. - No overhead applied.
    //

    DBG_MSG_ARRAY_OUT_OF_RANGE(i_arr, 50);



    //////////////////////////////////////////////////////////////
    //
    // Print structures and arrays of structures
    //

    // Output MQL structures
    MqlDateTime             mql_dtm;
    MqlRates                mql_rates;
    MqlTick                 mql_tick;
    MqlParam                mql_param;
    MqlBookInfo             mql_book;
    DBG_MSG_VAR(mql_dtm);
    DBG_MSG_VAR(mql_rates);
    DBG_MSG_VAR(mql_tick);
    DBG_MSG_VAR(mql_param);
    DBG_MSG_VAR(mql_book);

    #ifdef __MQL5__
        MqlTradeRequest         mql_trade_request;
        MqlTradeCheckResult     mql_tradecheckresult;
        MqlTradeResult          mql_trade_result;
        MqlTradeTransaction     mql_transaction;
        MqlCalendarCountry      mql_cal_cntry;
        MqlCalendarEvent        mql_cal_event;
        MqlCalendarValue        mql_cal_value;
        DXVector                mql_dxvector;
        DXVertexLayout          mql_dxvertex;
        DBG_MSG_VAR(mql_trade_request);
        DBG_MSG_VAR(mql_tradecheckresult);
        DBG_MSG_VAR(mql_trade_result);
        DBG_MSG_VAR(mql_transaction);
        DBG_MSG_VAR(mql_cal_cntry);
        DBG_MSG_VAR(mql_cal_event);
        DBG_MSG_VAR(mql_cal_value);
        DBG_MSG_VAR(mql_dxvector);
        DBG_MSG_VAR(mql_dxvertex);

    #endif


    // See the difference in type printed
    DBG_MSG_VAR(EnumToString(mql_param.type));
    DBG_MSG_VAR(mql_param.type);
    DBG_MSG_VAR(mql_param.integer_value);
    DBG_MSG_VAR(mql_param.double_value);


    // Output arrays of structures
    MqlDateTime             arr_mql_dtm[3];
    MqlRates                arr_mql_rates[3];
    MqlTick                 arr_mql_tick[3];
    MqlParam                arr_mql_param[3];
    MqlBookInfo             arr_mql_book[3];
    DBG_MSG_VAR(arr_mql_dtm);
    DBG_MSG_VAR(arr_mql_rates);
    DBG_MSG_VAR(arr_mql_tick);
    DBG_MSG_VAR(arr_mql_param);
    DBG_MSG_VAR(arr_mql_book);

    #ifdef __MQL5__
        MqlTradeRequest         arr_mql_trade_request[3];
        MqlTradeCheckResult     arr_mql_tradecheckresult[3];
        MqlTradeResult          arr_mql_trade_result[3];
        MqlTradeTransaction     arr_mql_transaction[3];
        MqlCalendarCountry      arr_mql_cal_cntry[3];
        MqlCalendarEvent        arr_mql_cal_event[3];
        MqlCalendarValue        arr_mql_cal_value[3];
        DBG_MSG_VAR(arr_mql_trade_request);
        DBG_MSG_VAR(arr_mql_tradecheckresult);
        DBG_MSG_VAR(arr_mql_trade_result);
        DBG_MSG_VAR(arr_mql_transaction);
        DBG_MSG_VAR(arr_mql_cal_cntry);
        DBG_MSG_VAR(arr_mql_cal_event);
        DBG_MSG_VAR(arr_mql_cal_value);

    #endif



    //////////////////////////////////////////////////////////////
    //
    // Analyze condition statements
    //
    //  Any complexity is supported.
    //  Applicable to all condition evaluations.
    //      if(), while(), for(), () ? : ; ....
    //
    //  The last parameter will be always your condition,
    //  you would have in your original ()-statement.
    //
    //  All combinations are possible.
    //
    //  Here are the available macros:

    if( (DBG_MSG_EVAL(0.1 + 0.2 == 0.3))
     || (DBG_MSG_EVAL(0.7 - 0.4 == 0.3)) )
    { Print("true"); }
    else
    { Print("false"); }

    if(DBG_MSG_EVAL_IF(printme, 0.1 + 0.2 == 0.3))
    { Print("true"); }
    else
    { Print("false"); }

    if(DBG_MSG_EVAL_CMNT("Check equality", 0.1 + 0.2 == 0.3))
    { Print("true"); }
    else
    { Print("false"); }

    if(DBG_MSG_EVAL_IF_CMNT(printme, "Check equality", 0.1 + 0.2 == 0.3))
    { Print("true"); }
    else
    { Print("false"); }


    printme = false;
    if(DBG_MSG_EVAL_IF(printme, 0.1 + 0.2 == 0.3))
    { Print("true"); }
    else
    { Print("false"); }

    if(DBG_MSG_EVAL_IF_CMNT(printme, "Check equality", 0.1 + 0.2 == 0.3))
    { Print("true"); }
    else
    { Print("false"); }


    // Return
    return(INIT_SUCCEEDED);
}



//+------------------------------------------------------------------+
//| Expert OnTick function                                           |
//+------------------------------------------------------------------+
void OnTick()
{

    return;
}

Permanent integration example

Here you can see how a permanent integration would look like, this is useful for different cases. One scenario could be if you are debugging different asset types and run into issues, depending on symbol specifications. Another scenario would be when building a GUI and having performance or function integration issues. 

Again some output from the example code:


Example LibDebug_Integrated.mq5:

#property version   "1.01"
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//  Additional options for LIB_DEBUG
//
//      LIB_DEBUG_MQLAPI_TRACE
//
//          MQL API function call tracing support. This will log calls to MQL built-in functions.
//          "LIB_DEBUG_MQLAPI_TRACE" will enable this feature. You will get trace output, if you have
//          LIB_DEBUG built-in tracing for the particular function enabled. - See example below on how to
//          integrate tracing of a function.
//
//          Functions for which tracing is activated, see description below, will now
//          also log calls to MQL-API functions. The error code and return type will be logged.
//
//
//      !!! IMPORTANT NOTE !!!
//
//          MQL-API-Function tracing is incompatible with the standard library from MQL.
//          You will receive compiler errors, if activated and using standard-library.
//
//          If you wish to do so anyways, move forward to "Custom Integration" below
//          to learn how you could do it anyways.
//
//
//      LIMITATION when using LIB_DEBUG_MQLAPI_TRACE
//
//          If you are callling an MQL function on global scope, you must do so before
//          including lib_debug.mqh.
//
//          Example:
//
//              // Global MQL function calls must happen before "#include lib_debug"
//              const string files  = TerminalInfoString(TERMINAL_DATA_PATH) + "\MQL5\files\";
//              const uint ticks    = GetTickCount();
//
//              // Include lib_debug
//              #define LIB_MQLAPI_TRACE
//              #include <MQLpluslib_debug.mqh>
//
//          These global calls will not be tracked.
//



/////////////////////////////////////////
//
// Include the library
//

// Debugging domain
#define LIB_DEBUG
// Additional options
//#define LIB_DEBUG_AUTOENABLE
//#define LIB_DEBUG_LOGFILE
//#define LIB_DEBUG_NO_JOURNAL_OUTPUT
//#define LIB_DEBUG_MQLAPI_TRACE


// Tracing domain
//#define LIB_MQLAPI_TRACE
// Additional options
//#define LIB_MQLAPI_TRACE_PERFORMANCE
//#define LIB_MQLAPI_TRACE_SHOW_PARAMS
//#define LIB_MQLAPI_TRACE_SHOW_RESULT

// Performance domain
//#define LIB_PERF_PROFILING

// File inclusion
//#include "lib_debug_CustomConfig.mqh"
#include <MQLplus/lib_debug.mqh>





///////////////////////////////////////////////////////////////////////
//
// In OnInit you can see a simple usage of the debugging macros.
//

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    //////////////////////////////////////////////////////////////
    //
    // Simple output macros
    //

    // Some initial debug message
    DBG_MSG("Example how to use lib_debug.mqh as project integrated version.");

    // Output details about current program
    //  Available in debugging and runtime
    printf("%s", DBG_STR_EX45_FILEINFO);

    // Keep this message also in runtime builds
    DBG_MSG_PERSIST("Persistent message in debug and runtime (Runtime is without debug details)");

    // Return
    return(INIT_SUCCEEDED);
}



///////////////////////////////////////////////////////////////////////
//
// In OnDeinit we will use full tracing of the function call.
//
//  First we define some helpers for compile time code
//  inclusion selection. This will make it easy to enable/disable
//  function tracing as needed.
//
//  To do this, first a macro is defined which will be used as a
//  switch (defined = trace function) (not defined = disable tracing)
//
//  We can collect these "switches" at top of file for a better overview
//  Also we could wrap them into a condition, so that they will only be
//  defined when debugging is enabled.
//
//  Example:
//  #ifdef LIB_DEBUG
//      #define DBG_TRACE_EXPERT_MAIN_ONDEINIT
//
//  #endif

// En/Disable function tracing by commenting following macro:
#define DBG_TRACE_EXPERT_MAIN_ONDEINIT


// Now we define the helper macros which will be used inside
//  the functions body.

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
/////////////////////////////////////
// Function debug trace code
#ifdef DBG_TRACE_EXPERT_MAIN_ONDEINIT
    #undef DBG_TRACE_EXPERT_MAIN_ONDEINIT
    #define DBG_TRACE_EXPERT_MAIN_ONDEINIT(x) x
    #define DBG_TRACE_EXPERT_MAIN_ONDEINIT_RETURN       DBG_MSG_TRACE_RETURN

#else
    #define DBG_TRACE_EXPERT_MAIN_ONDEINIT(x)
    #define DBG_TRACE_EXPERT_MAIN_ONDEINIT_RETURN       DBG_MSG_NOTRACE_RETURN

#endif
/////////////////////////////////////
void OnDeinit(const int reason)
{
    /////////////////////////////////////
    //
    // TOP of function body
    //
    // Here the tracing code gets included, this is mandatory for tracing to work.
    // The switching macro can also be used to include additional
    // debugging code, if any is required, see example below.
    DBG_TRACE_EXPERT_MAIN_ONDEINIT(
        DBG_MSG_TRACE_BEGIN;

        // Additional details can be added here, as required.
        // For example to trace the inputs to this function, we can add
        DBG_MSG_VAR(reason);

        // Or whatever we would like to add.
        );

        // Additionally the macro "PERF_COUNTER_BEGIN" must be added.
        // It is mandatory to complete the "return" macro, irrespective of it
        // being used or not. (Any insertions will be removed for runtime builds,
        // if "LIB_PERF_PROFILING" is not defined)
        PERF_COUNTER_BEGIN;

    //
    // This concludes the functions head
    //  control code required for tracing.
    //  If tracing is disabled for this function,
    //  all additional code will be stripped at
    //  compile time.
    //
    //  Now follows the actual functions body:
    /////////////////////////////////////


    // Local init

        int     some_int_val    = 3;
        string  some_string     = "I dont know";

        /////////////////////////////////////
        //
        // As an example, here the local variables
        //  are printed, but only if tracing is enbaled.
        //  In contrast to the usage of macros in OnInit, where
        //  the macros were not wrapped in the
        //  Trace-Enable-Disable macro.
        //
        DBG_TRACE_EXPERT_MAIN_ONDEINIT(
            DBG_MSG_VAR(some_int_val);
            DBG_MSG_VAR(some_string);
            );


    // Destroy timer

        EventKillTimer();



    /////////////////////////////////////
    //
    // EXITING/RETURNING from the fucntions body.
    //
    // A return statement is mandatory, no matter the functions signature.
    // This will inform the debug library about the functions ending/exiting.
    //
    //  The special function "OnDeinit" will auto-resolve the deinit reason
    //  and show the state of the _StopFlag/IsStopped() variable.
    //  This is automatically inserted and needs no extra care taking.
    //
    DBG_TRACE_EXPERT_MAIN_ONDEINIT_RETURN;
}



///////////////////////////////////////////////////////////////////////
//
// In OnTick we will use full tracing of the function call as
//  shown with OnDeinit, and we will integrate global performance
//  counters as well. This will give output of the runtime used by
//  this function.
//
//  Since the tracing has already been exlained, here is a
//  stripped version, showing the raw requirements for a full trace.
//
//  Short outline.
//  - First define the enable/disable macros.
//  - Then include the head required inside the body.
//  - Use the return macro at all exit points of the function.
//
//  To enable tracing for this function, same procedure as
//  with OnDeinit applies.
//  Again, this could be collected at top of file for better oversight.

// Turn on tracing
#define DBG_TRACE_EXPERT_MAIN_ONTICK


//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
/////////////////////////////////////
// Function debug trace code
#ifdef DBG_TRACE_EXPERT_MAIN_ONTICK
    #undef DBG_TRACE_EXPERT_MAIN_ONTICK
    #define DBG_TRACE_EXPERT_MAIN_ONTICK(x) x
    #define DBG_TRACE_EXPERT_MAIN_ONTICK_RETURN         DBG_MSG_TRACE_RETURN

#else
    #define DBG_TRACE_EXPERT_MAIN_ONTICK(x)
    #define DBG_TRACE_EXPERT_MAIN_ONTICK_RETURN         DBG_MSG_NOTRACE_RETURN

#endif
/////////////////////////////////////
void OnTick()
{
DBG_TRACE_EXPERT_MAIN_ONTICK(
    DBG_MSG_TRACE_BEGIN;
    );
PERF_COUNTER_BEGIN;

    // Local init
    int some_var = MathRand();
    DBG_MSG_VAR(some_var);


    // Some random check operation
    if((some_var % 2) == 0)
    {
        call_sub_function();
        call_sub2_function();
        call_obj_function();
    }


    // Some eventual exit at annother point inside the functions body
    if((some_var % 2048) == 0)
    { DBG_TRACE_EXPERT_MAIN_ONTICK_RETURN; }


    // Return
    DBG_TRACE_EXPERT_MAIN_ONTICK_RETURN;
}



///////////////////////////////////////////////////////////////////////
//
// In call_sub_function we will use full tracing of the function call.
//
//  This example shows the difference for a function with
//  a return value.
//
//  Take notice of the used macros. - It is required to use a
//  different return macro for this type of function call.
//
//  Instead of using the macro "DBG_MSG_TRACE_RETURN" and "DBG_MSG_NOTRACE_RETURN"
//  here the macros "DBG_MSG_TRACE_RETURN_VAR(x)" and "DBG_MSG_NOTRACE_RETURN_VAR(x)"
//  are used.
//
//  IMPORTANT NOTICE:
//      Calling the macro with the parameter "NULL" requires!!! a cast operation.
//      (This functions return value is "int", "const int" or "int" doesnt make a difference)
//
//      Example:
//      DBG_MSG_TRACE_RETURN_VAR((int)NULL);
//
//      This cast operation can be included in the macro definition itself, like this:
//      #define DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION_RETURN(x) DBG_MSG_TRACE_RETURN_VAR((int)x)
//      #define DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION_RETURN(x) DBG_MSG_NOTRACE_RETURN_VAR(x)
//
//      or inside the function using the custom defined macro:
//      DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION_RETURN((int)NULL);
//
//      See second function example below.
//      Here the cast operation is inside the functions body.
//
//  NOTE on CAST-Operation:
//      The runtime macro does not require the cast operation.
//      You should include the cast-operation as shown in second example. If it is included
//      in the functions body, it will be also part of the runtime-code and not being stripped
//      from your code by the library.
//
// Again the enbale trace macro needs to be defined, so tracing
//  is enabled.

// Enable tracing of this function
#define DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION

//+------------------------------------------------------------------+
//| Example function with return value                               |
//+------------------------------------------------------------------+
/////////////////////////////////////
// Function debug trace code
#ifdef DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION
    #undef DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION
    #define DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION(x) x
    #define DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION_RETURN(x) DBG_MSG_TRACE_RETURN_VAR(x)

#else
    #define DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION(x)
    #define DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION_RETURN(x) DBG_MSG_NOTRACE_RETURN_VAR(x)

#endif
/////////////////////////////////////
const int call_sub_function()
{
DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION(
    DBG_MSG_TRACE_BEGIN;
    );
PERF_COUNTER_BEGIN;

    // Do some stuff
    if(MathRand() == 5)
    { DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION_RETURN(5); }

    // Return
    DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION_RETURN((int)NULL);
}



// Enable tracing of this function
#define DBG_TRACE_EXPERT_MAIN_CALL_SUB2_FUNCTION

//+------------------------------------------------------------------+
//| Example function with return value                               |
//+------------------------------------------------------------------+
/////////////////////////////////////
// Function debug trace code
#ifdef DBG_TRACE_EXPERT_MAIN_CALL_SUB2_FUNCTION
    #undef DBG_TRACE_EXPERT_MAIN_CALL_SUB2_FUNCTION
    #define DBG_TRACE_EXPERT_MAIN_CALL_SUB2_FUNCTION(x) x
    #define DBG_TRACE_EXPERT_MAIN_CALL_SUB2_FUNCTION_RETURN(x) DBG_MSG_TRACE_RETURN_VAR((int)x)

#else
    #define DBG_TRACE_EXPERT_MAIN_CALL_SUB2_FUNCTION(x)
    #define DBG_TRACE_EXPERT_MAIN_CALL_SUB2_FUNCTION_RETURN(x) DBG_MSG_NOTRACE_RETURN_VAR(x)

#endif
/////////////////////////////////////
const int call_sub2_function()
{
DBG_TRACE_EXPERT_MAIN_CALL_SUB2_FUNCTION(
    DBG_MSG_TRACE_BEGIN;
    );
PERF_COUNTER_BEGIN;

    // Do some stuff
    if(MathRand() == 12)
    { DBG_TRACE_EXPERT_MAIN_CALL_SUB2_FUNCTION_RETURN(12); }

    // Return
    DBG_TRACE_EXPERT_MAIN_CALL_SUB2_FUNCTION_RETURN(NULL);
}







// A local class
class CObj
{
    public:
    int test;

    CObj() :
        test(NULL)
    { };

    CObj(const CObj& p_in)
    {
        test = p_in.test;
        printf("%s", "COPY");
    };


};

// Enable tracing of this function
#define DBG_TRACE_EXPERT_MAIN_CALL_OBJ_FUNCTION


//+------------------------------------------------------------------+
//| Example function with return object                              |
//+------------------------------------------------------------------+
/////////////////////////////////////
// Function debug trace code
#ifdef DBG_TRACE_EXPERT_MAIN_CALL_OBJ_FUNCTION
    #undef DBG_TRACE_EXPERT_MAIN_CALL_OBJ_FUNCTION
    #define DBG_TRACE_EXPERT_MAIN_CALL_OBJ_FUNCTION(x) x
    #define DBG_TRACE_EXPERT_MAIN_CALL_OBJ_FUNCTION_RETURN(x) DBG_MSG_TRACE_RETURN_VAR(x)

#else
    #define DBG_TRACE_EXPERT_MAIN_CALL_OBJ_FUNCTION(x)
    #define DBG_TRACE_EXPERT_MAIN_CALL_OBJ_FUNCTION_RETURN(x) DBG_MSG_NOTRACE_RETURN_VAR(x)

#endif
/////////////////////////////////////
CObj call_obj_function()
{
DBG_TRACE_EXPERT_MAIN_CALL_OBJ_FUNCTION(
    DBG_MSG_TRACE_BEGIN;
    );
PERF_COUNTER_BEGIN;

    // Local init
    CObj    test_obj;
    CObj*   test_ptr = new CObj();
    Print("1");

    DBG_TRACE_EXPERT_MAIN_CALL_OBJ_FUNCTION(
        DBG_MSG_VAR(1);
        DBG_MSG_VAR(test_obj);
        DBG_MSG_VAR(test_ptr);
        );

    // Do some stuff
    if(MathRand() == 12)
    { DBG_TRACE_EXPERT_MAIN_CALL_OBJ_FUNCTION_RETURN(test_obj); }

    // Return
    DBG_TRACE_EXPERT_MAIN_CALL_OBJ_FUNCTION_RETURN(test_obj);
}




Advanced debugging

Some additional advanced debugging macros to support more complex situations.

Alternative:   NRatio - indicator MetaTrader 5

Output of the example code:


Example LibDebug_Advanced.mq5:

#property version   "1.01"
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//  Advanced debugging
//
//  Example of more complex debugging situations.
//






/////////////////////////////////////////
//
// Include the library
//

// Debugging domain
#define LIB_DEBUG
// Additional options
//#define LIB_DEBUG_AUTOENABLE
//#define LIB_DEBUG_LOGFILE
//#define LIB_DEBUG_NO_JOURNAL_OUTPUT
//#define LIB_DEBUG_MQLAPI_TRACE


// Tracing domain
//#define LIB_MQLAPI_TRACE
// Additional options
//#define LIB_MQLAPI_TRACE_PERFORMANCE
//#define LIB_MQLAPI_TRACE_SHOW_PARAMS
//#define LIB_MQLAPI_TRACE_SHOW_RESULT

// Performance domain
//#define LIB_PERF_PROFILING

// File inclusion
//#include "lib_debug_CustomConfig.mqh"
#include <MQLplus/lib_debug.mqh>





///////////////////////////////////////////////////////////////////////
//
// In OnInit you can see a simple usage of the debugging macros.
//

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    //////////////////////////////////////////////////////////////
    //
    // Simple output macros
    //

    // Some initial debug message
    DBG_MSG("Example how to use lib_debug.mqh");

    // Output details about current program
    //  Available in debugging and runtime
    printf("%s", DBG_STR_EX45_FILEINFO);

    // Keep this message also in runtime builds
    DBG_MSG_PERSIST("Persistent message in debug and runtime (Runtime is without debug details)");

    // Activate chart events
    ChartSetInteger(ChartID(), CHART_EVENT_MOUSE_MOVE, true);

    // Return
    return(INIT_SUCCEEDED);
}



//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
/////////////////////////////////////
// Function debug trace code
#ifdef DBG_TRACE_EXPERT_MAIN_ONDEINIT
    #undef DBG_TRACE_EXPERT_MAIN_ONDEINIT
    #define DBG_TRACE_EXPERT_MAIN_ONDEINIT(x) x
    #define DBG_TRACE_EXPERT_MAIN_ONDEINIT_RETURN       DBG_MSG_TRACE_RETURN

#else
    #define DBG_TRACE_EXPERT_MAIN_ONDEINIT(x)
    #define DBG_TRACE_EXPERT_MAIN_ONDEINIT_RETURN       DBG_MSG_NOTRACE_RETURN

#endif
/////////////////////////////////////
void OnDeinit(const int reason)
{
DBG_TRACE_EXPERT_MAIN_ONDEINIT(
    DBG_MSG_TRACE_BEGIN;
    DBG_MSG_VAR(reason);
    );
    PERF_COUNTER_BEGIN;

    // Local init

        int     some_int_val    = 3;
        string  some_string     = "I dont know";

        DBG_TRACE_EXPERT_MAIN_ONDEINIT(
            DBG_MSG_VAR(some_int_val);
            DBG_MSG_VAR(some_string);
            );


    // Destroy timer

        EventKillTimer();

    // Return
    DBG_TRACE_EXPERT_MAIN_ONDEINIT_RETURN;
}




///////////////////////////////////////////////////////////////////////
//
// In OnTick we will use additional debug-break and
//  loop tracing macros as wel las some DBG_MSG_EVAL_* statements
//

// Turn on tracing
#define DBG_TRACE_EXPERT_MAIN_ONTICK


//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
/////////////////////////////////////
// Function debug trace code
#ifdef DBG_TRACE_EXPERT_MAIN_ONTICK
    #undef DBG_TRACE_EXPERT_MAIN_ONTICK
    #define DBG_TRACE_EXPERT_MAIN_ONTICK(x) x
    #define DBG_TRACE_EXPERT_MAIN_ONTICK_RETURN         DBG_MSG_TRACE_RETURN

#else
    #define DBG_TRACE_EXPERT_MAIN_ONTICK(x)
    #define DBG_TRACE_EXPERT_MAIN_ONTICK_RETURN         DBG_MSG_NOTRACE_RETURN

#endif
/////////////////////////////////////
void OnTick()
{
DBG_TRACE_EXPERT_MAIN_ONTICK(
    DBG_MSG_TRACE_BEGIN;

    // DebugBreak by call-counter condition
    //  This macro will DebugBreak if the call-counter
    //  of this function has reached a given value.
    //
    // This macro is only available, if tracing
    //  for this function is enabled.
    DBG_BREAK_CALL_COUNTER(25);

    );
PERF_COUNTER_BEGIN;

    // Local init
    MathSrand(GetTickCount());
    int some_var = MathRand();


    //////////////////////////////////////////////////////////
    //
    // Analyze the run of a simple loop
    //
    //  These macros can only be used once per
    //  function body or scope { }
    //
    DBG_TRACE_LOOP_BEGIN;
    for(int cnt = NULL; (cnt < 500) && !_StopFlag; cnt++)
    {
        DBG_TRACE_LOOP_START;

            /* Have your code here */

            some_var = MathRand();

        DBG_TRACE_LOOP_FINISH;
    }
    DBG_TRACE_LOOP_END;



    //////////////////////////////////////////////////////////
    //
    // Analyze the run of a nested loop
    //
    //  Here we will use explizit IDs to identify
    //  each loop.
    //
    DBG_TRACE_LOOP_BEGIN_ID(outer_loop);
    for(int cnt = NULL; (cnt < 25) && !_StopFlag; cnt++)
    {
        DBG_TRACE_LOOP_START_ID(outer_loop);

            /* Have your code here */
            some_var = MathRand();

            DBG_TRACE_LOOP_BEGIN_ID(inner_loop);
            while(DBG_MSG_EVAL_IF_CMNT((some_var % 1024) == 0, StringFormat("Outer loop counter: %i", cnt), (some_var % 1024) != 0))
            {
                DBG_TRACE_LOOP_START_ID(inner_loop);

                /* Have your code here */
                some_var = MathRand();

                DBG_TRACE_LOOP_FINISH_ID(inner_loop);
            }
            DBG_TRACE_LOOP_END_ID(inner_loop);


        DBG_TRACE_LOOP_FINISH_ID(outer_loop);
    }
    DBG_TRACE_LOOP_END_ID(outer_loop);




    // Some eventual exit at annother point inside the functions body
    some_var = MathRand();

    if(DBG_MSG_EVAL_CMNT("Going to alternate exit??", (some_var % 2048) == 0))
    { DBG_TRACE_EXPERT_MAIN_ONTICK_RETURN; }


    // Here is a simple debug break on condition
    DBG_BREAK_IF(some_var == 256);


    // Return
    DBG_TRACE_EXPERT_MAIN_ONTICK_RETURN;
}



///////////////////////////////////////////////////////////////////////
//
// In OnChartEvent we will use some advanced break-point conditions
//
//  Following macros will be used
//
//      DBG_BREAK_CONDITION_CREATE(x, y)
//      DBG_BREAK_CONDITION_ACTIVATE(x, y)
//      DBG_BREAK_CONDITION_DEACTIVATE(x, y)
//      DBG_BREAK_CONDITION_ON_ID(x)
//      DBG_BREAK_CONDITION_ON_ID_RESET(x)
//
// With these macros we can create complex break conditions
//  across the whole program, tese are not bound to the local function.
//  It is possible to have some part of the code create a break-condition,
//  but do the actual break at some totally unrelated point in the code.
//
//  Of course, the breakpoint will only work if it is after the
//  condition has been met. Or, with the next, repetitive call of the
//  function.
//
//  Following example shal demonstratte the usage
//


// Create a break condition.
//
//  For global governance of breakpoints,
//  the second parameter is available. If this is set to false,
//  this breakcondition is globally deactivated.
//
DBG_BREAK_CONDITION_CREATE(mouse_event, true);


//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
{
    // We want to debug break on mouse event click.
    //  These macros can be spread over the code, no metter the
    //  actual location. Complex dependencies can be easily debugged
    //  this way. You can reference any group as often as you like.
    //
    //  Also multiple references to the same group will work.
    //  If any of them resolves to true, the breakpoint is activated.
    //  It will stay active until it is being disabled.
    //
    DBG_BREAK_CONDITION_ACTIVATE(mouse_event, id == CHARTEVENT_CLICK);


    // Somewhere else in the code, we would now like to DebugBreak to this condition.
    //  Maybe at the point where we need the break, we dont even have the variable
    //  available to know if the current event is a "CHARTEVENT_OBJECT_CLICK".
    //  So we set the breakpoint like this. - Remember, this can be anywhere in the code.
    //  As soon as the conditon has been met, any mentioning of this macro will
    //  halt execution until it is cleard by "DBG_BREAK_CONDITION_DEACTIVATE"
    //
    DBG_BREAK_CONDITION_ON_ID(mouse_event);


    // Alternatively, you can use the break-reset macro, to clear the break conditon
    //
    DBG_BREAK_CONDITION_ON_ID_RESET(mouse_event);


    // Lets say, we would only want to break, if no tick has arrived since
    //  this event was activated. For this we could now place following code in
    //  OnTick(), maybe directly at the top of the function.
    //
    DBG_BREAK_CONDITION_DEACTIVATE(mouse_event, true);


    // Return
    return;
}



API Tracer

This module will track calls to functions to the MQL-API, like SymbolInfoInteger, CopyRates and so on. – This module will journal all calls to these functions, and in case an error occurs, the library will print out the input parameters and the results from that function. – Also this module will inform you in case you have missed any _LastError, GetLastError().

There are two currently known limitations to this module:

– It is not compatible with standard-library. 

– MQL-API-Functions called to assign a value to a global variable must be called before including “lib_debug.mqh”

Here the output form the example code:


Example LibDebug_APITracer.mq5:

#property version   "5.03"
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//  Difference between LIB_DEBUG_MQLAPI_TRACE and LIB_MQLAPI_TRACE
//
//      LIB_MQLAPI_TRACE
//
//          This will activate api tracing independantly of any debugging configuration.
//          For general tracing, define this macro, as the following is considered an
//          option-feature for debugging integration.
//
//
//      LIB_DEBUG_MQLAPI_TRACE
//
//          Using this macro to activate the api tracer will only include tracing
//          within lib_debug-enabled function tracing.
//
//          See example above, but you need to have your function
//          be set up with these macros:
//
//              DBG_MSG_TRACE_BEGIN and DBG_MSG_TRACE_RETURN, DBG_MSG_NOTRACE_RETURN
//
//          respectively. outside of this block, tracing will be disabled.
//
//
//
//  Additional options for LIB_DEBUG_MQLAPI_TRACE and LIB_MQLAPI_TRACE
//
//      To always print input parameters to a function, define
//
//      LIB_MQLAPI_TRACE_SHOW_PARAMS
//
//          This will force printing of all input parameters
//          which are not output parameters only.
//
//
//      LIB_MQLAPI_TRACE_SHOW_RESULT
//
//          This will force printing of results from the function call, including
//          current error state
//
//
//      LIB_MQLAPI_TRACE_PERFORMANCE
//
//          This option allows you to measure the time an API call takes in microseconds.
//
//
//  !!! IMPORTANT NOTE !!!
//
//      LIB_MQLAPI_TRACE and LIB_DEBUG_MQLAPI_TRACE is incompatible with the standard library from MQL.
//      Work to resolve this issue is underway, but might take quite a while.
//
//      Until then, if you are using any includes from standard library, you cannot activate
//      the MQLAPI tracer module. - You will get compiler errors.
//
//      Same goes for any function you created on your own, that shares a name with any of
//      the functions provided by MQL-API.
//
//      The tracer uses macro substitution to replace and intercept the calls to the API.
//      Therefore any function with the same name as listed in the file
//
//          "MQLplus/lib_debug/lib_debug_mqlapi_tracer_overwrite_macros.mqh"
//
//      will confuse the compiler and library. You will not be able to compile your code.
//
//      If it is mandatory for you to do so anyways, you need to setup a custom config
//      for your project. - See below.
//
//  !!! END !!!
//


/////////////////////////////////////////
//
// Include the library
//

// Debugging domain
//#define LIB_DEBUG
// Additional options
//#define LIB_DEBUG_AUTOENABLE
//#define LIB_DEBUG_LOGFILE
//#define LIB_DEBUG_NO_JOURNAL_OUTPUT
//#define LIB_DEBUG_MQLAPI_TRACE


// Tracing domain
#define LIB_MQLAPI_TRACE
// Additional options
//#define LIB_MQLAPI_TRACE_PERFORMANCE
//#define LIB_MQLAPI_TRACE_SHOW_PARAMS
//#define LIB_MQLAPI_TRACE_SHOW_RESULT

// Performance domain
//#define LIB_PERF_PROFILING

// File inclusion
//#include "lib_debug_CustomConfig.mqh"
#include <MQLplus/lib_debug.mqh>





//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    //////////////////////////////////////////////////////////////
    //
    // Simple output macros
    //

    // Some initial debug message
    DBG_MSG("Example how to use lib_debug.mqh MQL-API-Tracer");

    // Output details about current program
    //  Available in debugging and runtime
    printf("%s", DBG_STR_EX45_FILEINFO);

    // Keep this message also in runtime builds
    DBG_MSG_PERSIST("Persistent message in debug and runtime (Runtime is without debug details)");

    // Set timer
    EventSetTimer(5);

    // Return
    return(INIT_SUCCEEDED);
}



//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    // Destroy timer
    EventKillTimer();

    // Return
    return;
}



//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
    // Local init
    int some_var = MathRand();


    // Return
    return;
}



//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
{
    // CLear the timer for demo purposes
    EventKillTimer();

    // test unbraced if statements
    if(!MathIsValidNumber(1.23))
        Print("number is non valid");
    else
        Print("valid");

    // test nested api function calls
    if(MathIsValidNumber(MathRand()))
        Print("number is valid");

    // test unary operators
    if(!MathIsValidNumber(MathRand()))
        Print("number is non valid");

    // test void api function
    srand(GetTickCount());

    // test complex expressions + operator precedence rules
    Print(MathRand() + MathRand() * GetTickCount());

    // test unbraced for statements + explicit typecast
    for(int i = 0; i < 2; i++)
        (int) floor(log10(rand()));

    // test accuracy of printing doubles
    Print(MathAbs(0.3));
    Print(MathAbs(0.1 + 0.2));

    // test accuracy of printing floats
    Print(MathAbs(0.7f));
    Print(MathAbs(0.1f + 0.6f));

    // test typename bug for namespace identifiers.
    Print(typename((GetTickCount())));
    Print(sizeof(GetTickCount()));

    // test error codes from unsuccessful api function calls
    FileOpen("file.xyz", FILE_READ);

    int hdbase = DatabaseOpen("db.sqlite", DATABASE_OPEN_READONLY);
    DatabaseClose(hdbase);

    // test correct type promotion of ternary operator.
    #define tern_expr  (true ? MathMax(101, 201) : MathMax("a", "b"))
    Print(tern_expr);                   // compiler warning: implicit conversion from 'number' to 'string' (This is correct behaviour!)
    Print(typename(tern_expr));

    // test api function that returns an enumerated value
    int period = Period();

    // test overloads of one api function
    double ask = SymbolInfoDouble(NULL, SYMBOL_ASK);
    SymbolInfoDouble(NULL, SYMBOL_ASK, ask);

    // Return
    return;
}


Performance Counters

This module features measurement extensions to clearly get runtimes from your code and functions.

This module is available only in “runtime-mode” of a program. Meaning, after compilation, you must start your program inside the Terminal, as this is considered the “runtime-environment”

Output from the example code:


Example LibDebug_Performance.mq5:

#property version   "1.01"
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//  Performance profiling
//
//      This feature is only active in runtime builds of your program, and if you activate the flag
//      as shown below.
//
//      Compile your project and load it onto a chart in the Terminal.
//



/////////////////////////////////////////
//
// Include the library
//

// Debugging domain
#define LIB_DEBUG
// Additional options
//#define LIB_DEBUG_AUTOENABLE
//#define LIB_DEBUG_LOGFILE
//#define LIB_DEBUG_NO_JOURNAL_OUTPUT
//#define LIB_DEBUG_MQLAPI_TRACE


// Tracing domain
//#define LIB_MQLAPI_TRACE
// Additional options
//#define LIB_MQLAPI_TRACE_PERFORMANCE
//#define LIB_MQLAPI_TRACE_SHOW_PARAMS
//#define LIB_MQLAPI_TRACE_SHOW_RESULT

// Performance domain
#define LIB_PERF_PROFILING

// File inclusion
//#include "lib_debug_CustomConfig.mqh"
#include <MQLplus/lib_debug.mqh>





//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    // Some initial debug message
    DBG_MSG("Example how to use lib_debug.mqh for performance metrics");

    // Output details about current program
    //  Available in debugging and runtime
    printf("%s", DBG_STR_EX45_FILEINFO);


    /////////////////////////////////////////////////////////////////////////////
    //
    // Standalone performance macros example
    //
    //  To measure certain parts of code in a standalone environment,
    //  following macros are implemented to do this.
    //
    //  There are three variations of this macro available. Depending on
    //  the return value of the function to be measured, either having
    //  a return value, a return object or not having a return value, you
    //  need to use the appropiate macro.
    //
    //      PERF_COUNTER_TIMEIT_V   (V = void)
    //          This macro is used to measure functions or function sequences
    //          that do not have a return value.
    //
    //      PERF_COUNTER_TIMEIT_R   (R = return)
    //          This macro will return the resulting value (return value)
    //          of the function being called, or their combination of such.
    //          For a better understanding, see the examples below.
    //
    //      PERF_COUNTER_TIMEIT_O   (O = Object)
    //          This macro will return the object (returned by the function)
    //          of the function being called.
    //
    //  NOTE:
    //
    //      PERF_COUNTER_TIMEIT_V may not be mentioned more than once per line of code.
    //      Internally the macro uses the __LINE__ macro for identifying the call,
    //      therefore it is mandatory to have only one call per code line.
    //      Also be aware, if including this macro inside of another macro, all
    //      statements will be on one line of code.
    //
    //      PERF_COUNTER_TIMEIT_R uses the complier macro __COUNTER__ to identify
    //      unique its callings. Be aware, in case you rely on the sequence of
    //      counting provided by __COUNTER__ within your code. Each mentioning of
    //      the macro "PERF_COUNTER_TIMEIT_R" will increase the __COUNTER__ by one.
    //
    //      PERF_COUNTER_TIMEIT_O notes are same as for PERF_COUNTER_TIMEIT_R, with
    //      one addition: The Copy-Constructor of the returned object will be called
    //      twice. The first call contributes to the measurement results, the second
    //      consequently does not. In order to encapusalte the function calls, an
    //      additional copy of the object is required within the process of returning
    //      values from the function.
    //
    //      For pointers as return value, either macro "PERF_COUNTER_TIMEIT_R" or
    //      "PERF_COUNTER_TIMEIT_O" can be used. - Both work the same way in this case.
    //

    // A single function call
    PERF_COUNTER_TIMEIT_V(
        printf("Some text A");
        );

    // A block sequence of function calls
    PERF_COUNTER_TIMEIT_V(
        printf("Some text B");
        printf("Some text C");
        );

    // Activate timer
    EventSetTimer(10);

    // Example of complex measurements
    b_start();
    b_start();
    b_start();

    // Return
    return(INIT_SUCCEEDED);
}


// Some simple functions to be measured in their runtime
int f1() { Sleep(100); return(1); }
int f2() { Sleep(200); return(2); }
int f3() { Sleep(300); return(3); }

// A collection of function calls
int g()
{
  return(PERF_COUNTER_TIMEIT_R(f1() + f2() + f3() == 6) ?
                       PERF_COUNTER_TIMEIT_R(f1() * f2()) : 0);
}

//+------------------------------------------------------------------+
//| Service program start function                                   |
//+------------------------------------------------------------------+
void b_start()
{
    if (PERF_COUNTER_TIMEIT_R(g()) == 2)
    {
        PERF_COUNTER_TIMEIT_V(
            Print(f3() == 3);
            );
    }

    PERF_COUNTER_TIMEIT_V(
        Print(f1());
        );

    PERF_COUNTER_TIMEIT_V(
        Print(f2());
        );

    return;
};



//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    // Destroy timer
    EventKillTimer();

    // Return
    return;
}



///////////////////////////////////////////////////////////////////////
//
// In OnTick we will make use of the full trace integration macros.
//
//  Here the macros will be reduced to only perform runtime
//  performance data gathering and printing to expert journal.
//

// Turn on tracing
#define DBG_TRACE_EXPERT_MAIN_ONTICK

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
/////////////////////////////////////
// Function debug trace code
#ifdef DBG_TRACE_EXPERT_MAIN_ONTICK
    #undef DBG_TRACE_EXPERT_MAIN_ONTICK
    #define DBG_TRACE_EXPERT_MAIN_ONTICK(x) x
    #define DBG_TRACE_EXPERT_MAIN_ONTICK_RETURN         DBG_MSG_TRACE_RETURN

#else
    #define DBG_TRACE_EXPERT_MAIN_ONTICK(x)
    #define DBG_TRACE_EXPERT_MAIN_ONTICK_RETURN         DBG_MSG_NOTRACE_RETURN

#endif
/////////////////////////////////////
void OnTick()
{
DBG_TRACE_EXPERT_MAIN_ONTICK(
    DBG_MSG_TRACE_BEGIN;
    );
PERF_COUNTER_BEGIN;

    // Local init
    int some_var = MathRand();


    // Some eventual exit at annother point inside the functions body
    if((some_var % 2048) == 0)
    { DBG_TRACE_EXPERT_MAIN_ONTICK_RETURN; }


    // Return
    DBG_TRACE_EXPERT_MAIN_ONTICK_RETURN;
}



///////////////////////////////////////////////////////////////////////
//
// In OnTimer we will use only the performance metrics macros.
//
//  For ease of use, a return macro will be defined. This is the
//  only requirement for performance metrics to be displayed in the
//  experts journal.
//
//  Performance macros are enabled only in release versions
//  of the code, they are disabled inside of debugging environments.
//
//  Debugging environment is defined by the flag LIB_DEBUG.
//  To enable performance macros, define LIB_PERF_PROFILING.
//
//  NOTE:
//      The macro "DBG_MSG_TRACE_RETURN" or "DBG_MSG_NOTRACE_RETURN"
//      must be used, so the performance control block will be properly closed.
//      Therefore we define relevant macros again, as shown in other examples.
//
//  Enable/disable tracing does not influence the perfomrance macros, as tracing
//  is only enabled when "LIB_DEBUG" is defined and "PERF_*"-macros are disabled.
//  You will get performance measurements when the global macro "LIB_PERF_PROFILING"
//  is defined and the global macro "LIB_DEBUG" is not defined.
//
//
// We can now define additional, custom performance blocks on a global level.
//  These work accross functions and can be used to trace certain parts of the
//  code. - Each block has its own call counter, therefore it is not advised
//  to reuse the blocks for different code sections, except of course thats
//  exactly what you want to measure.
//
// These blocks may overlap, if required. - It is necessary t ounderstand,
//  when doing so, you will also measure the times each macro takes. The execution
//  of these macros would be part of your measurement.
//
//  See example below.
//

// Create additional performance counters
//  IDs can be specified freely
    PERF_COUNTER_DEFINE_ID(on_timer_for_loop);

    // Additional example (these are not used)
    PERF_COUNTER_DEFINE_ID(if_g_func);
    PERF_COUNTER_DEFINE_ID(call_f3_func);
    PERF_COUNTER_DEFINE_ID(printf_f1);
    PERF_COUNTER_DEFINE_ID(printf_f2);

// Performance block IDs can be created on a global scope as well
//  as on function local scope and even in code blocks inside of
//  functions scope.


//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
/////////////////////////////////////
// Function performance trace code
#define DBG_TRACE_EXPERT_MAIN_ONTIMER_RETURN        DBG_MSG_NOTRACE_RETURN

/////////////////////////////////////
void OnTimer()
{
PERF_COUNTER_BEGIN;

    // Disable timer for demo purpose
    EventKillTimer();

    // Measuring a loop
    double sum = NULL;

    PERF_COUNTER_BEGIN_ID(on_timer_for_loop);
    for(int cnt = NULL; (cnt < 1000000) && !_StopFlag; cnt++)
    {
        sum += 1.0;
    }
    PERF_COUNTER_END_ID(on_timer_for_loop);



    /////////////////////////////////////////////////////////////////////////////
    //
    // Here an example of possible nested performance blocks.
    // This example is very theoretical, but shows how blocks can be applied.
    //

    // Local scope
    {
        // These definitions can be on any scope in
        //  the hirarchy above this scope of the function all
        //  the way up to the global scope.
        PERF_COUNTER_DEFINE_ID(a_local_scope);
        PERF_COUNTER_DEFINE_ID(nested_level_1);
        PERF_COUNTER_DEFINE_ID(overlapping_block_a);
        PERF_COUNTER_DEFINE_ID(overlapping_block_b);


        // Begin measurement of most outer block
        PERF_COUNTER_BEGIN_ID(a_local_scope);

            // Have some code here (not mandatory)


            // Nested performance block 1
            PERF_COUNTER_BEGIN_ID(nested_level_1);

                // Have more code here


                // Overlapping execution block
                PERF_COUNTER_BEGIN_ID(overlapping_block_a);

                    // test unbraced if statements
                    if(!MathIsValidNumber(1.23))
                        Print("number is non valid");
                    else
                        Print("valid");

                    // test nested api function calls
                    if(MathIsValidNumber(MathRand()))
                        Print("number is valid");

                    // test unary operators
                    if(!MathIsValidNumber(MathRand()))
                        Print("number is non valid");

                    // test void api function
                    srand(GetTickCount());

                    // test complex expressions + operator precedence rules
                    Print(MathRand() + MathRand() * GetTickCount());

                    // test unbraced for statements + explicit typecast
                    for(int i = 0; i < 2; i++)
                        (int) floor(log10(rand()));

                    // test accuracy of printing doubles
                    Print(MathAbs(0.3));
                    Print(MathAbs(0.1 + 0.2));


                // Open next block
                PERF_COUNTER_BEGIN_ID(overlapping_block_b);

                    // test accuracy of printing floats
                    Print(MathAbs(0.7f));
                    Print(MathAbs(0.1f + 0.6f));

                    // test typename bug for namespace identifiers.
                    Print(typename((GetTickCount())));
                    Print(sizeof(GetTickCount()));

                    // test error codes from unsuccessful api function calls
                    FileOpen("file.xyz", FILE_READ);

                    #ifdef __MQL5__
                        int hdbase = DatabaseOpen("db.sqlite", DATABASE_OPEN_READONLY);
                        DatabaseClose(hdbase);

                    #endif

                    // test correct type promotion of ternary operator.
                    #define tern_expr  (true ? MathMax(101, 201) : MathMax("a", "b"))
                    Print(tern_expr);                   // compiler warning: implicit conversion from 'number' to 'string' (This is correct behaviour!)
                    Print(typename(tern_expr));

                    // test api function that returns an enumerated value
                    int period = Period();

                    // test overloads of one api function
                    double ask = SymbolInfoDouble(NULL, SYMBOL_ASK);
                    SymbolInfoDouble(NULL, SYMBOL_ASK, ask);


                // Now closing block A
                PERF_COUNTER_END_ID(overlapping_block_a);

                    // Some code for block B


                // Now closing block B
                PERF_COUNTER_END_ID(overlapping_block_b);

                // Maybe more code here for nested level 1


            // Close nested block 1
            PERF_COUNTER_END_ID(nested_level_1);

            // Optionally have code here as well....


        // Close most outer block
        PERF_COUNTER_END_ID(a_local_scope);
    }


    // The above example will also measure the performance counters itself.
    //  Although they are held as short as possible, code-wise, it will
    //  have some influence on the pure code thats being measured.
    //
    //  The impact can vary, depending on the memory-paging that happens
    //  by accessing different areas of memory. - The variations will be
    //  within the margins of error and therefore can (mostly) be ignored.
    //
    /////////////////////////////////////////////////////////////////////////////


    // Return
    DBG_TRACE_EXPERT_MAIN_ONTIMER_RETURN;
}

Custom Configuration / Customization

You can customize this library to your personal and project specific needs. Especially if you integrate the library permanently, this might be of interest to you.

Alternative:   Historical Volatility Bands - High/Low - indicator MetaTrader 5

But even if not, you can make changes to the library and add additional code to the API-Tracer module, if required.

To do so, go to MQLplus/Lib_debug and find the file lib_debug_CustomConfig.mqh, copy the file and make appropiate changes to it, as required.

Here is the content of this file. – Everything should be documented for you to make che required additions and changes.

#ifndef LIB_DBG_DEBUG_MQLAPI_CUSTOMCONFIG_MQH_INCLUDED
#define LIB_DBG_DEBUG_MQLAPI_CUSTOMCONFIG_MQH_INCLUDED
#property version   "5.10"
/**********************************************************************************
 * No Copyright (C) applied.
 * No Lisence applied.
 *
 * This file is the lib_debug custom config include file.
 *
 **********************************************************************************
 *
 *  Version: 5.10
 *  State: public
 *
 *  File information
 *  ================
 *
*/



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// !!! NOTE !!!
//
//      In case you have reached this file because you would like to exclude some functions from being
//      traced by the library, following suggestion to not edit the distribution-file, you are looking at
//      right now.
//
//
//  Make a copy of this file.
//
//      Copy the file to your project and make your required changes in the copy, instead of this file.
//      It is quite unlikely this file will change much in the future, therefore making copies will
//      not interfere with any updates to lib_debug. - This way you can update to any newer version
//      without loosing your changes.
//
//
//  Handle project specifics
//
//      After making a copy of this file rename it to your convenience.
//      Now change the include in your main project from:
//
//          #include <MQLplus/lib_debug.mqh>
//
//      to >this copied file<, ie:
//
//          #include "my_renamed_lib_debug_CustomConfig.mqh"
//
//      Note the enclosure used, but I guess, since you reached this file, you are familiar with this.
//      Rename it to whatever you like. - You should include the name of the project, (for your future self...)
//      I.E: lib_debug_CustomConfig.mqh >>> lib_debug_MyProjectname.mqh
//
//
//  Use this file for your project
//
//      Now you just use this file as if it were the original include file from the distribution (lib_debug.mqh).
//      Apply your switches and changes as they are required for your project.
//      Integration is seemless and you will have a project specific lib_debug configuration.
//
//      Make changes to this file as required.
//
//
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////





/////////////////////////////////////////////////////////////////////////////////////////////////////
//
// LIB_DEBUG custom project configuration settings
//

//#define LIB_DEBUG
//#define LIB_DEBUG_AUTOENABLE
//#define LIB_DEBUG_LOGFILE
//#define LIB_DEBUG_NO_JOURNAL_OUTPUT
//#define LIB_DEBUG_MQLAPI_TRACE
//#define LIB_MQLAPI_TRACE
#define LIB_MQLAPI_TRACE_CUSTOM_EXTENSION
//#define LIB_PERF_PROFILING


//
/////////////////////////////////////////////////////////////////////////////////////////////////////





/////////////////////////////////////////////////////////////////////////////////////////////////////
//
// MT4/5 API remove function from the MQLAPI tracer
//
//
//  You can exclude any function from the tracer as your project needs it.
//
//  Use DBG_MQLAPI_NOTRACE_*** to remove a function from the tracer module.
//  You can remove any function from the tracer this way.
//
//  See example below.
//
//      For a complete list of all traced functions, see file
//          "MQLplus/lib_debug/lib_debug_mqlapi_tracer_overwrite_macros.mqh"
//

// This will remove 'Alert' function from the tracer module
//#define DBG_MQLAPI_NOTRACE_Alert

// This will remove 'round' function from the tracer module
//#define DBG_MQLAPI_NOTRACE_round


//
/////////////////////////////////////////////////////////////////////////////////////////////////////





/////////////////////////////////////////////////////////////////////////////////////////////////////
//
// LIB_DEBUG customize the API-Tracer to trace your own functions
//
//
//  1. Your function definition must be included first.
//
//      #include "your_custom_function_definitions.mqh"
//
//      // Now include this file instead of "lib_debug.mqh"
//      #include ">this...file<"
//
//
//  2. Inform the library of your custom extensions
//
//      If this is not defined, your custom extensions will not be loaded
//      this way you can turn them on and off as required. You can also
//      define this in your main file, outside of this file. Do so before you
//      include this file.
//      #define LIB_MQLAPI_TRACE_CUSTOM_EXTENSION
//
//
//  3. Write the tracer code as required
//
//      Now add your functions to this class for which you want tracing code to be injected
//      Follow the examle below to see how it is done.
//
//
//
/////////////////////////////////////////////////////////////////////////////////////////////////////





// Include trace code wrapper
#include <MQLplus/lib_debug/lib_debug_mqlapi_definitions.mqh>
#ifdef LIB_MQLAPI_TRACE_CUSTOM_EXTENSION
/////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Custom extension begin
//
#ifdef __MQL5__
    namespace dbg_lib {

#endif

    // API Tracer Object
    class LIB_DBG_MQLAPI_TRACER_CUSTOM_OBJECT
    { public:
    /********************************************************************************************************************************************************************
    *
    *
    *   YOUR CUSTOM CODE BEGINS HERE...
    *
    */


        /////////////////////////////////////////////////////////////////////////////////////////////////////
        //
        // Define the functions you want to trace
        //
        //  Function name must begin with
        //      "dbg_userapi_trace_func_"
        //  and then add your actual function name.
        //
        //
        // Example, this is your function you would like to trace
        //
        //  This function is defined somewhere in before included
        //  include file. "your_custom_function_definitions.mqh"
        //
        //      template <typename T>
        //      bool MyArraySort(T& array[]) { /* your function code body here */ };
        //
        // Important is parameter signatures of the functions
        //  are the same, see below.
        //
        /////////////////////////////////////////////////////////////////////////////////////////////////////





        /////////////////////////////////////////////////////////////////////////////////////////////////////
        //
        // Custom function with a return value
        //

            ////////////////////////////////////////////////////////
            //
            // Define the function wrapper
            //
            //  Wrapper function name must start with
            //  "dbg_userapi_trace_func_"
            //

            // Here we will use a templated function, you could use also normal functions.
            template <typename T>

            // The functino name must begin with "dbg_userapi_trace_func_", followed by your actual function name
            bool dbg_userapi_trace_func_MyArraySort(T& array[])
            {
                // Define the return value of your function
                DBG_USERAPI_RETURN_TYPE(bool);


                // Call the setup-macro for code tracing
                //  Takes in one argument, a name to identify your API.
                DBG_USERAPI_TRACE_SETUP("MyAPI");


                // Load the input parameters onto the tracer output stack,
                //  The macro must be filled with all 8 parameters.
                //  The first parameter is the count of arguments (1-7).
                DBG_USERAPI_PARAMS(1, array, 0, 0, 0, 0, 0, 0);


                // Now call your actual function, just as you would do in your normal code.
                retval = MyArraySort(array);


                // Load the output parameters onto the tracer output stack
                //  The macro must be filled with all 8 parameters.
                //  The first parameter is the count of arguments (1-7).
                DBG_USERAPI_RESULTS(1, array, 0, 0, 0, 0, 0, 0);


                // Output will be printed in case _LastError is not ERR_SUCCES / ERR_NO_ERROR
                //
                // For customization, it is possible to force printing, regardless of _LastError
                //  Insert the macro "DBG_USERAPI_PRINT_PARAMS" for printing input parameters.
                //  Insert the macro "DBG_USERAPI_PRINT_RESULT" for printing results.
                //
                //  If omitted, the default settings will apply.
                //DBG_USERAPI_PRINT_PARAMS;
                //DBG_USERAPI_PRINT_RESULT;


                // Call the finalize-macro for code tracing
                DBG_USERAPI_TRACE_FINALIZE;

                // Return the result from your function
                return(retval);
            };


            ////////////////////////////////////////////////////////
            //
            // Define the function replacement macro
            //
            //  If this is not defined, tracing will be disabled.
            //  The macro takes in one argument.
            //
            //  NOTE
            //      Define this after the wrapper function,
            //      else you will receive a 'Stack overflow'
            //      error.
            //
            #define MyArraySort                         DBG_USERAPI_TRACE_FUNCTION(MyArraySort)


        //
        /////////////////////////////////////////////////////////////////////////////////////////////////////






        /////////////////////////////////////////////////////////////////////////////////////////////////////
        //
        // Custom function of VOID-Type
        //

            ////////////////////////////////////////////////////////
            //
            // Define the function wrapper
            //
            //  Wrapper function name must start with
            //  "dbg_userapi_trace_func_"
            //
            void dbg_userapi_trace_func_MyPersonalVoidFunc(int arg01, int arg02, int arg03, int arg04, int arg05, int arg06, datetime arg07, string& arg08, color arg09, double arg10)
            {
                // Define the return value to be of type void.
                DBG_USERAPI_RETURN_TYPE_VOID;


                // Call the setup-macro for code tracing
                //  Takes in one argument, a name to identify your API
                DBG_USERAPI_TRACE_SETUP("MyAPI");


                // Load the input parameters onto the tracer output stack,
                //  The macro must be filled with all 8 parameters.
                //  The first parameter is the count of arguments.
                //
                //  Note the ordering of calls, the pushing to the stack
                //  is in reverse order, last arguments first.
                //
                //  arg08 is omitted here, because it is used as the output
                //  and therefore not relevant to us as an input to the function.
                //
                DBG_USERAPI_PARAMS(2, arg09, arg10, 0, 0, 0, 0, 0);
                DBG_USERAPI_PARAMS(7, arg01, arg02, arg03, arg04, arg05, arg06, arg07);


                // Some custom code, which will be part of the tracer output.
                // As an example, we will measure the runtime of this function
                // and print the result as part of the output stack.
                ulong runtime = GetMicrosecondCount();

                // Now call your actual function, just as you would do in your normal code.
                MyPersonalVoidFunc(arg01, arg02, arg03, arg04, arg05, arg06, arg07, arg08, arg09, arg10);

                // Finalize runtime measurement
                runtime = GetMicrosecondCount() - runtime;


                // Load the output parameters onto the tracer output stack.
                // Here we will push the result in arg08 from a reference to the output stack
                // In case your function has no output at all, this macro can be omitted.
                //
                // We will add the runtime as well to the output, so we can see the time
                // we measured.
                DBG_USERAPI_RESULTS(2, arg08, runtime, 0, 0, 0, 0, 0);


                // Output will be printed in case _LastError is not ERR_SUCCES / ERR_NO_ERROR
                //
                // For customization, it is possible to force printing, regardless of _LastError
                //  Insert the macro "DBG_USERAPI_PRINT_PARAMS" for printing input parameters.
                //  Insert the macro "DBG_USERAPI_PRINT_RESULT" for printing results.
                //
                //  If omitted, the default will apply.
                DBG_USERAPI_PRINT_PARAMS;
                DBG_USERAPI_PRINT_RESULT;


                // Call the finalize-macro for code tracing
                DBG_USERAPI_TRACE_FINALIZE;

                // Return void
                return;
            };


            ////////////////////////////////////////////////////////
            //
            // Define the function replacement macro
            //
            //  If this is not defined, tracing will be disabled.
            //  The macro takes in one argument.
            //
            //  NOTE
            //      Define this after the wrapper function,
            //      else you will receive a 'Stack overflow'
            //      error.
            //
            #define MyPersonalVoidFunc                  DBG_USERAPI_TRACE_FUNCTION(MyPersonalVoidFunc)



        //
        /////////////////////////////////////////////////////////////////////////////////////////////////////




                                                                                                                                                                       /*
                                                                                                                                                                        *
        ...YOUR CUSTOM CODE ENDS HERE                                                                                                                                   *
                                                                                                                                                                        *
                                                                                                                                                                        *
    *********************************************************************************************************************************************************************/
    };


#ifdef __MQL5__
    };  // Namespace dbg_lib

#endif
//
/////////////////////////////////////////////////////////////////////////////////////////////////////
#endif  // Custom extensions
// Close custom trace wrapper and include distribution library
#include <MQLplus/lib_debug.mqh>

// Include function overwrite macros
#include <MQLplus/lib_debug/lib_debug_mqlapi_tracer_overwrite_macros.mqh>


//
// END Debugging support
//*********************************************************************************************************************************************************/
#endif // LIB_DBG_DEBUG_MQLAPI_CUSTOMCONFIG_MQH_INCLUDED
📈 ROBOTFX MetaTrader Expert Advisors and Indicators to maximize profits and minimize the risks