Download MetaTrader 5

Using the Object Pointers in MQL5

16 April 2010, 09:51
MetaQuotes Software Corp.
0
5 337

Introduction

In MQL5, you may create your own class for the further use of class type variables in your code. As we already know from the article The Order of Object Creation and Destruction in MQL5, the structures and classes can be created in two ways - automatically and dynamically.

To create an object automatically simply declare a class type variable - the system will create and initialize it automatically. To create an object dynamically it's necessary to apply the operator new  to the object pointer explicitly.

However, what is the difference between the objects created automatically and dynamically, and when we need the necessarily use of the object pointer, and when it's sufficient to create the objects automatically? This topic is the subject of this article. The first, let's discuss some possible pitfalls when working with objects and consider the ways to fix them.

A Critical Error in Access to Invalid Pointer

The first, that you should remember when using the object pointers - it's mandatory initialization of the object before its use. When you access to invalid pointer, the work of MQL program terminates with a critical error, so the program is removed. As an example, let's consider a simple Expert Advisor, with class CHello, declared there. The pointer to the class instance is declared at global level.

//+------------------------------------------------------------------+
//|                                             GetCriticalError.mq5 |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2009, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//| A simple class                                                   |
//+------------------------------------------------------------------+
class CHello
  {
private:
   string            m_message;
public:
                     CHello(){m_message="Starting...";}
   string            GetMessage(){return(m_message);}
  };
//---
CHello *pstatus;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- calling a method to show status
   Print(pstatus.GetMessage());
//--- printing a message if Expert Advisor has been initialized successfully
   Print(__FUNCTION__," The OnInit() function is completed");
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---

  }
//+------------------------------------------------------------------+

The pstatus variable is the object pointer, but we intentionally "forgot" to create the object itself using the new operator. If you will try to launch this Expert Advisor on the EURUSD chart, you will see the natural result - the Expert Advisor has been immediately unloaded at the stage of execution of the OnInit() function. The messages that appears in the Experts Journal are the following:

14:46:17 Expert GetCriticalError (EURUSD, H1) loaded successfully
14:46:18 Initializing of GetCriticalError (EURUSD, H1) failed
14:46:18 Expert GetCriticalError (EURUSD, H1) removed

This example is a very simple, the error catching is easy. However, if your MQL5-program contains hundreds or even thousands of lines of code, catching of these errors may be greatly complicated. It's important, especially for the cases of emergency situation conditions in the program behavior depends on the unpredictable factors - for example, on the particular market structure.

Checking of the Pointer Before its Use

Was it possible to avoid the critical program termination? Yes, of course! It's sufficient to insert the check of the object pointer before its use. Let's modify this example by adding the PrintStatus function:

//+------------------------------------------------------------------+
//| Prints a message using a method of CHello type object            |
//+------------------------------------------------------------------+
void PrintStatus(CHello *pobject)
  {
   if(CheckPointer(pobject)==POINTER_INVALID)
      Print(__FUNCTION__," the variable 'object' isn't initialized!");
   else Print(pobject.GetMessage());
  }

Now this function calls the GetMessage() method, the pointer of the object of CHello type is passed to the function. The first, it checks the pointer using the function CheckPointer(). Let's add an external parameter and save the code of an Expert Advisor to the file GetCriticalError_OnDemand.mq5.

//+------------------------------------------------------------------+
//|                                    GetCriticalError_OnDemand.mq5 |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2009, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"

input bool GetStop=false;// To get a critical error
//+------------------------------------------------------------------+
//| A simple class                                                   |
//+------------------------------------------------------------------+
class CHello
  {
private:
   string            m_message;
public:
                     CHello(){m_message="Starting...";}
   string            GetMessage(){return(m_message);}
  };
