From Basic to Intermediate: Objects (I) - MQL5 Articles

Introduction
In the previous article, "From Beginner to Expert: Indicators (V)", we explored how to adapt an indicator, at least partially, to MetaTrader 5's chart display modes. However, we didn't delve into using all three chart modes, as that requires knowledge not yet covered in detail.
We can readily use candlestick or bar chart modes within a single indicator. Yet, employing the linear mode currently necessitates creating a separate indicator and manually attaching it to the chart. Despite this minor inconvenience, we will demonstrate how to circumvent it in the future.
I hope you've recognized how we can temporarily work around existing limitations until our articles reach a suitable level for explaining operations without deep dives into every implementation detail.
Nevertheless, we can now begin discussing certain topics that might appeal to different readers. I say this because many might not immediately grasp the profound significance of what we'll cover today. However, for others, the concepts presented and explained in this and subsequent articles hold genuine interest and could even serve a noble purpose.
Let's start with something not fully explored in this article, but which will serve as our discussion's starting point.
Special Precautions
Many traders enjoy seeing various data types displayed on their charts. Some are more or less useful depending on the specific moment.
One such data point is the bar or candle counter. For many, such information is completely useless, while for others, it's a quick and efficient way to exchange market insights with fellow traders.
So, the above pertains to what we, as users, place on the chart. From a programmer's perspective, however, the situation is slightly different.
This is because we can create various appealing ways to present information on the chart. However, this modest example doesn't offer many different approaches, as its primary goal is simply to display text—nothing more. The real distinction lies not in the text itself, but in how we organize it into a tabular format and update it periodically.
Many hobbyist programmers lack the clear vision inherent to truly dedicated programmers who actively study the field. And please, don't misunderstand me: I'm not suggesting that someone who occasionally writes programs for specific purposes doesn't know what they're doing. The issue is that such individuals aren't always aware of certain problems that can arise from poorly optimized programs when interacting with the MetaTrader 5 platform, causing a gradual and progressive decline in performance to the point where the platform becomes completely unusable due to all these concurrently running applications on the chart.
A simple candle counter, designed only to display text on the chart, can consume far more resources than necessary if poorly optimized. Even here, where the primary objective will always be educational, we strive to present code that won't significantly negatively impact MetaTrader 5's performance. And if that inadvertently happens, I remind you to exercise caution so that those who apply the concepts from these articles can avoid unnecessary problems.
Dear readers, let's return to our question: what could we devise as a candle counter? One might consider an application that displays numerical values on a scale.
But the question remains open. What application would you use?
Since we've already covered creating scripts and indicators, you'd likely find yourself somewhere between these two options. However, due to certain limitations of scripts, we'll ultimately have to lean towards indicators.
And what exactly are the limitations of scripts? If you've experimented already, you've probably noticed that every time we add a script to a chart, it's removed when the chart's timeframe changes.
This occurs even if we use a loop within the script to prevent it. Thus, we would need to implement measures to adjust the script's interaction with the chart after a timeframe change.
There's a way to avoid the issues associated with running scripts. However, since it involves using services, and their connection to the chart is quite complex, this option isn't suitable for us.
If you're curious about the complexity of such a connection structure, look for my articles on building a replication/simulation system. But because those articles are for people with a solid foundational understanding of MQL5 programming — which is not the case for this topic, as we are just starting — I won't delve into chart manipulation via services for this purpose here.
Therefore, the remaining alternative is to use indicators. True, but nevertheless, indicators are often unsuitable for certain types of tasks.
This is because indicators in MetaTrader 5 execute as a single block, as if they were one application running on the chart. This is done to speed up the calculations MetaTrader 5 performs to provide us with specific values.
These values are part of a set of input parameters that can be selected when placing the indicator on the chart, a topic already covered in previous articles.
However, given that a bar counter only operates when a new bar appears on the chart, we might be tempted to use a timer for synchronization. And this is where problems begin.
Using a timer within an indicator is generally discouraged. This is because if the timer's code crashes, all other indicators will be affected, and it could even freeze MetaTrader 5 for several moments — moments that could be critical.
But there are methods for interacting with the system, whose purpose is to detect the appearance of new bars on the chart. Utilizing an indicator is, essentially, the simplest and most practical way to know when a new candle has emerged, enabling us to count candles and then display text.
To reach the point where candle counting begins, we need a starting point. And we'll begin with the code you see below.
101. //+------------------------------------------------------------------+
202. #property copyright "Daniel Jose"
303. //+------------------------------------------------------------------+
404. int OnInit
505. {
606. return INIT_SUCCEEDED;
707. };
808. //+------------------------------------------------------------------+
909. int OnCalculate(const int rates_total, const int prev_calculated, const datetime &Time[], const double &Open[], const double &High[], const double &Low[], const double &Close[], const long &TickVolume[], const long &Volume[], const int &Spread[])
1010. {
1111. return rates_total;
1212. };
1313. //+------------------------------------------------------------------+Code 01
Now, pay attention: each time the price of the symbol displayed on the chart changes, all indicators receive a Calculate event from MetaTrader 5. In MetaTrader 5, the Calculate event is not triggered based on activity over a specific time period.
If you're using a 10-minute chart, when each new bar appears approximately every 10 minutes, the Calculate event won't occur unless the price has actually changed. Until then, the Calculate event will not be triggered.
Considering that with highly volatile symbols, Calculate events can occur at very short intervals—on the order of milliseconds—we need this Calculate event to execute as quickly as possible. In other words, we shouldn't waste time calculating or searching for information unnecessarily, as this would significantly slow down event processing.
This negatively impacts all other applications associated with that particular chart. And remember, we haven't even touched on Expert Advisors yet.
Yes, poorly optimized indicators severely harm MetaTrader 5. But things will improve soon.
For now, you need to understand that execution speed depends on how we retrieve information. In Code 01, you see that we have an array designed to provide us with the timestamp for each bar.
Note that here, in the Calculate event, we don't know the exact moment the quote changed. We only know when that bar was created.
And, based on the timeframe displayed on the chart, we can estimate how much time remains until the bar closes. This information is crucial for creating another type of application, which we will examine later.
But here, our goal is to create a candle counter, or at least to know if a new bar has appeared on the chart. Therefore, whenever the value of the Time array, shown on line 09, changes, it signifies that a new bar has appeared on the chart.
Another method, which I find much simpler, is to observe the values of prev_calculated and rates_total. But why?
The reason is that the value in the Time array is of datetime type. In contrast, the prev_calculated and rates_total values are provided by the MetaTrader 5 platform itself and are of integer type, making the check much simpler and faster.
And since changes only occur when new bars appear on the chart, everything becomes much easier to monitor.
Apart from another issue we'll address shortly, do you understand where I'm going with this? It's not about how we implement something.
It's about understanding what information we have available to achieve our goal. To test this, we'll modify Code 01 into Code 02, as you can see below.
101. //+------------------------------------------------------------------+
202. #property copyright "Daniel Jose"
303. //+------------------------------------------------------------------+
404. int OnInit
505. {
606. return INIT_SUCCEEDED;
707. };
808. //+------------------------------------------------------------------+
909. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
1010. {
1111. return rates_total;
1212. };
1313. //+------------------------------------------------------------------+Code 02
Now we have a starting point for understanding that everything hinges on correctly grasping the situation and leveraging the available information. There isn't a single right (or wrong) way to do something; there are only simpler ways to achieve the same goal and outcome.
Okay, but how do we determine whether a new bar has appeared on the chart or not? This is the easiest part, dear readers.
To do this, we'll modify Code 02 as shown below.
101. //+------------------------------------------------------------------+
202. #property copyright "Daniel Jose"
303. //+------------------------------------------------------------------+
404. int OnInit
505. {
606. return INIT_SUCCEEDED;
707. };
808. //+------------------------------------------------------------------+
909. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
1010. {
1111. if ((prev_calculated > 0) && (prev_calculated != rates_total))
1212. Print("New Bar...");
1313.
1414. return rates_total;
1515. };
1616. //+------------------------------------------------------------------+Code 03
Code 03 precisely demonstrates what we intended to show and what was just discussed. In other words, here we have a very simple way to ascertain when the current bar has closed and when a new one has appeared. To confirm this, let's look at the animation below.

