Русский Español Português
preview
From Basic to Intermediate: Template and Typename (III)

From Basic to Intermediate: Template and Typename (III)

MetaTrader 5Examples |
212 2
CODE X
CODE X

Introduction

The materials provided here are for educational purposes only. It should not be considered in any way as a final application. Its purpose is not to explore the concepts presented.

In the previous article: “From Basic to Intermediate: Template and Typename (II)“, we explained how to handle some specific everyday situations for programmers. Whether it is your temporary hobby or you are a professional programmer, using function and procedure templates can be quite useful at certain times. Although this is not very common in MQL5 and is not always applicable, it is useful to know that such a concept is available for application and that it has its own points one should understand correctly so as not to get confused when trying to modify the code that eventually uses such simulation.

Templates do not just apply to functions and procedures. In fact, they have a wide range of practical applications, which more or less depends on the type of application you want to develop. It is worth mentioning - and emphasizing this once again - that we can implement the same kind of application without using templates. However, using such a tool and resource from MQL5 makes the implementation stage easier and more enjoyable. As well, it also helps avoid some types of complex and annoying detection failures.


Patterns in local variables

In the previous two articles, where we talked only about templates in functions or procedures, everything was quite simple and even interesting. But thanks to the explanations, it became clear that function or procedure templates are a good way to delegate work to the compiler, to the extent that overload of the function or procedure occurs. However, the examples provided were very simple, since the type of data used was specified only in connection with the input parameters of the function or procedure. Although one can elevate to a rather higher level of knowledge.

To understand this let us start with a simple example: calculating averages. Yes, even though it is a simple thing, we can see how to apply templates in a similar situation. But I would like to remind you that the codes provided here are intended solely for educational purposes. They do not necessarily represent code that will be used in the real world.

