Download MetaTrader 5

Information Storage and View

7 July 2006, 12:55
Andrey Khatimlianskii
4
3 161

1. Introduction

Have you ever spent hours of time trying to find some important information previously providently written by your expert into a log file? Or, perhaps, you are fully tired of peering at the small one-color letters displayed by the Comment() function? And yet, they can be of great importance for trading. If you know what I mean, this article is intended for you.

Below are the main problems I posed for myself writing this article:

  • How to create a function that can be used by the expert in order to write information into its native log file (the counterpart of the Print() function)?
  • How to create a tool that can make the displayed information have better visual impact: color and font size changes?


2. The Expert's Native Log File

As I said before, the Print() function is not always convenient for writing of information generated by the expert during its operation. This is especially evident when several experts trade in the same terminal simultaneously. Each expert writes its own information into the log file, and then it is rather difficult to find something there. In this case, the constructive analysis of such information is absolutely out of the question: it is very laborious and cumbersome.

This problem can be solved in a rather simple way. Every expert must have its own log file. And the information will be written in this separate log file of the expert, not in the general one. To be able to use this opportunity as easily as possible, let us write the code as a function.

Well, it is described below what the function must do:

  • create a file with a unique name
  • write information in it, as and when necessary
  • close the file as soon as the expert has finished operation in order the file to be available for other applications

Everything is clear. The only arguable thing is the necessity to close the file after every record. On the one hand, this would allow opening of the file in another appplication during the expert operation. But, on the other hand, it could be harmful if the expert would not be able to open the file to make the next record, since the file could be used by another application. In this case, information can be simply lost. This may not be allowed, especially since there are programs that can open files for reading only, without disturbing MetaTrader to work with them.

As opening and closing of the file is performed only once, the responsible code will be placed into the init() and deinit() functions, respectively. To make it occupy the minimum space, let us represent it as functions, as well:

int log_handle = -1;
 
//+------------------------------------------------------------------+
// void log_open( string ExpertName = "Expert" )
//
// Function that opens the expert's separate log file.
// Directory, in which the file will be created:
// "...\MetaTrader 4\experts\files\logs\ExpertName\"
// The file name is the date of the file record appeared as "YYYY.MM.DD"
//+------------------------------------------------------------------+
void log_open( string ExpertName = "Expert" )
 {
     string log_name = "logs\\" + ExpertName + " (" + Symbol() + ", " + 
                   strPeriod( Period() ) + ")\\" + TimeToStr( LocalTime(),
                   TIME_DATE ) + ".txt";
  log_handle = FileOpen ( log_name, FILE_READ | FILE_WRITE, " " );
    
  if ( log_handle < 0 )
     { 
         int _GetLastError = GetLastError();
    Print( "FileOpen( ", log_name, ", FILE_READ | FILE_WRITE, \" \" ) - Error #", 
                     _GetLastError );
    return(-1);
   }
 }
string strPeriod( int intPeriod )
 {
     switch ( intPeriod )
     {
          case PERIOD_MN1: return("Monthly");
    case PERIOD_W1:  return("Weekly");
    case PERIOD_D1:  return("Daily");
    case PERIOD_H4:  return("H4");
    case PERIOD_H1:  return("H1");
    case PERIOD_M30: return("M30");
    case PERIOD_M15: return("M15");
    case PERIOD_M5:  return("M5");
    case PERIOD_M1:  return("M1");
    default:        return("UnknownPeriod");
   }
 }
 
//+------------------------------------------------------------------+
// log_close()
//
// Function that closes the expert's native log file.
//+------------------------------------------------------------------+
void log_close()
 {
     if ( log_handle > 0 ) FileClose( log_handle );
 }
Now we have an open file, information can be written into it. To do so:
  • move the cursor to the end of the file in order not to lose the information;
  • substitute the recording time to the beginning of the line; this will be useful for analysis;
  • write the text into the file;
  • store the completed file on the disk; if the expert terminates its operation disorderly, this will prevent data losses

The following function must result from the above:

//+------------------------------------------------------------------+
// log( string text )
//
// The function that writes the text line into the expert's native log file.
//+------------------------------------------------------------------+
void log( string text )
 {
     int _GetLastError = 0;
  if ( log_handle < 0 )
     {
         Print( "Log write error! Text: ", text );
    return(-1);
   }
    
     //---- Move the file pointer to the end of file
    if ( !FileSeek ( log_handle, 0, SEEK_END ) )
      {
         _GetLastError = GetLastError();
    Print( "FileSeek ( " + log_handle + ", 0, SEEK_END ) - Error #", 
          _GetLastError );
    return(-1);
  }
    //---- If the line to be written by the expert is not the line feed character, 
    //---- add the recording time at the beginning of the line
    if( text != "\n" && text != "\r\n" )
         text = StringConcatenate( TimeToStr( LocalTime(), TIME_SECONDS ), 
                             " - - - ", text );
    if( FileWrite ( log_handle, text ) < 0 )
          {
             _GetLastError = GetLastError();
     Print( "FileWrite ( ", log_handle, ", ", text, " ) - Error #", 
           _GetLastError );
     return(-1);
    }
    
    //---- Save the written text on the disk
    FileFlush( log_handle );
 }

Now, the word Print can be easily replaced with the word log in all experts, calls of the log_open and log_close functions not to be forgotten.

This is a simple exemplary expert using the included file log.mq4:

#include <log.mq4>
 
int init()
  {
    log_open( "log_test" );
    log( "The log file has been successfully opened, the expert starts working..." );
    return(0);
  }
int deinit()
  {
    log( "Close the log file, the expert finishes work..." );
    log_close();
    return(0);
  }
int start()
  {
    log( "New tick: Bid = " + DoubleToStr( Bid, Digits ) );
    return(0);
  }

3. Information Displaying

Now, when we have solved the problem of the log file, we can start "decorating" the displayed information.

First of all, let us consider all possible ways of how to implement this task. In MQL4, the Comment() function is responsible for displaying of the information, but it does not suit for the reasons described above. So we have to find another solution. A good example of this can be objects containing texts. There are only two of them: "Text" and "Text Label". The fundamental difference between them is that the "Text" is anchored to the chart coordinates (price and time), and the "Text Label" is to the windows coordinates. Since we need information to remain on the spot when the chart moves or its scale is changed, this will be the "Text Label" that we will use.

There are some functions in MQL4 that create and control objects, all names starting with the word Object. Let us see what of them can be useful for our purposes:

  • bool ObjectCreate(...) – to create an object;
  • bool ObjectDelete(...) – to delete objects after usage;
  • bool ObjectSet(...) – to change the object properties, such as anchor (x,y);
  • bool ObjectSetText(...) – to display the text;
  • void ObjectsRedraw() – to redraw objects after the text has been changed.

Well, this is what we have to do:

  • in the expert function named init(), create objects to display information;
  • the the expert function named deinit(), delete all created objects;
  • in the start() function, be able to change text, font color and size of all created objects.

We will get 3 functions again, each completing its own task.

Before writing the code, I would like to mention an unpleasant limitation concerning usage of the "Text Label". It can be only of one line, i.e., it may not contain any line feed characters. But information is acquired much better if it is displayed in several lines. This is why we will create several objects and then distribute the data among them. I made five "lines", but you can use any other amounts of lines.

Besides, there is a limitation concerning the length of the displayed text. So I have added the second "column", i.e., five more lines on the right-hand side.

This is how the function info_init() that creates objects looks:

/////////////////////////////////////////////////////////////////////////////////
// void info_init()
//
// Creation of objects to display information
/////////////////////////////////////////////////////////////////////////////////
void info_init()
  {
    for( int row = 0; row <= 4; row ++ )
      {
        _LabelCreate( StringConcatenate( "InfoLabel_0", row ),   4, 15 + 15*row );
        _LabelCreate( StringConcatenate( "InfoLabel_1", row ), 270, 15 + 15*row );
      }
  }
 
