
Moving a Stop Loss to breakeven is a technique used in trading to manage open positions more safely. It involves moving the Stop Loss level to the trade’s opening price after the price has moved a certain number of points in profit. This helps protect the position and reduce the loss in case of an unexpected retracement.
The application of a breakeven mechanism involves two main approaches:
In this series of articles, we will develop three variations of the breakeven mechanism. Now, in the first part, we will create the base class of the system and program the first simple type of breakeven, which will serve as a template for future extensions.
Before integrating the concept of breakeven into our system, it is important to understand the basic mechanism of how it works.
In a simplified form, the breakeven mode involves moving the Stop Loss level by a fixed number of points (additional points) after the price has moved a certain distance from the trade’s opening price.
FIgure 1. A sell position before modifying the Stop Loss level
The first figure shows that the Stop Loss is initially placed at the level of the signal generated by the Order Blocks indicator (an indicator developed in previous articles of another series).
FIgure 2. A sell position after modifying the Stop Loss level
Figure 2 shows how, after the appearance of a new candle, the system automatically moves the Stop Loss level to a specified distance, corresponding to the set 150 points. This behavior reflects the activation process described above.
Next, we will examine what happened after the activation of the breakeven mode.
Figure 3: Sell position after closing at the breakeven level
As can be seen in Figure 3, the position closed at the breakeven level with a small profit due to the previously configured adjustment. In this particular case, a total loss (Stop Loss) was avoided because the price did not travel a significant distance from the entry point.
This is one of the advantages of the breakeven mechanism: it allows for potential profit or helps avoid total losses.
However, it is important to note that securing a profit depends on how many additional points the breakeven level is set at.
On the other hand, there are also some disadvantages to using this method. Sometimes, if the Stop Loss had not been moved, the trade could have reached its Take Profit target. This situation largely depends on the strategy being used.
In our case, when working with Order Blocks signals, applying the breakeven mode is generally advisable because there are instances where order blocks do not work out, making the protection of open positions critically important.
Another management option is to dynamically move the Stop Loss to breakeven based on the ATR (Average True Range).
This method is similar to the previous one, but instead of using a fixed number of points, it uses a multiplier parameter. The multiplier allows you to calculate the optimal distance for adjusting the Stop Loss, taking into account the current market volatility. This approach proves to be more flexible when dealing with highly volatile assets such as gold.
For example, if a fixed breakeven level of 150 points (equivalent to $1.50 in gold) is set, then during periods leading up to economic news releases, the price of gold can easily move $3.00 or higher. In such situations, using dynamic adjustment based on ATR helps to better adapt to market behavior, preventing normal fluctuations from closing the trade prematurely.
Thus, the ATR-based method does not use fixed distances but dynamically adapts to current market conditions.
Breakeven mode based on the Risk-Reward Ratio (RRR)
Finally, you can move the Stop Loss to breakeven based on the Risk-Reward Ratio (RRR). RRR represents the relationship between the risk taken and the expected profit from a trade. It is calculated by dividing the Take Profit value by the Stop Loss value.
Understanding RRR is important for better position management:
The logic behind applying the breakeven mode based on RRR is simple. Suppose a position is opened with a Take Profit that is twice the size of the Stop Loss, meaning an RRR of 1:2.
Using this method, the Stop Loss can be configured to move to the entry price when the trade reaches a 1:1 ratio relative to the initial risk. This means that once the market has advanced enough to offset the risk taken, the trade is automatically protected.
For example:
This approach allows for protecting trades after the market has shown sufficient movement in a favorable direction, based on a specific risk-reward ratio.
In this section, we will begin programming the fundamentals of the breakeven mechanism. Before creating the main class, we will define the necessary structures and enumerations. The process will be clear, simple, and straightforward. We will not use all the enumerations we created for risk management, as we only need a few of them to implement the breakeven feature. Let’s get started.
We will create a simple structure to store the core information that will allow us to apply the breakeven mode. This structure must contain:
We have created an enumeration that allows you to select one of the three types of breakeven activation mentioned earlier, designed to optimize the management of each trade:
Enumeration for breakeven mode
Not all trading strategies work the same way. Therefore, we have created an enumeration that allows you to unambiguously choose how the breakeven mode will be applied to open trades.
Before starting to define the base class, we will include the risk management system developed in previous articles.
Protected Variables of CBreakEvenBase
Below is the definition of the class and its internal variables. An explanation is also provided for each variable.
The variable that sends and modifies orders.
The MqlTick structure, which will store information about the last recorded tick.
The array of positions to which the breakeven mode will be applied.
The breakeven application mode (manual or automatic).
In this section, we begin defining the functions that will be inherited by the various classes implemented later.
Constructor
The constructor is a key part of any class. In the CBreakEvenBase class, its task is to initialize the internal variables and assign them the necessary values so that the class functions correctly.
This constructor accepts three parameters: symbol, from which the bid and ask data will be taken; magic, which allows for the identification of specific orders; and mode, which indicates the selected type of breakeven management (it can be automatic or manual).
Constructor definition
When the constructor is executed, the pause and allow_extra_logs variables are initialized with the value false. Then, it checks whether the received mode value is valid. If it is not, an error message is printed, and ExpertRemove is called to remove the expert advisor from the chart, preventing the code from continuing to execute with an incorrect setting.
The corresponding values are also assigned to the symbol, magic, and breakeven_mode parameters using the values received as arguments.
The point value of the symbol is stored in the point_value variable, and the number of parameters is initialized with the value 0. Each class inherited from CBreakEvenBase will define its own number of parameters in its own constructor.
Additionally, if the magic_ variable equals NOT_MAGIC_NUMBER, we will not execute the CTrade class member function intended for setting the magic number.
Note: NOT_MAGIC_NUMBER — is a define created inside the risk management include file. This define serves to indicate that the magic number will not be used. Therefore, if the breakeven mechanism is used, it will be applied to all trades (only if the breakeven mode is automatic).
Destructor
The destructor is used to free the memory used by the array that stores the managed positions. This function is executed automatically when the object is destroyed.
Next, we will define the functions that are part of the base class CBreakEvenBase. These functions will be used and extended by the derived classes that will be implemented later.
To begin, we will declare a function that returns the number of parameters required by the class. This function simply returns the value of the protected variable num_params. Additionally, it is marked as const to prevent modifying the state of the object, and as final to prevent it from being overridden by child classes.
Next, we declare the Add function, which will be used to add a new position_be structure to the PositionsBe array. This function will receive the necessary trade data, such as the ticket, opening price, Stop Loss, and position type.
Each class inheriting from CBreakEvenBase must implement its own version of this function, as it is declared as pure and virtual.
The main function of this class is to ensure breakeven. It is responsible for applying the breakeven adjustment to all positions stored in the PositionsBe array. To do this, the array is iterated over using a for loop.
Before applying the adjustment, it checks that the array size is greater than zero and that the pause variable is false. If these conditions are not met, the function immediately exits.
Since the position_be structure already contains both the target level and the price at which the adjustment should be triggered, it is only necessary to check whether the market condition is met:
After confirming the condition, the position is identified using its corresponding ticket, and the current Take Profit value is calculated. Then, the modification is performed using the PositionModify method of the CTrade class. Finally, this position is marked for removal from the array.
To add a structure to the PositionsBe array when a new trade is opened, we define a function that will be called inside the OnTradeTransaction event. This function is named OnTradeTransactionEvent and will be executed every time a trading transaction occurs.
Before adding a position to the PositionsBe array, it is necessary to select its corresponding ticket using the HistoryDealSelect function. Then, the trade entry type is determined using the entry value.
If the entry corresponds to a market entry, i.e., DEAL_ENTRY_IN, it checks that the deal is fully opened, that the breakeven mode is automatic — BREAKEVEN_MODE_AUTOMATIC, and that the deal’s magic number matches the internal magic variable, or that the latter has the value NOT_MAGIC_NUMBER.
When all conditions are met, the position is added to the PositionsBe array using the Add function. If allow_extra_logs is activated, a message indicating the position registration is printed.
In the case where the entry corresponds to an exit, i.e., DEAL_ENTRY_OUT, and the position is fully closed, the corresponding ticket is removed from the PositionsBe array. To avoid potential conflicts during this removal, the pause variable is temporarily set to true.
The code of the function is structured as follows:
Some derived classes will require the configuration of various parameters. For example, a simple adjustment needs two integer values, while other methods, such as the ATR-based method, require data like the period, timeframe, and multiplier.
Since it is not possible to define multiple versions of a single virtual function with different parameters in the base class, it is proposed to use an array of type MqlParam to store the necessary configuration. This way, each inheriting class can internally interpret and assign the received values.
Finally, a function is added to allow enabling or disabling additional informational messages. This is useful when you need to log actions such as the addition or removal of tickets during program execution.
Now that the base class is ready, we will begin developing the CBreakEvenSimple class. This class applies the breakeven method using a fixed number of points.
In the constructor, the initial values must be set. The base class constructor is also called to assign the inherited parameters. Inside the constructor body, the internal variables are initialized to zero, and the number of parameters required by the class is defined, which in this case is two.
Function for setting parameters
To assign values to the internal variables extra_points_be and points_be, an array of type MqlParam will be used. This structure is part of the MQL5 language and is typically used when adding indicators via the ChartIndicatorAdd() function.
In this case, it will be used to pass values to the class. The Set function will be overridden from the base class. The keyword “override” is used for this.
Inside the function, it checks that the size of the params array is not less than two. If this condition is not met, an informational error message is displayed. Otherwise, the SetSimple function is called to assign the corresponding values.
The SetSimple function
The SetSimple function allows directly setting the values for points_be and extra_points_be without using a parameter array. In this case, the integer values are accepted directly when the function is called.
This method can be useful if the values are defined directly in the code, or if in some specific cases it is necessary to avoid using the MqlParam type.
Function for adding position data to the PositionsBe array
To continue developing the CBreakEvenSimple class, the Add function must be overridden. This function allows calculating and registering the main values of the position_be structure, which will then be stored in the PositionsBe array.
The open_price parameter is used as a basis for calculating two values: breakeven_price and price_to_beat. These variables define the level to which the Stop Loss will be moved, and the level the price must reach to trigger the adjustment.
The following formulas are used to calculate the breakeven_price value:
break_even_price = open_price + (point_value * extra_points_be)
break_even_price = open_price – (point_value * extra_points_be)
On the other hand, to calculate price_to_beat, the same formulas are used, but the variable extra_points_be is replaced by points_be.
After determining these values, the position_be structure is filled with the necessary data. This structure is then added to the PositionsBe array using the AddArrayNoVerification function, which was already used in the previous section on risk management.
This completes the basic implementation of the fixed-point breakeven method. In the next section, the functionality of this class will be tested using the Order Blocks expert advisor, developed in the last article on risk management.
In this section, we will adapt the Order Blocks expert advisor to integrate the application of the fixed-point breakeven mode. The goal is to verify the system’s operation and evaluate the behavior of positions under such active management.
To begin, we modify the advisor’s parameters, adding a new specialized section:
In this section, all parameters related to the breakeven mechanism will be placed. Since only the fixed-point variant has been developed so far, a general bool parameter is added to activate or deactivate it.
Next, a subsection for the specific parameters of this method will be defined.
To integrate the functionality, an object of type CBreakEvenSimple is created. In the future, this will be replaced by a more flexible manager type object, which will allow managing different variants from a single interface.
In the OnInit function, if the use_be parameter is enabled, the values configured via the SetSimple function will be assigned.
For the breakeven management to work correctly, the OnTradeTransactionEvent function of the break_even object must be executed inside the OnTradeTransaction function. It will be responsible for registering or removing deals in the corresponding array.
Finally, inside OnTick, the breakeven function will only be called if the use of this mechanism is enabled by the user:
Thanks to these changes, the robot can now automatically apply the breakeven mechanism when the specified conditions are met.
Backtest
The backtest of the Order Blocks expert advisor with the breakeven mode was conducted without capital management restrictions. No profit or loss limits were set. Trades could only close when the Stop Loss or Take Profit level was reached. A Risk:Reward ratio of 1:2 was used, where the Take Profit is twice the Stop Loss.
2. Backtest chart of the strategy operating without the breakeven mechanism.
FIgure 5. Backtest chart without using the breakeven mechanism
Figure 5 shows the behavior of the advisor without using the breakeven mechanism. During this test, the initial balance of $15,000 decreased to $8,700, resulting in a loss of approximately $7,000. This result serves as a benchmark for evaluating the consequences of enabling or disabling this feature.
3. Backtest chart of the strategy operating without the breakeven mechanism.
FIgure 6. Backtest chart without using the breakeven mechanism
The second test was conducted with the breakeven mode activated. The be_fixed_points_extra parameter was set to 100 points, and the be_fixed_points_to_put_be parameter was set to 600 points.
During this testing, the chart showed a slower progression. In series of losing trades, the balance decline was smaller. The balance decreased from $10,900 to $7,900, representing a $3,000 loss. This $3,000 difference compared to the first scenario indicates that using the breakeven mechanism can help reduce the impact of losing streaks.
However, limitations were also identified. In both tests, trades started with losses until approximately March 2024. Starting from 01.03.2024, changes were observed: a series of profitable trades appeared. In the first backtest, the balance exceeded $16,000, whereas in the second, with the breakeven mode activated, it only reached $10,900.
One possible explanation is early closures caused by the breakeven mode. If, after opening a position, the price retraces but does not reach the Stop Loss level, then in the absence of a breakeven mechanism, it may reverse in the expected direction and close with a profit. Conversely, with the breakeven mode activated, the position would close at zero, eliminating the possibility of capturing that profit.
This suggests that in the first scenario, many trades remained in a negative balance until reaching Take Profit. For this reason, when applying the fixed-point breakeven mode, the result may be limited in scenarios where price retracements occur frequently.
In conditions of high volatility, this configuration can activate quickly, protecting capital, but at the same time prematurely closing trades. In periods of lower activity, it may not trigger at all. Thus, using the fixed-point breakeven mode is more suitable for traders who prioritize balance protection over achieving higher profit per trade.
In this article, we analyzed the concept of breakeven, its implementation in MQL5, and some possible variations. Everything developed was applied to the Order Blocks expert advisor, created in the last article on risk management.
To conclude the analysis, a comparison was made of the system’s behavior with the breakeven mode activated and without it. It was observed that during series of profitable trades, its use could limit balance growth. Conversely, during losing streaks, the breakeven mode reduces exposure to large losses. This duality indicates the existence of a certain balance between risk and result, although the real effectiveness will depend on the statistical results of the underlying strategy without external constraints (such as profit or loss limits).