Since I think you already understand how overloading a function or procedure works, we can focus only on the parts that really deserve attention by removing everything unnecessary from the code. Thus, we have the first-hand code presented below.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define PrintX(X) Print(#X, " :: ", X)
05. //+------------------------------------------------------------------+
06. void OnStart(void)
07. {
08.     const double d_values[] = {2.5, 3.4, 1.25, 2.48, .85, 3.75};
09.     const long i_values[] = {8, 3, 4, 6, 4, 5, 7};
10.     
11.     PrintX(Averange(d_values));
12.     PrintX(Averange(i_values));
13. }
14. //+------------------------------------------------------------------+
15. double Averange(const double &arg[])
16. {
17.     double local = 0;
18. 
19.     for (uint c = 0; c < arg.Size(); c++)
20.         local += arg[c];
21. 
22.     return local / arg.Size();
23. }
24. //+------------------------------------------------------------------+
25. long Averange(const long &arg[])
26. {
27.     long local = 0;
28. 
29.     for (uint c = 0; c < arg.Size(); c++)
30.         local += arg[c];
31. 
32.     return local / arg.Size();
33. }
34. //+------------------------------------------------------------------+

Code 01

We have a simple code in front of us, the purpose of which is to calculate the average value of a data set. For those who do not understand, the average calculation is to add up all the values and then divide the result of a given sum by the number of elements present in the calculation. Although this type of calculation is simple, there is a small problem: values can be both positive and negative. We may get a distorted mean value that does not reflect what we really want to know. But this is an insignificant thing that is not important here. We only mentioned this so that you know that the code does not always supply to us the information we need, even if it was created by someone else. In addition, the fact that another programmer created a certain code makes the code respond to what that programmer was searching, rather than what we might be searching.

However, I do not think it is necessary to explain how code 01 runs, since it uses overloading to make factorization executed in the most appropriate way. You can see the result of this operation in the following image.

Figure 01

In other words, it is something trivial and simple. But I want you to take a close look at the function implemented in lines 15 and 25. What do they have in common? We can say that they are almost identical, except for one specific detail. It is this part that relates to the type of data used. In other respects they are the same, with no apparent differences. And, indeed, if you have noticed this, it means that you are thinking about something: we can convert these two functions shown in code 01 into a template. This will simplify the task. "However, I have doubts about this. Both functions have a local variable, which can be seen in lines 17 and 27. I know how to declare and implement a function call as a template, because I have seen it being done in previous articles. However, this local variable is bothering me, I do not know how to tackle it. Perhaps, I can do something similar to the code below."

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define PrintX(X) Print(#X, " :: ", X)
05. //+------------------------------------------------------------------+
06. void OnStart(void)
07. {
08.     const double d_values[] = {2.5, 3.4, 1.25, 2.48, .85, 3.75};
09.     const long i_values[] = {8, 3, 4, 6, 4, 5, 7};
10.     
11.     PrintX(Averange(d_values));
12.     PrintX(Averange(i_values));
13. }
14. //+------------------------------------------------------------------+
15. template <typename T>
16. T Averange(const T &arg[])
17. {
18.     double local = 0;
19. 
20.     for (uint c = 0; c < arg.Size(); c++)
21.         local += arg[c];
22. 
23.     return (T)(local / arg.Size());
24. }
25. //+------------------------------------------------------------------+

Code 02

In fact, you can do what is shown in code 02. However, when trying to compile this code, you will receive this warning from the compiler:

Figure 02

Please note that THIS IS NOT AN ERROR MESSAGE, but a warning. This happens because in line 21, where we sum the array values, there will be a moment when we sum a floating-point value with an integer value. And this can lead to distortion of the final result. However, the final result, when we execute code (02), will be the same as in image 01. However, this compiler warning may be a little disturbing to some programmers. How can I fix this issue? To solve the problem, we need to perform a type conversion, just like we do in line 23, only the conversion will be applied here in line 21. But be careful: you should not use what is shown below.

local += (T)arg[c];

This will NOT SOLVE THE ISSUE WITH THE WARNING, since we need to perform the conversion so that the value in the array is compatible with the variable value in line 18. Although it may seem complicated, it is actually very simple and straightforward. All we need to do is change line 21 of code 02 to the line you see just below.

local += (double)arg[c];

Now we have solved our problem. We have an Average function implemented as a template, and it works great. However, there is another way to fix these same problems, so as not to change the code so much. Let us return to code 01. Please note that code 02 represents what is implemented in code 01. If the problems are due to a local variable, as shown above, why not do something more complicated? Thus, the compiler will take care of such problems on its own. That sounds daring, doesn’t it?

But if you really understood how code 01 was converted to code 02 and how we handled the warning issued by the compiler, you should have thought of another solution. One of such solutions that allows you to change the type of the variable as declared in line 18 of code 02. That would be a really interesting solution, and that is exactly what we are seeing in the implementation process.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define PrintX(X) Print(#X, " :: ", X)
05. //+------------------------------------------------------------------+
06. void OnStart(void)
07. {
08.     const double d_values[] = {2.5, 3.4, 1.25, 2.48, .85, 3.75};
09.     const long i_values[] = {8, 3, 4, 6, 4, 5, 7};
10.     
11.     PrintX(Averange(d_values));
12.     PrintX(Averange(i_values));
13. }
14. //+------------------------------------------------------------------+
15. template <typename T>
16. T Averange(const T &arg[])
17. {
18.     T local = 0;
19. 
20.     for (uint c = 0; c < arg.Size(); c++)
21.         local += arg[c];
22. 
23.     return local / arg.Size();
24. }
25. //+------------------------------------------------------------------+

Code 03

Code 03 yields the same result as in image 01, and does not issue any warnings from the compiler. Why so? Because when the compiler notices that we want an integer type, it will adjust the type of the variable to match the integer type. If the compiler notices that we want a floating-point type, it will make the variable as declared in line 18 of the appropriate type for using with floating-point.

Thus, we will get a much simpler code than code 01, and although it is very similar to code 02, there is no need to make all the various adjustments. This ensures that type conversion occurs and compiler warnings will not be issued. It is great, isn't it? I think if you have already seen code similar to this one, you have probably imagined how it works. But if you understand the basic concepts and see how everything fits together, then the codes that earlier seemed complicated become simpler and more interesting.

Okay, that was the most enjoyable part. And now we will see a really interesting part. Given the use cases presented in code 03, I now want to experiment with other data types.

Since as of the moment we have only shown the data type used by the union, we can try using the same technique shown in code 03 to create an even larger template. However, in order to correctly divide tasks, we will discuss this in another thread.


Using elements in unions

First, we will look at what is shown in code 03 in a somewhat simpler form, at least at the beginning. Actually, we are not going to change code 03 again, but we are going to use this concept to create a dynamic memory area inside the union. In other words, we are going to create a template for creating a union between an array and any data type.

When we studied how a union is defined, we saw that it consists of a fixed size block. This block, defined in bytes, has the value of the number of bytes belonging to the largest type present in the union. We can make it so that this is not the absolute truth, that is, we can dynamically configure this union by the compiler, which will allow us to create a union that, principally, will have a dynamic block size, since the width in bytes will be determined at the time of compilation of our code. And from that moment it will have a fixed size.

Now you might be thinking, "This seems like a very difficult task to me. At first glance, I do not know how we will tell the compiler how to work with this approach or implementation. Do we need this right now?" Well, in a sense, some may consider this concept to be advanced, but in my opinion, it is still a basic concept that every beginner should know, as it greatly facilitates the creation of various types of code for broader purposes.

I know that many people may think that we are moving too fast. However, we go as slowly as possible, without missing a single concept or explanation. If this knowledge base is well formed and perceived, everything that follows will become much more natural and easier to understand. Although, since I know that future materials may be a bit confusing at first, we will start with simpler code. This will make learning new concepts more enjoyable and easier. Thinking in this way, let us start with the code below.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     const ulong info = 0xA1B2C3D4E5F6789A;
07. 
08.     PrintFormat("Before modification: 0x%I64X", info);
09.     PrintFormat("After modification : 0x%I64X", Swap(info));
10. }
11. //+------------------------------------------------------------------+
12. ulong Swap(ulong arg)
13. {
14.     union un_01
15.     {
16.         ulong   value;
17.         uchar   u8_bits[sizeof(ulong)];
18.     }info;
19. 
20.     info.value = arg;
21. 
22.     PrintFormat("The region is composed of %d bytes", sizeof(info));
23. 
24.     for (uchar i = 0, j = sizeof(info) - 1, tmp; i < j; i++, j--)
25.     {
26.         tmp = info.u8_bits[i];
27.         info.u8_bits[i] = info.u8_bits[j];
28.         info.u8_bits[j] = tmp;
29.     }
30. 
31.     return info.value;
32. }
33. //+------------------------------------------------------------------+

Code 04

This code does not use the template resource yet. Since this was already explained in another article when we introduced the topic of union, I cannot see the need to explain how it works again. However, when introducing code 04, we were not interested in expanding it. Therefore, the function from line 12 did not actually exist. Everything was done as part of OnStart. But now, due to the fact that we will talk about templates in unions, it is appropriate to separate everything into a separate block. This will facilitate the initial contact with union templates. Then we will see that it is possible to put everything in a single code chunk, as was done initially.

Once you compile and run code 04, you will see the response in MetaTrader 5 terminal.

Figure 03

In other words, we move the bits. Everything is simple. How do we change bits of other data types? Because if we have a type like ushort, which has 2 bytes, and we send this type to the function in line 12, we will get a wrong result, or at least something rather strange. To verify this, simply change line 06 from code 04 to the line shown below.

const ushort info = 0xCADA;

Now, when we run code 04 with this line shown above, the result will be what we see on the screen of MetaTrader 5 terminal.

Figure 04

Please note one thing in image 04: the area that I highlighted in red. This is unnecessary data added to ushort type value. Even if you notice that the rotation has actually occurred, these bytes are a problem. Especially if we need to save the ushort type. It is in this case that the template comes to the rescue.

As already shown in the previous topic, working with values of various types is very simple, practical and safe. This allows us to calculate the mean value. In fact, our application can work with both integer and floating-point values without any problems. But how do we do the same thing here, where we use and need a union to simplify the code? Well, that is the most interesting part.

First, we need to convert code 04 to a template-friendly code. Judging by what has been shown so far, it is very easy to do this, especially if you actually understand the concepts outlined. Then, without any problems, we create the code shown below.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     const ushort info = 0xCADA;
07. 
08.     PrintFormat("Before modification: 0x%I64X", info);
09.     PrintFormat("After modification : 0x%I64X", Swap(info));
10. }
11. //+------------------------------------------------------------------+
12. template <typename T>
13. T Swap(T arg)
14. {
15.     union un_01
16.     {
17.         ulong   value;
18.         uchar   u8_bits[sizeof(ulong)];
19.     }info;
20. 
21.     info.value = arg;
22. 
23.     PrintFormat("The region is composed of %d bytes", sizeof(info));
24. 
25.     for (uchar i = 0, j = sizeof(info) - 1, tmp; i < j; i++, j--)
26.     {
27.         tmp = info.u8_bits[i];
28.         info.u8_bits[i] = info.u8_bits[j];
29.         info.u8_bits[j] = tmp;
30.     }
31. 
32.     return info.value;
33. }
34. //+------------------------------------------------------------------+

Code 05

However, when trying to compile code 05, the compiler issues a warning. This warning is in the image below.

Figure 05

Now think with me. You see this warning that says that the value in line 32 is not compatible with the returned value because they are of different types. Where have you seen this before? If you go back a bit in the same article, you will see something similar in image 02. Based on this, you already have the means to solve this very problem. You can go either way. However, as you should have noticed in the previous thread, the best way that forces us to make fewer changes to the code is the one that converts a variable of a wrong type to the expected type as a return. In other words, you DO NOT have to change line 32, but rather line 15, where the union of values is declared.

Guess why don't I say that we should amend line 17? After all, that is where the variable is, which is used in line 32 to return the value. The reason is that we are not going to change only line 17, we also want to change the array in line 18. And if we change only line 17, we will fix part of the problem. However, the problem will be moving from one location to another, since we will not be changing the width of the union. Therefore, it is important to pay attention to exactly what is being programmed. If we take due care, there will be no problems, and our code will become more useful, covering more cases with much less risk and effort.

Now that we have reviewed necessary steps, we can change code 05 as shown below.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     const ushort info = 0xCADA;
07. 
08.     PrintFormat("Before modification: 0x%I64X", info);
09.     PrintFormat("After modification : 0x%I64X", Swap(info));
10. }
11. //+------------------------------------------------------------------+
12. template <typename T>
13. T Swap(T arg)
14. {
15.     union un_01
16.     {
17.         T     value;
18.         uchar u8_bits[sizeof(T)];  
19.     }info;
20. 
21.     info.value = arg;
22. 
23.     PrintFormat("The region is composed of %d bytes", sizeof(info));
24. 
25.     for (uchar i = 0, j = sizeof(info) - 1, tmp; i < j; i++, j--)
26.     {
27.         tmp = info.u8_bits[i];
28.         info.u8_bits[i] = info.u8_bits[j];
29.         info.u8_bits[j] = tmp;
30.     }
31. 
32.     return info.value;
33. }
34. //+------------------------------------------------------------------+

