Русский Español Deutsch 日本語 Português
preview
From Basic to Intermediate: Passing by Value or by Reference

From Basic to Intermediate: Passing by Value or by Reference

MetaTrader 5Examples | 12 March 2025, 09:59
1 168 2
CODE X
CODE X

Introduction

The content presented here is intended solely for educational purposes. Under no circumstances should the application be viewed for any purpose other than to learn and master the concepts presented.

In the previous article "From Basic to Intermediate: Operators", we explored arithmetic and logical operations. Although the explanation was somewhat superficial, it is sufficient for us to move on to other topics. As time goes on and more articles are published, we will gradually delve deeper into the subjects initially introduced.

Therefore, be patient and take your time to study the material. Results do not appear overnight; they come with dedication and consistency. By starting your studies now, you will barely notice the increasing complexity over time. As mentioned in the previous article, we were about to start discussing control operators. However, before we dive into that topic, we need to address another important concept. This will make learning about control operators more engaging and enjoyable, as you will better understand what can be accomplished with them.

To fully and properly understand what will be explained and demonstrated in this article, a prerequisite is required: understanding the difference between a variable and a constant. If you are unfamiliar with this distinction, please refer to the article From Basic to Intermediate: Variables (I).

One of the most common sources of confusion and mistakes in programs written by beginners is knowing when to use passing by value and when to use passing by reference in functions and procedures. Depending on the situation, passing by reference may be more practical. However, passing by value is often safer. But when should you choose one over the other? Well, dear reader, it depends. There is no absolute or definitive rule for this practice. In some cases, passing by reference is indeed the best choice, while in others, passing by value is the appropriate approach.

Typically, the compiler attempts to make choices that result in the most efficient executable code possible. Nevertheless, it is crucial that you understand what each scenario demands so that you can write code that is both safe and efficient.


Passing by Value

To begin understanding this concept in practice, the most appropriate approach is to work with code that is simple to implement. Therefore, let's start by examining a first usage example. This will be passing by value in the code below.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     int info = 10;
07. 
08.     Print(__FUNCTION__, " : #1 => ", info);
09.     Procedure(info);
10.     Print(__FUNCTION__, " : #2 => ", info);
11. }
12. //+------------------------------------------------------------------+
13. void Procedure(int arg1)
14. {
15.     Print(__FUNCTION__, " : #1 => ", arg1);
16.     arg1 = arg1 + 3;
17.     Print(__FUNCTION__, " : #2 => ", arg1);
18. }
19. //+------------------------------------------------------------------+

Code 01

When Code 01 is executed, you will see something in the MetaTrader 5 terminal similar to what is shown in Figure 01 below.


Figure 01

I know that, for many, what is shown in Figure 01 may seem quite complex. But since you are committed to learning how to properly program in MQL5, let's take the time to understand what this image is telling us. To do this, you need to follow both Code 01 and what is displayed in Figure 01 closely.

Based on what was explained in previous articles, you should already know that on line six we are defining a variable with a given value. On line eight, we are printing this same variable. However, if you look at the image, you will notice there is other information being printed. In this case, it's the name of the procedure or function where line eight is located.

Now, pay close attention, dear reader. During the compilation phase of Code 01, as soon as the compiler encounters the predefined macro __FUNCTION__, it will search for the name of the current routine. In this case, that name is defined on line four, OnStart. As soon as this name is found, the compiler will replace __FUNCTION__ with OnStart, thereby creating a new string to be printed to the terminal. This is because the Print function directs its output to the standard output, which in MetaTrader 5 is the terminal, as you can see in Figure 01. As a result, we get both the routine's name and the value of the variable declared on line six printed out. There are other predefined macros, each very useful in particular situations. Be sure to study them, as they greatly assist in tracking what your code is doing. Just as __FUNCTION__ inside the OnStart routine is replaced with its name, if used within the Procedure routine, it will be replaced there too. This explains the information that precedes the numerical values shown.

Now, let's return to understanding passing by value. Because we are using passing by value system, when the function call on line nine is executed, the value held by the variable defined on line six will be passed to the procedure declared on line thirteen. Here's an important observation. Although I am saying that we are "passing" or rather "copying" the value of the 'info' variable into 'arg1', this doesn't always happen literally. Often, the compiler, in a very efficient way, makes 'arg1' point to 'info'. However (and here is where things get truly interesting) even though 'arg1' points to 'info', it does not share the same memory space. What happens is that the compiler makes 'arg1' reference 'info' in a way that allows it to "see" and "use" its value, but without modifying it - like looking through a glass window. You can see what's on the other side, but you can't touch it. Likewise, 'arg1' treats 'info' as a constant.