/////////////////////////////////////////////////////////////////////////////////
// void _LabelCreate ( string _Name, int _XDistance, int _YDistance, int _Corner = 0 )
//
// Creation of "Text Label" named as _Name.
// Coordinates: х = _XDistance, у = _YDistance, corner = _Corner.
/////////////////////////////////////////////////////////////////////////////////
void _LabelCreate ( string _Name, int _XDistance, int _YDistance, int _Corner = 0 )
  {
    int _GetLastError;
 
    if( !ObjectCreate( _Name, OBJ_LABEL, 0, 0, 0 ) )
      {
        _GetLastError = GetLastError();
        if ( _GetLastError != 4200 )
          {
            Print( "ObjectCreate( \"", _Name, "\", OBJ_LABEL,0,0,0 ) - Error #",
                   _GetLastError );
            return(-1);
          }
      }
    if( !ObjectSet( _Name, OBJPROP_CORNER, _Corner ) )
      {
        _GetLastError = GetLastError();
        Print( "ObjectSet( \"", _Name, "\", OBJPROP_CORNER, ", _Corner, 
                                       " ) - Error #", _GetLastError );
      }
    if( !ObjectSet( _Name, OBJPROP_XDISTANCE, _XDistance ) )
      {
        _GetLastError = GetLastError();
        Print( "ObjectSet( \"", _Name, "\", OBJPROP_XDISTANCE, ", _XDistance, 
                                             " ) - Error #", _GetLastError );
      }
    if( !ObjectSet( _Name, OBJPROP_YDISTANCE, _YDistance ) )
      {
        _GetLastError = GetLastError();
        Print( "ObjectSet( \"", _Name, "\", OBJPROP_YDISTANCE, ", _YDistance, 
                                             " ) - Error #", _GetLastError );
      }
    if( !ObjectSetText ( _Name, "", 10 ) )
      {
        _GetLastError = GetLastError();
        Print( "ObjectSetText( \"", _Name, "\", \"\", 10 ) - Error #", _GetLastError );
      }
  }

As you can see, objects will be named as "InfoLabel_" + object number (from 00 to 04 for the left "column" and from 10 to 14 for the right one). The objects are anchored to the upper left corner. A space is intentionally made before, since many users got used to see OHLC (the current bar information) there. I made the vertical space between lines equal to 15, this is enough for a text of a normal size. So, initialization is over, let us make the deinitialization. They will be rather similar:

/////////////////////////////////////////////////////////////////////////////////
// void info_deinit()
//
// Deletion of objects created by the info_init() function
/////////////////////////////////////////////////////////////////////////////////
void info_deinit()
 {
     int _GetLastError;
   for ( int row = 0; row <= 4; row ++ )
      {
          if ( !ObjectDelete( StringConcatenate( "InfoLabel_0", row ) ) )
           {
                   _GetLastError = GetLastError();
       Print( "ObjectDelete( \"", StringConcatenate( "InfoLabel_0", row ), 
                                        "\" ) - Error #", _GetLastError );
      }
          if( !ObjectDelete( StringConcatenate( "InfoLabel_1", row ) ) )
               {
                  _GetLastError = GetLastError();
       Print( "ObjectDelete( \"", StringConcatenate( "InfoLabel_1", row ), 
                                       "\" ) - Error #", _GetLastError );
      }
       }
 }

Now, the most interesting thing is the information displaying itself.

Thus, we have 10 objects, which are each on their places and ready to "accept" the text to be displayed. Let us think of how to make the use of the displaying function as simple as possible.

First, it is simpler to specify the number, not the name of the object.

Second, only two parameters must be used: the object number and the displayed text number. Other parameters can be optional since the text color or the font size do not often need to be changed. Here, we have to think over what to do if the parameters are skipped. There are two choices:

  • to use parameters by default;
  • or to use the last used parameters.

I think the second choice to be more useful: The default parameters would need being changed in the code for every expert, and the last used parameters will be saved automatically at every use of the function.

Thus, this is how our function looks:

void info( int LabelNumber, string Text, color Color = -1, 
              double FontSize = -1.0, string Font = "-1" )
 {
     //---- define the object name
      string LabelName;
  if ( LabelNumber < 10 )
          LabelName = StringConcatenate( "InfoLabel_0", LabelNumber );
  else
         LabelName = StringConcatenate( "InfoLabel_" , LabelNumber );
 
  //---- if the additional parameters were not specified, 
    //---- set the last used values for them
    if ( Color < 0 ) 
          Color = lastusedColor;
  if ( FontSize < 0 ) 
     FontSize = lastusedFontSize;
  if ( Font == "-1" ) 
     Font = lastusedFont;
 
  //---- save the last used values
    lastusedColor = Color;
  lastusedFontSize = FontSize;
  lastusedFont = Font;

To avoid situations where the last used variables contain the null as a value, let us assign them their values immediately at declaration:

color lastusedColor = Black;
double lastusedFontSize = 9.0;
string lastusedFont = "Arial";

You could probably note that the last used variables are declared out of functions. This will prevent their values from zeroizing at every info() function call.

And now, let us display the new text and redraw the objects. This must be done for the changes that we have made to the object to be displayed immediately:

 //---- display the new text
    if( !ObjectSetText( LabelName, Text, FontSize, Font, Color ) )
      {
        int _GetLastError = GetLastError();
        Print( "ObjectSetText( \"", LabelName,"\", \"", Text, "\", ", FontSize, ", ", Font, 
                                             ", ", Color, " ) - Error #", _GetLastError );
      }
    //---- redraw the objects
    ObjectsRedraw();
  }

The full code of all three functions can be found in the attached file named info.mq4.

Now, let us check what we have got:




I suppose this to be very good.

Finally, we can create one more convenience – a function that would clear the entire information. It will be named info_clear(). I am sure you know how to use it.

void info_clear()
 {
     for ( int n = 0;  n < 5;  n ++ ) 
       info( n, "" );
  for (     n = 10; n < 15; n ++ ) 
       info( n, "" );
 }

4. Summary

The article described alternative methods of how to keep log files and display information. Two included files were created to be kept in the "experts/include" folder: the log.mq4 and the info.mq4. They can be used from any expert.

Translated from Russian by MetaQuotes Software Corp.
Original article: https://www.mql5.com/ru/articles/1405

Attached files |
info.mq4 (4.5 KB)
info_test_eng.mq4 (1.24 KB)
log.mq4 (3.19 KB)
log_test_eng.mq4 (0.76 KB)
Last comments | Go to discussion (4)
OldZ
OldZ | 11 May 2010 at 06:05

It is helpful.Thanks.

MQL4 Comments
MQL4 Comments | 22 Oct 2011 at 22:57
greet !!
nduru22
nduru22 | 27 Mar 2013 at 14:20
should function void_init() be placed under the init() special function? and the same for void_deinit()?
nduru22
nduru22 | 27 Mar 2013 at 14:53
sorry, have been able to do it. great. thanks
Step on New Rails: Custom Indicators in MQL5 Step on New Rails: Custom Indicators in MQL5

I will not list all of the new possibilities and features of the new terminal and language. They are numerous, and some novelties are worth the discussion in a separate article. Also there is no code here, written with object-oriented programming, it is a too serous topic to be simply mentioned in a context as additional advantages for developers. In this article we will consider the indicators, their structure, drawing, types and their programming details, as compared to MQL4. I hope that this article will be useful both for beginners and experienced developers, maybe some of them will find something new.

Here Comes the New MetaTrader 5 and MQL5 Here Comes the New MetaTrader 5 and MQL5

This is just a brief review of MetaTrader 5. I can't describe all the system's new features for such a short time period - the testing started on 2009.09.09. This is a symbolical date, and I am sure it will be a lucky number. A few days have passed since I got the beta version of the MetaTrader 5 terminal and MQL5. I haven't managed to try all its features, but I am already impressed.

False trigger protection for Trading Robot False trigger protection for Trading Robot

Profitability of trading systems is defined not only by logic and precision of analyzing the financial instrument dynamics, but also by the quality of the performance algorithm of this logic. False trigger is typical for low quality performance of the main logic of a trading robot. Ways of solving the specified problem are considered in this article.

Using text files for storing input parameters of Expert Advisors, indicators and scripts Using text files for storing input parameters of Expert Advisors, indicators and scripts

The article describes the application of text files for storing dynamic objects, arrays and other variables used as properties of Expert Advisors, indicators and scripts. The files serve as a convenient addition to the functionality of standard tools offered by MQL languages.