MQL5: How to Create and Use Class Objects?

Introduction to Classes in MQL5

What are Classes and Objects?

In MQL5, a class serves as a blueprint for creating objects. Think of a class as a template that defines the characteristics (data members or attributes) and behaviors (member functions or methods) that objects of that class will possess. An object is a specific instance of a class, a concrete entity created based on the class definition.

Benefits of Using Classes in MQL5

Object-oriented programming (OOP) with classes provides several advantages in MQL5 development:

  • Modularity: Classes break down complex tasks into smaller, manageable units.
  • Reusability: Classes can be reused across multiple projects.
  • Maintainability: Changes within a class typically don’t affect other parts of the code.
  • Abstraction: Hiding internal complexities and exposing only essential functionalities.
  • Encapsulation: Bundling data and methods that operate on that data within a single unit, protecting data from accidental modification.

Basic Syntax for Class Declaration

The basic syntax for declaring a class in MQL5 is as follows:

class MyClass
{
   // Class members (variables and functions)
   private:
      int myInteger;

   public:
      void MyMethod() {
         // Method implementation
      }
};

Creating Class Objects in MQL5

Declaring Class Objects

To create an object of a class, you first declare a variable of the class type, similar to declaring variables of built-in types (e.g., int, double).

MyClass myObject;

Instantiation of Class Objects using ‘new’

In MQL5, objects are typically created dynamically using the new operator. This allocates memory for the object on the heap. It is crucial to delete them later using delete operator. The advantage of allocating the objects on the heap is to avoid the stack overflow, especially in the long running programs such as Expert Advisors and Services.

MyClass *myObject = new MyClass();

Initializing Class Objects with Constructors

Constructors are special member functions that are automatically called when an object is created. They are used to initialize the object’s data members.

class MyClass
{
public:
   int value;

   // Default constructor
   MyClass() : value(0) {}

   // Parameterized constructor
   MyClass(int initialValue) : value(initialValue) {}
};

// Usage:
MyClass *obj1 = new MyClass();        // Uses the default constructor
MyClass *obj2 = new MyClass(10);      // Uses the parameterized constructor

Object Lifespan and Garbage Collection

Unlike some other programming languages, MQL5 doesn’t have automatic garbage collection. This means you are responsible for releasing the memory allocated to class objects when they are no longer needed using the delete operator. Failure to do so can lead to memory leaks, which can degrade performance over time, especially in long-running EAs. If you use new you must use delete.

MyClass *myObject = new MyClass();
// ... use the object
delete myObject;
myObject = NULL; // Optional, but good practice

Accessing Class Members

Public, Private and Protected Members

  • Public: Accessible from anywhere.
  • Private: Accessible only from within the class itself.
  • Protected: Accessible from within the class and its derived classes (inheritance).
class MyClass
{
private:
   int privateValue;

protected:
   int protectedValue;

public:
   int publicValue;

   void SetPrivateValue(int value) {
      privateValue = value; // Accessing private member from within the class
   }

   int GetPrivateValue() const { return privateValue; }
};

Using the Dot Operator to Access Members

To access public members of an object, you use the dot operator (.) when working with object variables, or the arrow operator (->) when working with object pointers.

MyClass *myObject = new MyClass();
myObject->publicValue = 10;  // Accessing a public member using the arrow operator
int value = myObject->publicValue; // Accessing a public member using the arrow operator

//... delete myObject;

Accessing Members from Within the Class

Within a class’s member functions, you can directly access all members (public, private, and protected) using their names.

class MyClass
{
private:
   int myValue;

public:
   void SetValue(int value) {
      myValue = value; // Accessing private member from within the class
   }

   int GetValue() const { return myValue; }
};

Advanced Class Object Usage

Arrays of Class Objects

You can create arrays of class objects, similar to arrays of built-in types.

MyClass *myObjects[10];
for(int i = 0; i < 10; i++) {
    myObjects[i] = new MyClass(i); // Initialize each object
}

//... Use the objects

for(int i = 0; i < 10; i++) {
   delete myObjects[i];
}

Pointers to Class Objects

Pointers to class objects are frequently used, especially when dealing with dynamic memory allocation and polymorphism.

MyClass *myObject = new MyClass();
MyClass *anotherPointer = myObject; // anotherPointer now points to the same object
//... use the object via pointers
delete myObject; // Only delete once!

Passing Class Objects as Function Parameters

Class objects can be passed as parameters to functions, either by value or by reference.

void MyFunction(MyClass *obj) // Pass by pointer
{
   // Modify the object
   obj->publicValue = 20;
}

MyClass *myObject = new MyClass();
MyFunction(myObject);
//...delete myObject;

Returning Class Objects from Functions

Functions can also return class objects, typically by pointer to avoid unnecessary copying. However, the object must outlive the function call, so be mindful of ownership and memory management.

MyClass* CreateMyClass() {
   MyClass* obj = new MyClass();
   return obj;
}

MyClass* myObject = CreateMyClass();
//... use the object
delete myObject;

Practical Examples and Use Cases

Creating a Simple Trade Management Class

class TradeManager
{
private:
   double  stopLossPips;
   double  takeProfitPips;
   double  lotSize;

public:
   TradeManager(double sl, double tp, double lot) : stopLossPips(sl), takeProfitPips(tp), lotSize(lot) {}

   bool OpenTrade(string symbol, int tradeType) {
      // Implementation to open a trade with given parameters
      double price = SymbolInfoDouble(symbol, tradeType == OP_BUY ? SYMBOL_ASK : SYMBOL_BID);
      double stopLoss = tradeType == OP_BUY ? price - stopLossPips * Point() : price + stopLossPips * Point();
      double takeProfit = tradeType == OP_BUY ? price + takeProfitPips * Point() : price - takeProfitPips * Point();

      MqlTradeRequest request = {0};
      MqlTradeResult  result  = {0};

      request.action   = TRADE_ACTION_DEAL;
      request.symbol   = symbol;
      request.volume   = lotSize;
      request.type     = tradeType;
      request.price    = price;
      request.sl       = stopLoss;
      request.tp       = takeProfit;
      request.magic    = 123456; // Magic number for identifying trades
      request.type_filling = ORDER_FILLING_FOK;
      request.type_time = ORDER_TIME_GTC;

      bool success = OrderSend(request, result);
      return success;
   }
};

// Example usage in an Expert Advisor:
TradeManager *tradeMgr = new TradeManager(50, 100, 0.01);
if(tradeMgr->OpenTrade(Symbol(), OP_BUY)) {
   Print("Trade opened successfully");
}
//... some code
delete tradeMgr;

Implementing a Custom Indicator Using Classes

Classes can encapsulate the logic for custom indicators, making the code more organized and reusable.

Developing an Expert Advisor with Class-Based Structure

Structuring an EA with classes promotes modularity and maintainability, especially for complex trading strategies. Separate classes can handle different aspects of the EA, such as trade management, risk management, and signal generation.


Leave a Reply