Because of this, when we print 'arg1' value on line fifteen, We see in Figure 01 that the second line shows the same value for both 'arg1' and 'info'. However, when we change 'arg1' value on line sixteen, something important happens:. During compilationб knowing that this operation will occur, the compiler allocates new space to store the same amount of bytes as 'info'. Even so, 'arg1' will still initially "observe" 'info'. But when line sixteen is executed, 'arg1' will take 'info's current value as if it were immutable and create a local copy for itself. From that moment on, 'arg1' becomes completely independent from 'info'. Thus, when line seventeen is executed, we get the third line printed in Figure 01, showing that 'arg1' value has indeed been changed.

However, when the procedure returns and line ten is executed, the fourth line in Image 01 is printed, showing that 'info' value remained intact.

This, dear reader, is essentially how passing by value works. Of course, the exact implementation depends on how the compiler is built, but this is the general idea.

Very well, that was the easy part. However, even this mechanism of passing by value can be used in a slightly different way in code. I won't go into detail about that right now. This is because before discussing other ways to use passing by value, we need to cover more about operators and data types. Discussing that here would make things more complicated rather than clearer. So let's proceed step by step.

Nonetheless, even this mechanism we just examined can be adapted. This will allow us to avoid what happened in the example above. To explore this properly, let's consider a small hypothetical situation. Suppose, for some reason, you do not want 'arg1' to have its value modified. You want it to only reference 'info', and every time 'arg1' is used, you want it to reflect the current value of 'info'.

So, how can we achieve this? There are many ways. One is to be extremely careful not to modify 'arg1' within the procedure block. But although this may seem simple, in practice, it's not. Often, without realizing it, we end up changing a variable value, and only notice the issue when strange behavior appears during program execution. These situations can cost a lot of time and effort to fix. However, there is a better solution: let the compiler do the work for us by warning us if we attempt something we shouldn't.

To make this clear, let's look at the following code.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     int info = 10;
07. 
08.     Print(__FUNCTION__, " : #1 => ", info);
09.     Procedure(info);
10.     Print(__FUNCTION__, " : #2 => ", info);
11. }
12. //+------------------------------------------------------------------+
13. void Procedure(const int arg1)
14. {
15.     Print(__FUNCTION__, " : #1 => ", arg1);
16.     arg1 = arg1 + 3;
17.     Print(__FUNCTION__, " : #2 => ", arg1);
18. }
19. //+------------------------------------------------------------------+

Code 02

When attempting to compile Code 02, the following error message will be displayed.


Figure 02

Clearly, you can see that the compiler indicates the error is on line 16. This happens because we are trying to modify the value of the variable 'arg1'. But hold on - 'arg1' is no longer a regular variable. It has been declared as a constant. Thus, throughout the entire Procedure function, you will no longer be able to change the value of 'arg1'. This effectively eliminates the risk we identified earlier, since the compiler itself will now ensure that 'arg1' cannot be modified. In other words, knowing what you are doing and having the proper understanding of these concepts is extremely useful - it makes the programming process much more efficient.

Does this mean we can't change the value that will be printed on line 17 of Code 02? Yes, dear reader, you can still modify the value that will be printed, as long as you assign it to another variable, even if this new variable is not explicitly declared in the previous code. So, to achieve the same output as seen in Figure 01, while using an approach similar to Code 02, we can write code very close to what is shown below.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     int info = 10;
07. 
08.     Print(__FUNCTION__, " : #1 => ", info);
09.     Procedure(info);
10.     Print(__FUNCTION__, " : #2 => ", info);
11. }
12. //+------------------------------------------------------------------+
13. void Procedure(const int arg1)
14. {
15.     Print(__FUNCTION__, " : #1 => ", arg1);
16.     Print(__FUNCTION__, " : #2 => ", arg1 + 3);
17. }
18. //+------------------------------------------------------------------+

Code 03