Code 06

By executing code 06, which is just a modification of code 05, we will get the result shown in the image below.

Figure 06

Simply perfect. Now we have the correct answer. Besides, we have converted code 04 into a much more advanced code. Now that the compiler can create and generate overloaded functions or procedures for us, we can use any data types without fear. Based on the template of code 06, the compiler will create appropriate procedures for the correct operation of the routine, always producing results that match the expected ones.

Because we have already created a much more interesting model, you might think, "Well, that's it. Now I know how to create union templates." That's right, you already know how to do this in a very simple case. However, what we have shown here IS NOT A UNION TEMPLATE. It is only about the suitability of the union for using the template in a function or procedure. To achieve what would become a union template, the question, as some say, is a little deeper. But in order to properly separate questions, we will look at how to do this in another, new thread. Thus, we do not risk confusing one with the other.


Definition of union template

Now let us go a little deeper into the matter of how to work with templates. Unlike what we have seen up to this point, when defining a template for the union, like for other types that we will discuss later, we want to make some decisions that novice programmers are concerned about, namely how the code should be implemented. This is due to the fact that a part of the code that we will create may look, say, exotic.

If it seems to you that the code is getting too complicated, please calm down and take a step back. Ideally, we should strive to study and practice what has been shown so far until we gain a proper understanding of principles and concepts. Only then go back and start from the place where you stopped before the pause.