//---
CHello *pstatus;
//+------------------------------------------------------------------+
//| Prints a message using a method of CHello type object            |
//+------------------------------------------------------------------+
void PrintStatus(CHello *pobject)
  {
   if(CheckPointer(pobject)==POINTER_INVALID)
      Print(__FUNCTION__," the variable 'object' isn't initialized!");
   else Print(pobject.GetMessage());
  }
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- calling a method to show status
   if(GetStop)
      pstatus.GetMessage();
   else
      PrintStatus(pstatus);
//--- printing a message if Expert Advisor has been initialized successfully
   Print(__FUNCTION__," The OnInit() function is completed");
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---

  }
//+------------------------------------------------------------------+

Now we can launch the Expert Advisor in two ways:

  1. With a critical error (GetStop = true)
  2. Without an error, but with the message about invalid pointer (GetStop = false)

By default, the execution of Expert Advisor is successful and the following message appears in the "Experts" Journal:

GetCriticalError_OnDemand (EURUSD, H1) 15:01:57 PrintStatus the variable 'object' isn't initialized!
GetCriticalError_OnDemand (EURUSD, H1) 15:01:57 OnInit function OnInit () is completed

Thus, the checking of the pointer before its use allows to avoid the critical errors.

Always check for the pointer correctness before its use in the function

Passing the Uninitialized Object by Reference

What happens if you will pass the uninitialized object as an input parameter of the function? (the object itself by reference, not the object pointer). The complex objects, such as classes and structures are passed by reference with an ampersand. Let's rewrite some code of the GetCriticalError_OnDemand.mq5. Let's rename the PrintStatus() function and rewrite its code in a different way.

//+------------------------------------------------------------------+
//| Prints a message using a method of CHello type object            |
//+------------------------------------------------------------------+
void UnsafePrintStatus(CHello &object)
  {
   DebugBreak();
   if(CheckPointer(GetPointer(object))==POINTER_INVALID)
      Print(__FUNCTION__," the variable 'object' isn't initialized!");
   else Print(object.GetMessage());
  }

Now the difference is that the variable itself of this type is passed by reference as an input parameter instead of the object pointer of CClassHello type. Let's save the new version of an Expert Advisor as GetCriticalError_Unsafe.mq5.

//+------------------------------------------------------------------+
//|                                      GetCriticalError_Unsafe.mq5 |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2009, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"

input bool GetStop=false;// To get a critical error
//+------------------------------------------------------------------+
//| A simple class                                                   |
//+------------------------------------------------------------------+
class CHello
  {
private:
   string            m_message;
public:
                     CHello(){m_message="Starting...";}
   string            GetMessage(){return(m_message);}
  };
//---
CHello *pstatus;
//+------------------------------------------------------------------+
//| Prints a message using a method of CHello type object            |
//+------------------------------------------------------------------+
void UnsafePrintStatus(CHello &object)
  {
   DebugBreak();
   if(CheckPointer(GetPointer(object))==POINTER_INVALID)
      Print(__FUNCTION__," the variable 'object' isn't initialized!");
   else Print(object.GetMessage());
  }
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- calling a method to show status
   if(GetStop)
      pstatus.GetMessage();
   else
      UnsafePrintStatus(pstatus);
//--- printing a message if Expert Advisor has been initialized successfully
   Print(__FUNCTION__," The OnInit() function is completed");
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---

  }
//+------------------------------------------------------------------+

One can see the difference between the GetCriticalError_OnDemand.mq5 and GetCriticalError_Unsafe.mq5 Expert Advisors in the way of parameter passing to the function. For the first case, the object pointer is passed to the function, and for the second case the object itself is passed by reference. In both cases, before the use of the object and its pointer, the function checks it for the pointer correctness.

Does it means that Expert Advisors will work the same way? No, it doesn't! Let's launch the Expert Advisor with parameter GetStop=false, and again we will get a critical error. The fact is that if an object is passed by reference, the critical error occurs at the stage of a function call, because the uninitialized object is passed as a parameter. You can verify it, if you launch the script in debug mode directly from MetaEditor5, using the  F5 button.

To avoid the use of the manual breakpoints, let's modify the function by adding the DebugBreak () breakpoint inside of the function.