Animation 01
Since Code 03 is very straightforward, as is the animation, we can skip any further explanation and move on to the most interesting part of the article. But for that, we need to begin a new topic.
Objects and the Chart
Contrary to popular belief, everything present on the chart is an object. Of course, these objects can serve vastly different purposes depending on what we're trying to create, but that doesn't change the fact that they are still objects.
And another point: the objects we're discussing have nothing to do with Object-Oriented Programming (OOP). Now we're approaching the complex part.
There are two types of objects, or rather, two ways to place objects on the quote chart. This is how MetaTrader 5 charts are organized.
The first type consists of objects whose coordinate system is based on the chart's coordinates, while the second type uses screen coordinates. There are differences between these objects both in how they are displayed on the screen and how we interact with them.
However, a certain relationship exists among some types. Knowing how to choose the right type greatly simplifies implementation and, consequently, accelerates code execution.
Since this is our first encounter with something like this, let's briefly step away from the candle counter, because first, we need to understand how to work with these objects.
As a starting point, it's advisable to consult the MQL5 documentation, specifically the "Object Types" section. There, you'll find a list of various objects.
While it might seem that there aren't enough objects and some are missing, this isn't the case. In my opinion, MQL5 provides us with more than enough objects to use on the chart.
Frankly, we won't need all these elements. But if they're already implemented and available to us, I don't see any problem with using them effectively.
Furthermore, many of them can be employed in very interesting ways, saving us a lot of time.
To demonstrate this, we'll write a small piece of code to show how to work with objects on the chart. This code is shown below.
101. //+------------------------------------------------------------------+
202. #property copyright "Daniel Jose"
303. //+------------------------------------------------------------------+
404. #define def_NameChannel "Demo"
505. //+------------------------------------------------------------------+
606. int OnInit
707. {
808. ObjectCreate(0, def_NameChannel, OBJ_REGRESSION, 0, 0, 0);
909.
1010. return INIT_SUCCEEDED;
1111. };
1212. //+------------------------------------------------------------------+
1313. int OnCalculate(const int rates_total, const int prev_calculated, const datetime &Time[], const double &Open[], const double &High[], const double &Low[], const double &Close[], const long &TickVolume[], const long &Volume[], const int &Spread[])
1414. {
1515. const int n = 20,
1616. a = 2;
1717.
1818. if ((prev_calculated > (n + a)) && (prev_calculated != rates_total))
1919. {
2020. ObjectMove(0, def_NameChannel, 0, Time[rates_total - (n + a)], Close[rates_total - (n + a)]);
2121. ObjectMove(0, def_NameChannel, 1, Time[rates_total - a], Close[rates_total - a]);
2222. }
2323.
2424. return rates_total;
2525. };
2626. //+------------------------------------------------------------------+
2727. void OnDeinit(const int reason)
2828. {
2929. ObjectDelete(0, def_NameChannel);
3030. ChartRedraw;
3131. };
3232. //+------------------------------------------------------------------+Code 04
Code 04 demonstrates what our first object will look like within an indicator. Looking at it, you might think, "Well, that's not an indicator because it doesn't contain the structures we covered in previous articles." Yes, dear reader, Code 04 is indeed an indicator. You can verify this by looking at the OnCalculate function, which is responsible for handling the Calculate event generated by MetaTrader 5.
The thing is, the indicator shown in Code 04 is not a typical indicator. It has a much more specific purpose and aims to perform a very particular task. Before we examine how Code 04 works, let's see it in action on the chart, as shown in the animation below.