By making the necessary adjustments to create Code 03, we continue to treat 'arg1' as a constant. However, on line 16 of Code 03, we are assigning the sum of two constants (in this case, arg1 and the value 3) to another variable. This new variable is created by the compiler to allow Print to correctly display the result. Of course, you could create a dedicated variable solely for this purpose. However, I don't see the need to do that, at least not in this kind of code being presented here.


Passing by Reference

The other way to pass values between routines is by reference. In this case, we must take some additional precautions. But before we proceed, I would like to pause briefly to explain something very important at this point.

You, dear reader, should avoid using passing by reference as much as possible, unless it is truly and absolutely necessary. One of the most common causes of difficult to solve errors is the improper or careless use of passing by reference. In fact, for some programmers, it becomes almost a habit, and this can make fixing and improving existing code a real nightmare. Therefore, avoid using pass by reference unless strictly needed, especially if the only purpose is to modify a single value. I will show an example of this shortly. But before that, let's first examine a case where passing by reference is used, and understand what this does to the application. To illustrate this, let's analyze the code below.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     int info = 10;
07. 
08.     Print(__FUNCTION__, " : #1 => ", info);
09.     Procedure(info);
10.     Print(__FUNCTION__, " : #2 => ", info);
11. }
12. //+------------------------------------------------------------------+
13. void Procedure(int & arg1)
14. {
15.     Print(__FUNCTION__, " : #1 => ", arg1);
16.     arg1 = arg1 + 3;
17.     Print(__FUNCTION__, " : #2 => ", arg1);
18. }
19. //+------------------------------------------------------------------+

Code 04

Now, pay very close attention to what I'm about to explain here. Because this is where things can get really tricky, especially when you're working with truly complex code. When you execute this code, you will see in the MetaTrader 5 terminal the following output:


Figure 03

This kind of issue, when it happens unintentionally, can cause you to waste hours, days, or even months trying to figure out why the code is not working as expected. Notice that the result is completely different from what was shown in Figure 01, especially regarding the fourth line in Figure 03. But why did this happen? After all, Code 04 looks identical to Code 01, right? It makes no sense for the fourth line of Figure 03 to be different from that in Figure 01.

Well, my dear reader, despite appearances, Code 01 and Code 04 have a small but crucial difference. This difference is right there on line 13. I made a point of highlighting it so you can understand what's going on. Take a close look at a seemingly innocent symbol that appears in Code 04 but does not exist in Code 01. That symbol is & also known as the ampersand. And it is precisely the cause of the discrepancy between Figure 03 and Figure 01. Usually, you'll see this symbol used in bitwise logical operations, specifically, AND operations, when working with MQL5. However, when it comes to C and C++, beyond applying it for AND operations, it is also used to reference variables in memory.

And now, things get really serious. Because if it's already hard to understand what one simple symbol means, imagine when the same symbol has two completely different meanings within the same code. That's literally insane. This is one of the reasons why programming in C and C++ is so complex and takes a long time to master. But, fortunately, in MQL5, things are a little simpler. At least, we don’t deal directly with one of C and C++'s most confusing mechanisms: pointers.

To help you, dear reader, properly understand why the presence of this symbol on line 13 of Code 04 affects the final result, we need to clarify what this symbol really means. So, let me explain the concept of pointers from C/C++, but without diving into all the complexity involved.

A pointer is like a variable, but not just any variable. As the name implies, it points to something. Specifically, a pointer points to a memory address where a variable is stored. I know this may sound confusing - a variable pointing to another variable. But this concept is widely used in C and C++ to write highly efficient and diverse code. It's one of the reasons why C and C++ are among the fastest languages out there, rivaling Assembly in terms of execution speed. But let's not dive too deep into pointers to avoid confusion. What you need to understand here is that when 'arg1' is declared as shown in Code 04, we are NOT using a conventional way of creating the 'info' variable. Instead, 'arg1' is treated as a pointer to 'info' by the MQL5 compiler.

Because of this, when we perform the addition on line 16, we are not changing 'arg'. We are NOT adding to 'arg1' itself. We are actually modifying 'info' because 'arg1' and 'info' share the same memory space. Inside the Procedure routine, 'arg1' is 'info', and 'info' is 'arg1'.

Confused? That's understandable. This is actually the "simple" and "easy" part of using pointers. Luckily, since MQL5 does NOT (and will NOT) allow us to directly use pointers as in C/C++, our explanation of how 'arg1' modifies 'info' can stop here. You should now think of 'arg1' and 'info' as the same entity, even though they are declared in different places and appear unrelated. In C/C++, this would be way more complicated. And to avoid overwhelming you, dear reader, we'll stop here on that topic.