//+------------------------------------------------------------------+
//| Prints a message using a method of CHello type object            |
//+------------------------------------------------------------------+
void UnsafePrintStatus(CHello &object)
  {
   DebugBreak();
   if(CheckPointer(GetPointer(object))==POINTER_INVALID)
      Print(__FUNCTION__," the variable 'object' isn't initialized!");
   else Print(object.GetMessage());
  }

In debug mode, the Expert Advisor will unloaded before a call of the DebugBreak() function. How to write the secure code for the case if an uninitialized object is passed by the reference? The answer is simple - to use the function overloading.

If a pointer of an uninitialized object is passed by reference as a parameter of the function, it will lead to a critical error and stop the mql5-program.

Using the Overloaded Functions for Safe Code

It would be very inconvenient, if the developer, that uses the external library, had forced to carry out checking of the input objects for the correctness. It's much better to perform all the necessary checks inside the library, it can be implemented using the function overloading.

Let's implement the method UnsafePrintStatus() with the function overloading and write two versions of this function - the first that uses the passed object pointer instead of the object itself, the second that uses the passing of object by reference. Both functions will have the same name "PrintStatus", but this implementation will be no longer potentially dangerous.

//+------------------------------------------------------------------+
//| The safe printing of a message using the CHello object pointer     |
//+------------------------------------------------------------------+
void SafePrintStatus(CHello *pobject)
  {
   if(CheckPointer(pobject)==POINTER_INVALID)
      Print(__FUNCTION__," the variable 'object' isn't initialized!");
   else Print(pobject.GetMessage());
  }
//+------------------------------------------------------------------+
//| Printing a message using the CHello object, passed by reference  |
//+------------------------------------------------------------------+
void SafePrintStatus(CHello &pobject)
  {
   DebugBreak();
   SafePrintStatus(GetPointer(pobject));
  }

Now for the case if the function is called by passing of the object pointer, the checking for a correctness is performed and the critical error doesn't occur. If you call the overloaded function with passing of an object by reference, the first we get the object pointer using the GetPointer function and then we call the safe code, that uses the passing of an object by the pointer.

We can rewrite a secure version of the function is even shorter, instead of

void SafePrintStatus (CHello & pobject)
  (
   DebugBreak ();
   CHello * p = GetPointer (pobject);
   SafePrintStatus (p);
  )

let's write the compact version:

void SafePrintStatus (CHello & object)
  (
   DebugBreak ();
   SafePrintStatus (GetPointer (object));
  )

The both versions are equal. Let's save the second version with the name GetCriticalError_Safe.mq5.

//+------------------------------------------------------------------+
//|                                        GetCriticalError_Safe.mq5 |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2009, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"

input bool GetStop=false;// To get a critical error
//+------------------------------------------------------------------+
//| A simple class                                                   |
//+------------------------------------------------------------------+
class CHello
  {
private:
   string            m_message;
public:
                     CHello(){m_message="Starting...";}
   string            GetMessage(){return(m_message);}
  };
//---
CHello *pstatus;
//+------------------------------------------------------------------+
//| The safe printing of a message using the CHello object pointer     |
//+------------------------------------------------------------------+
void SafePrintStatus(CHello *pobject)
  {
   if(CheckPointer(pobject)==POINTER_INVALID)
      Print(__FUNCTION__," the variable 'object' isn't initialized!");
   else Print(pobject.GetMessage());
  }
//+------------------------------------------------------------------+
//| Printing a message using the CHello object, passed by reference  |
//+------------------------------------------------------------------+
void SafePrintStatus(CHello &pobject)
  {
   DebugBreak();
   SafePrintStatus(GetPointer(pobject));
  }
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- calling a method to show status
   if(GetStop)
      pstatus.GetMessage();
   else
      SafePrintStatus(pstatus);
//--- printing a message if Expert Advisor has been initialized successfully
   Print(__FUNCTION__," The OnInit() function is completed");
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---

  }