Animation 02
This needs to be seen in an animation because a static image wouldn't suffice to understand what we're about to witness. But before we start scrutinizing the code, let's understand how Code 04 operates.
On line 04, we have a definition that allows us to assign a name to the object we're creating. And yes, everything placed on the chart must be assigned a name.
It's time to get used to this now.
So, on line 08, we simply attempt to create an object. In this case, it's a linear regression channel.
Since we don't know where this channel will be displayed, we don't do anything else. Now, what happens is that the indicator will wait until MetaTrader 5 initiates a Calculate event with a specific characteristic.
The required condition is defined on line 18. Notice that line 18 of Code 04 is very similar to line 11 of Code 03, and it serves the same purpose: to wait for a new bar to appear.
Until this happens, the object we created on line 08 will not be displayed on the chart.
Once a new bar appears, we use lines 20 and 21 to create the linear regression channel. Notice how this is done.
We'll discuss this in more detail a bit later. For now, it's sufficient to note that there's nothing mysterious about it.
Line 20 indicates where the channel's drawing begins, and line 21 indicates where it ends. Thus, we get a channel that will follow the price with a regression range of 20 bars and ignore the current bar.
Whenever you create an object and add it to the chart via an application, good practice dictates that your application should remove this object from the chart. We achieve this using line 29 within the Deinit event handler.
But now the interesting part begins. What if we change how the linear regression is analyzed and its results are displayed?
How would the indicator behave? To find out, we'll completely modify Code 04, as shown below.
101. //+------------------------------------------------------------------+
202. #property copyright "Daniel Jose"
303. //+------------------------------------------------------------------+
404. #define def_NameChannel "Demo"
505. //+------------------------------------------------------------------+
606. int OnInit
707. {
808. ObjectCreate(0, def_NameChannel, OBJ_REGRESSION, 0, 0, 0);
909.
1010. return INIT_SUCCEEDED;
1111. };
1212. //+------------------------------------------------------------------+
1313. int OnCalculate(const int rates_total, const int prev_calculated, const datetime &Time[], const double &Open[], const double &High[], const double &Low[], const double &Close[], const long &TickVolume[], const long &Volume[], const int &Spread[])
1414. {
1515. const int n = 20,
1616. a = 1;
1717.
1818. if (prev_calculated > (n + a))
1919. {
2020. ObjectMove(0, def_NameChannel, 0, Time[rates_total - (n + a)], Close[rates_total - (n + a)]);
2121. ObjectMove(0, def_NameChannel, 1, Time[rates_total - a], Close[rates_total - a]);
2222. }
2323.
2424. return rates_total;
2525. };
2626. //+------------------------------------------------------------------+
2727. void OnDeinit(const int reason)
2828. {
2929. ObjectDelete(0, def_NameChannel);
3030. ChartRedraw;
3131. };
3232. //+------------------------------------------------------------------+Code 05
Notice that in Code 05, we only modified the OnCalculate event handler compared to Code 04. But the result is, at the very least, quite interesting. You can see this for yourself in the animation below.