Do not try to get ahead of yourself and do not think that you have figured something out just because you have seen code that uses a specific resource, because everything does not work the way it should. Get this concept right, and everything will become much simpler and clearer. 

What we are going to do now is actually quite confusing for such an early stage. We will talk about the confusion in the next article. In any case, it is necessary to do what will be shown here, and you must understand what will happen when you encounter something like this or when you need to implement something in the way shown.

As usual, we shall start with a simple thing. Our goal will be something similar to code 06 presented in the previous section. However, WE DO NOT USE A FUNCTION OR PROCEDURE. We shall talk about this later, as it is a bit more difficult to understand. Therefore, at this stage we will do everything in OnStart function. Let us get started! First, the code is provided below.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06. //+----------------+
07. #define macro_Swap(X)   for (uchar i = 0, j = sizeof(X) - 1, tmp; i < j; i++, j--)  \
08.                         {                                                           \
09.                             tmp = X.u8_bits[i];                                     \
10.                             X.u8_bits[i] = X.u8_bits[j];                            \
11.                             X.u8_bits[j] = tmp;                                     \
12.                         }
13. //+----------------+
14.     union un_01
15.     {
16.         ulong value;
17.         uchar u8_bits[sizeof(ulong)];
18.     };
19. 
20.     {
21.         un_01 info;
22. 
23.         info.value = 0xA1B2C3D4E5F6789A;
24.         PrintFormat("The region is composed of %d bytes", sizeof(info));
25.         PrintFormat("Before modification: 0x%I64X", info.value);
26.         macro_Swap(info);
27.         PrintFormat("After modification : 0x%I64X", info.value);
28.     }
29.     
30.     {
31.         un_01 info;
32. 
33.         info.value = 0xCADA;
34.         PrintFormat("The region is composed of %d bytes", sizeof(info));
35.         PrintFormat("Before modification: 0x%I64X", info.value);
36.         macro_Swap(info);
37.         PrintFormat("After modification : 0x%I64X", info.value);
38.     }
39. }
40. //+------------------------------------------------------------------+