//+------------------------------------------------------------------+

If you use two functions with the different implementations (by passing the reference and by passing the object pointer), it allows to ensure the safe work of the overloaded function.

Finally, we have learned how to use the objects, passed to the function as parameters, now it's time to learn:

When do You Need Pointers?

The object pointers allow to perform a flexible management of the process of objects creation and destroying, and allow you to create more complex abstract objects. It makes the program more flexible.

Linked List

But in some cases a different way of data organization is required, the linked list is the one of them. The class of a linked list CList is available in the Standard Library, here we will present our own examples. A linked list means, that each list item is connected with the next and previous items, if they exist. To organize such links, it's convenient to use the object pointers of list items (ListItem).

Let's create a class, that will represent an item of a list, like this one:

class CListItem
  {
private:
   int               m_ID;
   CListItem        *m_next;
   CListItem        *m_prev;
public:
                    ~CListItem();
   void              setID(int id){m_ID=id;}
   int               getID(){return(m_ID);}
   void              next(CListItem *item){m_next=item;}
   void              prev(CListItem *item){m_prev=item;}
   CListItem*        next(){return(m_next);}
   CListItem*        prev(){return(m_prev);}
  };

The list itself will be organized in a separate class:

//+------------------------------------------------------------------+
//| Linked list                                                      |
//+------------------------------------------------------------------+
class CList
  {
private:
   int               m_counter;
   CListItem        *m_first;
public:
                     CList(){m_counter=0;}
                    ~CList();
   void              addItem(CListItem *item);
   int               size(){return(m_counter);}
  };

The CList class contains a pointer m_first  of the first list item, an access to the other list items is always available via the next () and prev() functions of the CListItem() class. The CList class has two interesting functions. The first is the function for adding of a new item to the list.

//+------------------------------------------------------------------+
//| Adding of an item to the list                                    |
//+------------------------------------------------------------------+
CList::addItem(CListItem *item)
  {
//--- checking of a pointer, it should be correct
   if(CheckPointer(item)==POINTER_INVALID) return;
//--- increasing the number of list items
   m_counter++;
//--- if there isn't any items in the list
   if(CheckPointer(m_first)!=POINTER_DYNAMIC)
     {
      m_first=item;
     }
   else
     {
      //--- setting for first a pointer to the previous item
      m_first.prev(item);
      //--- saving a pointer of the current first item
      CListItem *p=m_first;
      //--- placing the input item on the place of the first element
      m_first=item;
      //--- for the first item in the list, setting a pointer to the next item 
      m_first.next(p);
     }
  }

An each item, added to the list, becomes the first item, the pointer of the previous first item is stored in the m_next  field. Thus, an item, added to the list first, will be at the end of the list. The last added item, will be the first, and its pointer is stored in a m_first  variable. The second interesting function is the ~CList() destructor. It calls when the object is destroyed, it should ensure the correct destruction of the list objects. It can be done very simple:

//+------------------------------------------------------------------+
//| List destructor                                                  |
//+------------------------------------------------------------------+
CList::~CList(void)
  {
   int ID=m_first.getID();
   if(CheckPointer(m_first)==POINTER_DYNAMIC) delete(m_first);
   Print(__FUNCTION__," The first item with ID =",ID," is destroyed");
  }

One can see, if the m_first  contain the correct pointer of the list item, only first list item is removed. All other list items are removed avalanche, because the CListItem()  class destructor in turn, produces the correct object deinitialization.

//+------------------------------------------------------------------+
//| Item destructor                                                  |
//+------------------------------------------------------------------+
CListItem::~CListItem(void)
  {
   if(CheckPointer(m_next)==POINTER_DYNAMIC)
     {
      delete(m_next);
      Print(__FUNCTION__," Removing an item with ID =",m_ID);
     }
   else
      Print(__FUNCTION__," The next item isn't defined for the item with ID=",m_ID);

  }