Animation 03
What we see in Animation 03 is simply astonishing, especially considering that the regression is built in real-time. In other words, with each new price, the linear regression channel changes.
And depending on the configuration used, you can obtain a truly sensitive trend indicator, even more sensitive than an exponential moving average. But I'll leave that to today's traders, who are always eager to find a way to create a self-adjusting linear regression channel.
" I would reply, I don't know. There's no denying it's quite interesting and fun.
However, anyone with a more deliberate concept has already figured out how to use this for other purposes since all that needs to be done is to change the type of object created and promptly specify the anchor points. From time to time, the objects will automatically adjust, without the need to remember this, because the simple fact of being on the chart will be enough to execute the rules we have created.
Let's look at one more example. In this instance, we'll use a different type of object.
Remember, everything we'll show is purely for educational purposes. The code is displayed just below.
101. //+------------------------------------------------------------------+
202. #property copyright "Daniel Jose"
303. //+------------------------------------------------------------------+
404. #define def_NameChannel "Demo"
505. //+------------------------------------------------------------------+
606. int OnInit
707. {
808. ObjectCreate(0, def_NameChannel, OBJ_TREND, 0, 0, 0);
909. ObjectSetInteger(0, def_NameChannel, OBJPROP_RAY_RIGHT, true);
1010.
1111. return INIT_SUCCEEDED;
1212. };
1313. //+------------------------------------------------------------------+
1414. int OnCalculate(const int rates_total, const int prev_calculated, const datetime &Time[], const double &Open[], const double &High[], const double &Low[], const double &Close[], const long &TickVolume[], const long &Volume[], const int &Spread[])
1515. {
1616. const int n = 60;
1717. double p = DBL_MIN;
1818. int t = 0;
1919.
2020. for (int c = rates_total - (n + 1); c < rates_total; c++)
2121. p = (High[c] > p ? High[t = c] : p);
2222. ObjectMove(0, def_NameChannel, 0, Time[t], p);
2323. p = DBL_MIN;
2424. for (int c = t + 1; c < rates_total; c++)
2525. p = ((High[c] > p) && (Open[c] < Close[c]) ? High[t = c] : p);
2626. ObjectMove(0, def_NameChannel, 1, Time[t], p);
2727.
2828. return rates_total;
2929. };
3030. //+------------------------------------------------------------------+
3131. void OnDeinit(const int reason)
3232. {
3333. ObjectDelete(0, def_NameChannel);
3434. ChartRedraw;
3535. };
3636. //+------------------------------------------------------------------+Code 06
When Code 06 is executed, the result is what's shown below.