Code 07

When executing code 07, we get a result very similar to code 04. However, here we are entering two values at the same time to show everything at once. Thus, the actual result of executing code 07 is what we see just below.

Figure 07

Just as we noted in image 04, where inappropriate values were placed in the final result, here in image 07, we see that the same values appear again. To simplify a bit: we have a value that could be ushort variable, but which is hindered by other incorrect values. I know that at first this code may seem correct, and it may even be correct, depending on what is implemented in it. But the purpose of this study is to help create a template for using things in such a way as to get a result similar to the one obtained in the previous section.

However, without using functions or procedures. Please note one more point: in order to avoid using a function or procedure at this stage, we are passing the general part of the code to a macro. You can see it from line 07 and further on. Since operation of macros has already been described, I will not go into details, as I do not see the need in this particular case.

Okay, let us proceed to the main issue. Unlike the previous thread, we are now dealing with a different kind of situation, but with a similar goal. Please note that the concept that could have been applied before can no longer be applied. This happens because in code 07 we do not have a function or procedure to which the template can be applied. However, the basic concept that is applied here is very similar to the one that has already been applied, only at first it is somewhat confusing.

Be careful, my dear reader. Our goal is to create a code capable of working with any type of data, as we did when creating what later became code 06. However, to achieve this goal, we will have to resort to a somewhat different type of overload. So far, overloading has been applied to functions or procedures. But now we need type overloading. This may seem a bit confusing, but the basic concept is the same as when using overloading for functions or procedures.

Then pay attention to the following: we need the union declared in line 14 of code 07 to work the same way as the union available in code 06. But how can we do it? It is very simple. Suffice it to say that union of line 14 is a template. "I do not understand. How are we going to do this?" Well, as I said, it can be a bit confusing when you first get to know this kind of simulation. To begin with, the link we see in line 14 is a local one. And each template MUST BE GLOBAL. Therefore, the first thing to do is to remove this declaration from OnStart procedure. Whereas, we convert the declaration into a global model. Then we just want to add what we are already used to seeing, that is, converting the union into a template, as is done for a function or procedure. This results in what is shown below in code 08.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. template <typename T>
05. union un_01
06. {
07.     T value;
08.     uchar u8_bits[sizeof(T)];
09. };
10. //+------------------------------------------------------------------+
11. void OnStart(void)
12. {
13. //+----------------+
14. #define macro_Swap(X)   for (uchar i = 0, j = sizeof(X) - 1, tmp; i < j; i++, j--)  \
15.                         {                                                           \
16.                             tmp = X.u8_bits[i];                                     \
17.                             X.u8_bits[i] = X.u8_bits[j];                            \
18.                             X.u8_bits[j] = tmp;                                     \
19.                         }
20. //+----------------+
21. 
22.     {
23.         un_01 info;
24. 
25.         info.value = 0xA1B2C3D4E5F6789A;
26.         PrintFormat("The region is composed of %d bytes", sizeof(info));
27.         PrintFormat("Before modification: 0x%I64X", info.value);
28.         macro_Swap(info);
29.         PrintFormat("After modification : 0x%I64X", info.value);
30.     }
31.     
32.     {
33.         un_01 info;
34. 
35.         info.value = 0xCADA;
36.         PrintFormat("The region is composed of %d bytes", sizeof(info));
37.         PrintFormat("Before modification: 0x%I64X", info.value);
38.         macro_Swap(info);
39.         PrintFormat("After modification : 0x%I64X", info.value);
40.     }
41. }
42. //+------------------------------------------------------------------+