The correctness of a pointer is verified inside the destructor, if it specified, the object with pointer m_next  is destroyed using the delete() operator. Before the destruction, the first list item calls the destructor, it will delete the second item, then it will cause the deletion of the third item and so on until the end of the chain.

The work of the list is shown in the script SampleList.mq5. The declaration of a list (a variable of CListType) is in the OnStart () function. This list will be created and initialized automatically. The filling of the list is performed inside of a list, and the first, each list item is created dynamically using the new operator and then it added to the list. 

void OnStart()
  {
//---
   CList list;
   for(int i=0;i<7;i++)
     {
      CListItem *item=new CListItem;
      item.setID(i);
      list.addItem(item);
     }
     Print("There are ",list.size()," items in the list");
  }

Launch the script and you will see the following messages in the "Experts" journal.

2010.03.18 11:22:05 SampleList (EURUSD, H1) CList:: ~ CList The first item with ID=6 is destroyed
2010.03.18 11:22:05 SampleList (EURUSD, H1) CListItem:: ~ CListItem Removing an item with ID = 6
2010.03.18 11:22:05 SampleList (EURUSD, H1) CListItem:: ~ CListItem Removing an item with ID = 5
2010.03.18 11:22:05 SampleList (EURUSD, H1) CListItem:: ~ CListItem Removing an item with ID = 4
2010.03.18 11:22:05 SampleList (EURUSD, H1) CListItem:: ~ CListItem Removing an item with ID = 3
2010.03.18 11:22:05 SampleList (EURUSD, H1) CListItem:: ~ CListItem Removing an item with ID = 2
2010.03.18 11:22:05 SampleList (EURUSD, H1) CListItem:: ~ CListItem Removing an item with ID = 1
2010.03.18 11:22:05 SampleList (EURUSD, H1) CListItem:: ~ CListItem The next item isn't defined for the item with ID=0
2010.03.18 11:22:05 SampleList (EURUSD, H1) There are 7 items in the list

If you have studied the code carefully, it isn't a surprise that item with ID=0 doesn't has the next following item. However, we have considered only one reason, when use of the pointers makes writing of programs convenient and readable. There is a second reason, and it's called as

Polymorphism

It's often necessary to implement the same functionality for different objects, belonging to the same type. For example, there are simple objects like Line, Triangle, Rectangle and Circle. Despite the fact that they look differently, they have a common feature - they can be drawn. Let's create a base class CShape, and use it for creation of its descendants for each type of geometric shapes.


For the educational purposes, the base class and its descendants have a minimal functionality.

//+------------------------------------------------------------------+
//| The base class for a Shape object                                |
//+------------------------------------------------------------------+
class CShape
  {
private:
   int               m_type;
public:
                     CShape(){m_type=0;}
   void              Draw();
   string            getTypeName(){return("Shape");}
  };
//+------------------------------------------------------------------+
//| The class for a Line object                                      |
//+------------------------------------------------------------------+
class CLine:public CShape
  {
private:
   int               m_type;
public:
                     CLine(){m_type=1;}
   void              Draw();
   string            getTypeName(){return("Line");}
  };
//+------------------------------------------------------------------+
//| The class for a Triangle object                                  |
//+------------------------------------------------------------------+
class CTriangle:public CShape
  {
private:
   int               m_type;
public:
                     CTriangle(){m_type=2;}
   void              Draw();
   string            getTypeName(){return("Triangle");}
  };
//+------------------------------------------------------------------+
//| The class for a Rectangle object                                 |
//+------------------------------------------------------------------+
class CRectangle:public CShape
  {
private:
   int               m_type;
public:
                     CRectangle(){m_type=3;}
   void              Draw();
   string            getTypeName(){return("Rectangle");}
  };
//+------------------------------------------------------------------+
//| The class for a Cirlce object                                    |
//+------------------------------------------------------------------+
class CCircle:public CShape
  {
private:
   int               m_type;
public:
                     CCircle(){m_type=4;}
   void              Draw();
   string            getTypeName(){return("Circle");}
  };