Now there's a question: Is there a way to block this kind of modification? Meaning, is there a way to prevent 'arg1', when changed in line 16, from also modifying 'info'? Yes, there is! One way, as explained in the previous topic, is using passing by value. Another way is to modify Code 04 to look like what is shown in Code 05.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     int info = 10;
07. 
08.     Print(__FUNCTION__, " : #1 => ", info);
09.     Procedure(info);
10.     Print(__FUNCTION__, " : #2 => ", info);
11. }
12. //+------------------------------------------------------------------+
13. void Procedure(const int & arg1)
14. {
15.     Print(__FUNCTION__, " : #1 => ", arg1);
16.     arg1 = arg1 + 3;
17.     Print(__FUNCTION__, " : #2 => ", arg1);
18. }
19. //+------------------------------------------------------------------+

Code 05

However, when we write Code 05, we fall into the same scenario as Code 02. In other words, 'arg1' will be treated as a constant. And trying to compile Code 05 will yield the same error as seen in Figure 02. Even though 'arg1' is pointing to 'info', which is a variable, this is not a compiler error. In fact, sometimes we are forced to do something like Code 05. But still, it won't compile because line 16 tries to modify 'arg1'. The final solution to this problem would be to adopt a code similar to Code 03, which would result in the code shown below:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     int info = 10;
07. 
08.     Print(__FUNCTION__, " : #1 => ", info);
09.     Procedure(info);
10.     Print(__FUNCTION__, " : #2 => ", info);
11. }
12. //+------------------------------------------------------------------+
13. void Procedure(const int & arg1)
14. {
15.     Print(__FUNCTION__, " : #1 => ", arg1);
16.     Print(__FUNCTION__, " : #2 => ", arg1 + 3);
17. }
18. //+------------------------------------------------------------------+

Code 06

With this, we now have a functional code that resembles Code 03. But instead of passing by value, we are using passing by reference. The result of running Code 06 will be the same as in Figure 01.

This brings us back to the point raised at the start of this topic - avoiding pass by reference to modify just a single variable.

If, for any reason, you need a routine to modify a variable's value, especially a basic type, you should prefer using a function instead of a procedure. That's why functions exist in programming languages. Functions were not created just to make code pretty - they exist to avoid issues in code.

Now, if you truly want 'info' to be modified, but from a routine and not directly where it's declared, the correct and most appropriate way is similar to the one shown below:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     int info = 10;
07. 
08.     Print(__FUNCTION__, " : #1 => ", info);
09.     info = Function(info);
10.     Print(__FUNCTION__, " : #2 => ", info);
11. }
12. //+------------------------------------------------------------------+
13. int Function(const int & arg1)
14. {
15.     Print(__FUNCTION__, " : #1 => ", arg1);
16. 
17.     return arg1 + 3;
18. }
19. //+------------------------------------------------------------------+

Code 07

Notice that Code 07 still uses passing by reference. But it uses it in a fully controlled way. As you can see on line 09, where 'info' is explicitly modified. When written this way, using functions, it's very unlikely this will cause problems. And the result of running Code 07 is shown right below:


Figure 04

See? At no point are we left wondering what's happening. A simple look at the code makes it clear why info is modified inside the OnStart block. Be careful and don't do anything like that.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     int info = 10;
07. 
08.     Print(__FUNCTION__, " : #1 => ", info);
09.     info = Function(info);
10.     Print(__FUNCTION__, " : #2 => ", info);
11. }
12. //+------------------------------------------------------------------+
13. int Function(int & arg1)
14. {
15.     Print(__FUNCTION__, " : #1 => ", arg1);
16. 
17.     return (arg1 = arg1 + 3) + 3;
18. }
19. //+------------------------------------------------------------------+

Code 08

It's very common for some programmers to attempt something like this, with a goal in mind, but end up creating a real mess. An example would be code 08. Although this isn't a perfect example since these mistakes often occur in much more complex operations involving many variables and steps. Anyway, it serves to illustrate the issue.

So here's my question to you, my dear reader: What is the value of 'info' to be printed on line 10 of Code 08? You may not have noticed, but arg1 is no longer a constant. Then, what information value should be output in line 10? You may be confused because of line 17 where both the returned value and the value assigned to 'info' are modified at the same time. Before explaining why this isn't so confusing, check the output, shown below:


Figure 05

Notice that the value is 16, not 13. Why? Because although 'arg1' is a pointer to 'info', and line 17 assigns 13 to both, the function's return value overrides it. This happens because it adds 3 more to what was assigned in line 17. Therefore, the output that is printed is actually 16, since the function's return is assigned to the variable 'info'.

However, if instead of assigning the return value to 'info' on line 9, you simply ignored the value returned by the function, you would inevitably end up in the same situation we observed in previous examples. In other words, the value of info would be modified, but as you try to understand and debug the code, you would likely spend a significant amount of time just identifying where the issue lies. Keep in mind that code containing this type of error is often spread across multiple files, each with hundreds of lines of code. So finding the root cause of such a problem can be extremely time-consuming.

Regarding return values, it is not uncommon for them to be completely ignored. After all, we are not obliged to assign or even use a return value in our code. Therefore, be cautious when using this kind of construction in your programs. Despite the great flexibility that passing by reference offers, it is neither difficult nor unlikely that you will eventually face issues arising from this approach, especially if you are still gaining experience in programming.

Now, as a final point to address here: there is another problem that often occurs when using passing by reference. It usually stems from a mistake when passing arguments to the routine that will process them. When working with passing by value, accidentally swapping one argument for another typically has minimal impact on the overall code. More often than not, during the compilation process, the compiler will raise a warning, especially if the expected data types are different. However, when using passing by reference, the compiler might warn you about a mismatch, if the data types are compatible and you overlook the fact that the argument order is incorrect. Despite that, you may commit an error that is much harder to identify later. For example, imagine you want to create a function where you pass a date and a time in hours. The function should add the specified number of hours to the date, and in the same function, you want to convert those hours into seconds, updating the corresponding variable. This seems like a straightforward and practical task that could be implemented with the following code:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     uint        info = 10;
07.     datetime    dt = D'17.07.2024';
08. 
09.     Print(__FUNCTION__, " : #1 => ", info, " :: ", dt);
10.     dt = Function(dt, info);
11.     Print(__FUNCTION__, " : #2 => ", info, " :: ", dt);
12. }
13. //+------------------------------------------------------------------+
14. datetime Function(ulong &arg1, datetime arg2)
15. {
16.     Print(__FUNCTION__, " : #1 => ", arg1, " :: ", arg2);
17. 
18.     return arg2 + (arg1 = arg1 * 3600);
19. }
20. //+------------------------------------------------------------------+

Code 09

However, when requesting the compiler to build the executable, it issues a warning as shown below. This kind of warning does not prevent the code from compiling:


Figure 06

But since you are a programmer who always pays attention to every message the compiler generates, you immediately review the line referenced in the warning and make the necessary correction. In this case, it's simply a matter of explicitly casting the value from ulong to datetime. Important detail: both data types have the same bit-width. This is why many people tend to ignore such warnings. As a result, line 18 of Code 09 is adjusted as shown below.

return arg2 + (datetime)(arg1 = arg1 * 3600);

Now that the compiler warning is gone, you proceed to run the program. But to your surprise, the output is as shown below:


Figure 07

At this point, you may feel completely puzzled because the code appears to be correct, yet it doesn't work. And here lies the key detail: your code is not entirely wrong. It merely contains a subtle error. Such errors are often the hardest to identify. Here, it's easier to spot the problem because the code is simple and short, and because we are just demonstrating basic concepts. In real-world scenarios, you rarely write and test small isolated code snippets. Typically, you develop various parts of a program that interact. And only later start testing to see if everything works together. Sometimes you will notice an issue, but other times you won't. What makes finding a bug in complex code so challenging is that the same routine may cause errors in some contexts but work perfectly in others. This makes debugging extremely difficult.

Finally, let's figure out where the error lies here. The flaw is exactly on line 10. Now, you might be thinking: "Come on, how could the mistake be on line 10? Surely the error is within the routine starting on line 14, probably on line 18. It can't possibly be on line 10."