Code 08

So far, everything has been very easy and simple, because we are dealing exactly with what we already can do. However, this is where the difficulties begin: if you try to compile code 08, you will fail. This is because, from the compiler's point of view, the code contains a type of error that is difficult for beginners to understand. The following shows the type of information that the compiler will provide when we try to compile this code.

Figure 08

It seems that we are not on the right track, as this number of errors is somewhat discouraging. However, this is where the problem lies, which really confuses many beginners. Because all these errors, which can be seen in Figure 08, occur because the compiler DOES NOT KNOW which data type to use. Since I have already provided many explanations on various topics in this article and do not want to confuse you with the solution to this issue, I will make a pause, leaving this question for explanation in the next article.


Final considerations

In this article, we have started exploring a very difficult topic, unless, of course, you practice what you are shown. However, since I want you to study properly, I decided to interrupt the last topic of this article. A proper explanation of how to tell the compiler what to do takes time, and I do not want to further confuse a topic that is already complicated enough.

Therefore, try to practice what was shown in this article. But basically try to figure out how to tell the compiler which data should be used so that code 08 provided at the end of this article actually works. Think about it calmly, because in the next article we will see how to actually do it, as well as how to use it to your advantage.

Translated from Portuguese by MetaQuotes Ltd.
Original article: https://www.mql5.com/pt/articles/15669

Attached files |
Anexo.zip (4.04 KB)
Last comments | Go to discussion (2)
Denis Kirichenko
Denis Kirichenko | 15 Aug 2025 at 09:54
Probably not Averange(), but Average()....
Ilya Prozumentov
Ilya Prozumentov | 15 Aug 2025 at 23:26
I think you need to specify the data type to the compiler when declaring a union implemented through a template.
un_01 <ulong> info;
CRUD Operations in Firebase using MQL CRUD Operations in Firebase using MQL
This article offers a step-by-step guide to mastering CRUD (Create, Read, Update, Delete) operations in Firebase, focusing on its Realtime Database and Firestore. Discover how to use Firebase SDK methods to efficiently manage data in web and mobile apps, from adding new records to querying, modifying, and deleting entries. Explore practical code examples and best practices for structuring and handling data in real-time, empowering developers to build dynamic, scalable applications with Firebase’s flexible NoSQL architecture.
Automating Trading Strategies in MQL5 (Part 27): Creating a Price Action Crab Harmonic Pattern with Visual Feedback Automating Trading Strategies in MQL5 (Part 27): Creating a Price Action Crab Harmonic Pattern with Visual Feedback
In this article, we develop a Crab Harmonic Pattern system in MQL5 that identifies bullish and bearish Crab harmonic patterns using pivot points and Fibonacci ratios, triggering trades with precise entry, stop loss, and take-profit levels. We incorporate visual feedback through chart objects like triangles and trendlines to display the XABCD pattern structure and trade levels.
Features of Experts Advisors Features of Experts Advisors
Creation of expert advisors in the MetaTrader trading system has a number of features.
From Basic to Intermediate: Template and Typename (II) From Basic to Intermediate: Template and Typename (II)
This article explains how to deal with one of the most difficult programming situations you can encounter: using different types in the same function or procedure template. Although we have spent most of our time focusing only on functions, everything covered here is useful and can be applied to procedures.