The parent CShape class contains two functions, which are overridden in his descendants - a Draw() and getTypeName(). The Draw() function is intended to draw a shape, and the getTypeName() function returns a string description of the shape.

Let's create a *shapes[] array, which contains pointers of the base type CShape, and specifies the values of a pointer for the different classes.

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- an array of pointers of objects of CShape type
   CShape *shapes[];
//--- resizing of an array 
   ArrayResize(shapes,5);
//--- filling of a pointers array
   shapes[0]=new CShape;
   shapes[1]=new CLine;
   shapes[2]=new CTriangle;
   shapes[3]=new CRectangle;
   shapes[4]=new CCircle;
//--- printing the type of each array element
   for(int i=0;i<5;i++)
     {
      Print(i,shapes[i].getTypeName());
     }
//--- deleting all objects in the array
   for(int i=0;i<5;i++) delete(shapes[i]);
  }

Inside of the for()  cycle we call the getTypeName () method for each element of the *shapes[]  array. The first time you may be surprised the fact, that the getTypeName() function of a base class calls for the each object in the list, despite the fact that the each derived class has its own implementation of the getTypeName() function.

2010.03.18 14:06:18 DemoPolymorphism (EURUSD, H1) 4 Shape
2010.03.18 14:06:18 DemoPolymorphism (EURUSD, H1) 3 Shape
2010.03.18 14:06:18 DemoPolymorphism (EURUSD, H1) 2 Shape
2010.03.18 14:06:18 DemoPolymorphism (EURUSD, H1) 1 Shape
2010.03.18 14:06:18 DemoPolymorphism (EURUSD, H1) 0 Shape

The explanation of this fact is the following: the *shapes []  array is declared as an array of pointers of CShape type, and therefore the each array object calls the getTypeName() method of a base class, even if a descendant has a different implementation. To call the getTypeName() function corresponding to the actual object type (descendant) at the moment of the program execution, it's necessary to declare this function in a base class as a virtual function.

Let's add the virtual keyword to the getTypeName() function in the declaration of a parent CShape class.

class CShape
  (
private:
   int m_type;
public:
                     CShape () (m_type = 0;)
   void Draw ();
   virtual string getTypeName () (return ("Shape");)
  )

and launch the script again. Now the results are consistent with those expected:

2010.03.18 15:01:11 DemoPolymorphism (EURUSD, H1) 4 Circle
2010.03.18 15:01:11 DemoPolymorphism (EURUSD, H1) 3 Rectangle
2010.03.18 15:01:11 DemoPolymorphism (EURUSD, H1) 2 Triangle
2010.03.18 15:01:11 DemoPolymorphism (EURUSD, H1) 1 Line
2010.03.18 15:01:11 DemoPolymorphism (EURUSD, H1) 0 Shape

So, the declaration of a virtual function in a base class allowed to call the same function of a descendant during the execution of program. Now we can implement the fully-featured  Draw() function for the each of derived classes.

An example of its work can be found in the attached  DrawManyObjects.mq5 script, which shows a random shapes on the chart.

Conclusion

So, it's time to sum up. In MQL5, the objects creation and destroying is performed automatically, so you should use the pointers only if they are really needed, and if you understand how to work with them.

However, if you cannot do it without the use of pointers, be sure to check the pointer correctness before its use, using the CheckPointer() - it has been added special for these cases.

One last thing: in MQL5 the pointers aren't actual memory pointers, as they used in C++, so you should not pass them to DLL as input parameters.

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

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.

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.

How to create an indicator of non-standard charts for MetaTrader Market How to create an indicator of non-standard charts for MetaTrader Market

Through offline charts, programming in MQL4, and reasonable willingness, you can get a variety of chart types: "Point & Figure", "Renko", "Kagi", "Range bars", equivolume charts, etc. In this article, we will show how this can be achieved without using DLL, and therefore such "two-for-one" indicators can be published and purchased from the Market.