But that's where you are mistaken, dear reader. Let's analyze this carefully. You expect 'info' to hold the final value in seconds. You want the 'dt' variable to start with the date declared on line 7 but be adjusted by the function based on 'info'. This adjustment must be made in line 14 of the subroutine, namely in line 18. So far, so good. However, there's a small but critical error in the code. Since datetime (8 bytes) and ulong (also 8 bytes) share the same size, the function won't complain about the difference in data types. But 'info' is declared as uint. You may think that's where the issue lies, but it's not. Because 24 hours equals 86,400 seconds, and uint that takes 4 bytes can comfortably store this value. Although you could use a smaller data type, that would risk overflow.

Now, since 'arg1' is a pointer (thus using passing by reference) and 'arg2' is passed by value, the error truly lies in line 10, where the first argument should be the variable 'info' (as it will be modified). And the second one should be 'dt', which is to be adjusted based on the function's return value. With this in mind, you should replace line 10 with the following corrected version:

    dt = Function(info, dt);

Upon trying to compile this corrected code, the compiler will raise an error like this:


Figure 08

Now, frustrated and desperate to get the code working, and realizing that you don't actually need an 8-byte value (since 4 bytes are sufficient), you decide to modify line 14 as follows.

datetime Function(uint &arg1, datetime arg2)

Finally, the code compiles. When you run it again, the expected and correct output appears, as shown here:


Figure 09


Final considerations

In this article, we explored subtle but critical nuances of real-world programming. Of course, all examples here are simple and designed for educational purposes. Still, explaining how things truly work under the hood is both enjoyable and valuable. I know many may find this kind of material "too basic" or not particularly exciting, especially those who have been programming for a long time. But let me ask you: how much time did you waste learning the concepts explained here in such a straightforward way? Personally, I spent a long time figuring out why my C/C++ code would sometimes behave in strange and inexplicable ways, until I finally understood all the underlying details.

Once you grasp these fundamentals, everything else becomes much simpler and more intuitive. Today, I have fun programming in various languages, enjoying the challenges each one throws at me. And that's because I built a solid foundation from C/C++. MQL5 is a far more pleasant, simpler, and easier language to explain compared to all the complexity inherent in C/C++. However, if you, dear reader, truly understand the explanations and demonstrations in this article, you will be able to learn how to create excellent MQL5 applications much more quickly. In the next article, we will finally begin working on more fun and practical content. See you soon


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

Attached files |
Anexo.zip (3.12 KB)
Last comments | Go to discussion (2)
MrBrooklin
MrBrooklin | 28 Jan 2025 at 09:00

Hello. Inaccuracy in the text (underlined with red lines)

Regards, Vladimir.


Rashid Umarov
Rashid Umarov | 28 Jan 2025 at 09:47
MrBrooklin #:

Hello. Inaccuracy in the text (underlined with red lines)

Regards, Vladimir.


Thank you, corrected

Neural Networks in Trading: A Complex Trajectory Prediction Method (Traj-LLM) Neural Networks in Trading: A Complex Trajectory Prediction Method (Traj-LLM)
In this article, I would like to introduce you to an interesting trajectory prediction method developed to solve problems in the field of autonomous vehicle movements. The authors of the method combined the best elements of various architectural solutions.
Data Science and ML (Part 34): Time series decomposition, Breaking the stock market down to the core Data Science and ML (Part 34): Time series decomposition, Breaking the stock market down to the core
In a world overflowing with noisy and unpredictable data, identifying meaningful patterns can be challenging. In this article, we'll explore seasonal decomposition, a powerful analytical technique that helps separate data into its key components: trend, seasonal patterns, and noise. By breaking data down this way, we can uncover hidden insights and work with cleaner, more interpretable information.
Developing a multi-currency Expert Advisor (Part 17): Further preparation for real trading Developing a multi-currency Expert Advisor (Part 17): Further preparation for real trading
Currently, our EA uses the database to obtain initialization strings for single instances of trading strategies. However, the database is quite large and contains a lot of information that is not needed for the actual EA operation. Let's try to ensure the EA's functionality without a mandatory connection to the database.
Automating Trading Strategies in MQL5 (Part 11): Developing a Multi-Level Grid Trading System Automating Trading Strategies in MQL5 (Part 11): Developing a Multi-Level Grid Trading System
In this article, we develop a multi-level grid trading system EA using MQL5, focusing on the architecture and algorithm design behind grid trading strategies. We explore the implementation of multi-layered grid logic and risk management techniques to handle varying market conditions. Finally, we provide detailed explanations and practical tips to guide you through building, testing, and refining the automated trading system.