Animation 04
Don't get too carried away by what you see, dear readers, because this is just a demonstration. This should by no means be considered a finished application.
But in Animation 04, we see the appearance of a downtrend line drawn according to the criteria embedded in Code 06. " Simply put: I just told the code how to search for a downtrend line according to my criteria.
Now, let's understand what happened. On line 08, just like in Code 03, we specify the type of object we want to place on the chart.
Then, on line 09, we modify one of the object's properties. If this property weren't defined as shown here in Code 06, the result would be as in the image below.

Image 01
Notice that now, looking at Image 01, we see where the trendline was drawn from and to. In this case, we're looking for a downtrend line.
Uptrend lines are defined using other criteria. But since the objective here is not to show how to automatically identify and create trendlines, the criteria and methods we use do indeed work.
Alright, but then how did the indicator manage to find these points to draw the trendline? It seems like magic.
No, dear reader, it is definitely not magic. It's just the application of a basic criterion.
Pay attention to the Calculate event handler. Notice that here we have two loops: one to determine the first point for drawing the trendline, and another to determine the second point needed to draw the trendline.
When the first point is found, we use line 22 to tell the object where the first anchor point will be. Immediately after finding the second point, we use line 26 to specify the location of the second anchor point.
The rest is handled by MetaTrader 5 itself.
However, despite this, I want to emphasize that YOU SHOULD NOT CONSIDER this indicator to be something worthy of praise. It was created so that we could detect what is shown in Image 01 and Animation 04. You are unlikely to find other details as perfect as those shown above.
"Okay, I get it. But can we change other properties to create more personalized objects?" Yes, that's possible, dear readers. And another point: unlike what happens when trying to modify properties directly on the chart, some of these objects have properties that can only be changed via code.
But let's look at a simple example of changing properties. You can see it below:
101. //+------------------------------------------------------------------+
202. #property copyright "Daniel Jose"
303. //+------------------------------------------------------------------+
404. #define def_NameChannel "Demo"
505. //+------------------------------------------------------------------+
606. int OnInit
707. {
808. ObjectCreate(0, def_NameChannel, OBJ_TREND, 0, 0, 0);
909. ObjectSetInteger(0, def_NameChannel, OBJPROP_COLOR, clrLawnGreen);
1010. ObjectSetInteger(0, def_NameChannel, OBJPROP_WIDTH, 4);
1111.
1212. return INIT_SUCCEEDED;
1313. };
1414. //+------------------------------------------------------------------+
15 .
16 .
17 .Code 07
In this case, in Code 07, we only see the fragment that needed to be changed compared to Code 06. But notice that on line 09, we specify which color we want to use for the object, and on line 10, we indicate what line thickness should be used. The result of running this code is what we see in the image below.

Image 02
So listen carefully, dear readers. In the current version of the code, it is IMPOSSIBLE to change the color or line thickness using the keyboard shortcut to access the indicator's information, as shown in the image below.

Image 03
To modify the properties of these objects, we would need to use a different dialog box. In other words, in this scenario, we would have to use what's shown in the image below.

Image 04
I understand this seems overly complicated, but this is the classic way MetaTrader 5 is typically used. However, there are ways to circumvent such situations, allowing the user to modify object properties based on a configuration established directly within the indicator.
But let's not rush, as this is just our initial encounter with objects. In that case, I believe you'll already have ample opportunities to experiment, so you'll be ready for the next article.
Concluding Remarks
In today's article, we began exploring how to interact with objects directly on the chart using specially designed code meant to illustrate the process. While this initial contact might have been somewhat superficial, it has already demonstrated that, with careful consideration of the goal and proper task planning, many interesting things can be accomplished.
For this, you'll need practice and a thorough understanding of how each object provided by MQL5 works and how they can be utilized within MetaTrader 5. Without a true grasp of the available possibilities, you won't be able to bring any of your ideas to fruition.
As always, you'll find the complete code attached to this article series. However, especially in the context of this particular article, I must warn you that all provided code is intended for studying and practicing the concepts explained. Please do not use the code presented here as ready-made applications or for purposes unrelated to learning the material, as doing so could severely harm your wallet.
Translated from Portuguese by MetaQuotes Ltd.
Original article:
Attached Files |
Download ZIP
Anexo.zip (2.82 KB)
Warning: All rights to these materials are reserved by MetaQuotes Ltd. Copying or reprinting these materials in whole or in part is prohibited.
This article is written by a website user and reflects their personal opinion. MetaQuotes Ltd is not responsible for the accuracy of the information provided, nor for any consequences arising from the use of the described solutions, strategies, or recommendations.
CODE X
- Brazil
- 22778
Other articles by this author
- From Beginner to Expert: Indicators (V)
- From Beginner to Expert: Inheritance
- From Beginner to Expert: Structures (VII)
- From Beginner to Expert: Structures (VI)
- From Beginner to Expert: Structures (V)
- From Beginner to Expert: Structures (IV)
Join the Discussion
Market Simulation (Part 22): Getting Started with SQL (V)
Before you give up and decide to abandon learning SQL, let me remind you, dear readers, that here we are still only using the most basic elements. We haven't even touched upon some of SQL's capabilities yet.
Once you understand them, you'll see that SQL is much more practical than it appears. Although we will likely end up changing the direction of what we are building, because the creation process is dynamic.
We will show a little more about creating different things in SQL because it is truly important and useful for you. Simply thinking that you are more capable than an entire community of programmers and developers will only lead to a waste of time and opportunities.
Don't worry, because things will get even more interesting.
Analysis of temporal price gaps in MQL5 (Part I): Building a base indicator
Analyzing temporal gaps helps traders identify potential market reversal points. The article discusses what a temporal gap is, how to interpret it, and how it can be used to detect large volume inflows into the market.
Features of Expert Advisors
Creating Expert Advisors in the MetaTrader trading system has a number of features.
Dolphin Echolocation Algorithm (DEA)
In this article, we will delve deeper into the DEA algorithm, a metaheuristic optimization method inspired by the unique ability of dolphins to locate prey using echolocation. From its mathematical foundations to its practical implementation in MQL5, from analysis to comparison with classical algorithms, we will thoroughly explore why this relatively new method deserves a place in the arsenal of researchers facing optimization challenges.
\
Build Smarter with MQL5 Algo Forge\
\
Use Git Tools in MetaEditor for Structured and Reliable Development\
\
Learn More
This website uses cookies. Learn more about our Cookie Policy.
You are missing out on trading opportunities:
- Free trading applications
- Over 8,000 signals to copy
- Financial news to study financial markets
RegisterLog in
Latin characters without spaces
Password will be sent to this email address
An error occurred
- Log in with Google
You agree to the site's policy and terms of use
If you don't have an account, please register
Allow cookies to log in to the site.
Enable the necessary setting in your browser, otherwise you will not be able to log in.
Forgot login/password?
- Log in with Google
