preview
Automating Trading Strategies in MQL5 (Part 47): Nick Rypock Trailing Reverse (NRTR) with Hedging Features

Automating Trading Strategies in MQL5 (Part 47): Nick Rypock Trailing Reverse (NRTR) with Hedging Features

MetaTrader 5Trading |
879 0
Allan Munene Mutiiria
Allan Munene Mutiiria

Introduction

In our previous article (Part 46), we built a Liquidity Sweep on Break of Structure trading system in MetaQuotes Language 5 (MQL5) that identifies liquidity zones, detects breaks of structure, and executes trades with customizable risk parameters and visual indicators. In Part 47, we create a Nick Rypock Trailing Reverse (NRTR) system that utilizes channel-based reversal signals for trend-following entries, and we integrate hedging capabilities, dynamic trailing stops, and risk management features. We will cover the following topics:

  1. Examining the Nick Rypock Trailing Reverse Strategy and Its Components
  2. Implementation in MQL5
  3. Backtesting
  4. Conclusion

By the end, you’ll have a functional MQL5 program for NRTR-driven reversal trading with hedging, ready for customization—let’s dive in!


Examining the Nick Rypock Trailing Reverse Strategy and Its Components

The Nick Rypock Trailing Reverse strategy identifies potential trend reversals by tracking the emergence of dynamic support levels in an NRTR channel, which is constructed using Average True Range calculations multiplied by a user-defined factor to form upper and lower boundaries that adapt to volatility. Entry signals occur when a new support level appears, initiating a buy position at the start of an upward support (long channel) or a sell position at the beginning of a downward support (short channel), enabling the system to follow emerging trends. Hedging is permitted to maintain opposing positions if enabled. Risk controls are integral, featuring automatic lot sizing proportional to account balance, equity, or free margin to cap exposure per trade, combined with fixed-point or ATR-based stop-loss and take-profit settings for volatility-responsive protection.

Our plan involves loading the NRTR channel indicator to detect support transitions for signals, a free indicator provided by MQL5 which is our main feature here, enforcing entry rules with position caps and hedging, computing adaptive sizes and stops via risk parameters, and implementing trailing and virtual closure routines for active oversight, yielding a reversal-oriented system that balances trend capture with disciplined risk mitigation. In brief, this framework provides a volatility-adapted trading approach centered on trailing reversals and customizable safeguards to ensure consistent operation. Here is a visualization of the strategy framework.

NRTR FRAMEWORK


Implementation in MQL5

To create the program in MQL5, open the MetaEditor, go to the Navigator, locate the Experts folder, click on the "New" tab, and follow the prompts to create the file. Once it is made, in the coding environment, we will need to declare some input parameters and global variables that we will use throughout the program.

//+------------------------------------------------------------------+
//|                     NRTR - Nick Rypock Trailing Reverse - EA.mq5 |
//|                           Copyright 2026, Allan Munene Mutiiria. |
//|                                   https://t.me/Forex_Algo_Trader |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, Allan Munene Mutiiria."
#property link "https://t.me/Forex_Algo_Trader"
#property version "1.00"

#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
#include <Trade\SymbolInfo.mqh>

//+------------------------------------------------------------------+
//| Enumerations                                                     |
//+------------------------------------------------------------------+
enum ENUM_RISK_BASE {
   RISK_BASE_EQUITY    = 1, // Equity
   RISK_BASE_BALANCE   = 2, // Balance
   RISK_BASE_FREEMARGIN = 3 // Free Margin
};

enum ENUM_RISK_DEFAULT_SIZE {
   RISK_DEFAULT_FIXED = 1, // Fixed
   RISK_DEFAULT_AUTO  = 2  // Auto
};

enum ENUM_MODE_SL {
   SL_FIXED = 0, // Fixed
   SL_AUTO  = 1  // Auto
};

enum ENUM_MODE_TP {
   TP_FIXED = 0, // Fixed
   TP_AUTO  = 1  // Auto
};

enum ENUM_TRAILING_TYPE {
   TRAILING_NONE          = 0, // None
   TRAILING_POINTS        = 1, // Points
   TRAILING_SUPPORT_LEVELS = 2 // Support Levels
};

//+------------------------------------------------------------------+
//| Inputs                                                           |
//+------------------------------------------------------------------+
input group "Risk Management Settings"
input ENUM_RISK_DEFAULT_SIZE RiskDefaultSize = RISK_DEFAULT_FIXED; // Default Risk Size
input double DefaultLotSize                  = 0.01;               // Default Lot Size
input ENUM_RISK_BASE RiskBase                = RISK_BASE_BALANCE;  // Risk Base
input int MaxRiskPerTrade                    = 5;                  // Max Risk Per Trade (%)
double MinLotSize                            = 0.01;               // Min Lot Size
double MaxLotSize                            = 100;                // Max Lot Size
input int MaxPositions                       = 1;                  // Max Positions
input bool HedgeMode                         = true;               // Hedge Mode
bool CloseOnReversalSignal                   = false;              // Close On Reversal Signal

input group "Stop-Loss and Take-Profit Settings"
input int DefaultStopLossPoints              = 300;                // Default Stop Loss (Points)
input int DefaultTakeProfitPoints            = 300;                // Default Take Profit (Points)
input bool CloseOnStopLossHit                = false;              // Close On Stop Loss Hit
input bool CloseOnTakeProfitHit              = false;              // Close On Take Profit Hit

input group "Additional Settings"
input int MagicNumber                        = 1234567890;         // Magic Number

input group "Trailing Stop Loss"
input ENUM_TRAILING_TYPE TrailingType        = TRAILING_POINTS;    // Trailing Type
input ushort TrailingFrequencySeconds        = 10;                 // Trailing Frequency (Seconds)
input ushort SignalCheckFrequencySeconds     = 10;                 // Signal Check Frequency (Seconds)
input ushort TrailingStopPoints              = 120;                // Trailing Stop (Points)
input ushort TrailingStepPoints              = 100;                // Trailing Step (Points)
input ushort BreakEvenPoints                 = 10;                 // Break Even (Points)
input ushort BreakEvenTriggerPoints          = 30;                 // Break Even Trigger (Points)

input group "NRTR Channel Settings"
input int NRTR_ATR_Period                    = 40;                 // NRTR ATR Period
input double NRTR_Multiplier                 = 2.0;                // NRTR Multiplier
input bool NRTR_Show_Price_Label             = true;               // Show Price Label

//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+
double CurrentStopLossPoints = DefaultStopLossPoints;              //--- Initialize current SL points
double CurrentTakeProfitPoints = DefaultTakeProfitPoints;          //--- Initialize current TP points
double CurrentTrailingStopPoints = TrailingStopPoints;             //--- Initialize current trailing stop points
double CurrentTrailingStepPoints = TrailingStepPoints;             //--- Initialize current trailing step points
bool PrintLog = false;                                             //--- Set print log flag
datetime LastTrailingTime = 0;                                     //--- Initialize last trailing time
ENUM_MODE_SL StopLossMode = SL_FIXED;                              //--- Set SL mode
ENUM_MODE_TP TakeProfitMode = TP_FIXED;                            //--- Set TP mode

double AtrMultiplierForStopLoss = 2;                               //--- Set ATR multiplier for SL
double AtrMultiplierForTakeProfit = 3;                             //--- Set ATR multiplier for TP

CTrade TradeObject;                                                //--- Declare trade object
CSymbolInfo SymbolInfo;                                            //--- Declare symbol info
int TrendIndicatorHandle = -1;                                     //--- Initialize trend handle
double CurrentTrendValue, PreviousTrendValue;                      //--- Declare trend values

double CurrentTrendDirection, PreviousTrendDirection;              //--- Declare trend directions
double CurrentUpSupport, PreviousUpSupport;                        //--- Declare up supports
double CurrentDownSupport, PreviousDownSupport;                    //--- Declare down supports
double CurrentNrtrAtr, PreviousNrtrAtr;                            //--- Declare NRTR ATR values

We begin the implementation by including essential MQL5 libraries with "#include <Trade\Trade.mqh>", "#include <Trade\PositionInfo.mqh>", and "#include <Trade\SymbolInfo.mqh>" to access classes for handling trade operations, position information, and symbol details. We then define several enumerations to categorize user options systematically. The "ENUM_RISK_BASE" enumeration provides selections for risk calculations based on equity, balance, or free margin. We create "ENUM_RISK_DEFAULT_SIZE" to choose between fixed or automatic lot sizing. For stop-loss and take-profit, we establish "ENUM_MODE_SL" and "ENUM_MODE_TP" with options for fixed or auto modes. Additionally, we set up "ENUM_TRAILING_TYPE" to define trailing stop varieties: none, points-based, or aligned with support levels.

Next, we organize user inputs into groups for better usability. In the risk management section, we include "RiskDefaultSize" for the default sizing method, "DefaultLotSize" as the fixed volume value, "RiskBase" for the computation foundation, "MaxRiskPerTrade" as a percentage limit, minimum and maximum lot sizes (defined as globals), "MaxPositions" to cap open trades, "HedgeMode" to allow hedging, and "CloseOnReversalSignal" for exiting on opposite signals. For stop-loss and take-profit configurations, we add "DefaultStopLossPoints" and "DefaultTakeProfitPoints" in points, along with booleans to enable closures upon hitting these levels.

In additional settings, we provide a "MagicNumber" for unique trade identification. We incorporate trailing stop parameters such as "TrailingType", frequencies in seconds for trailing updates and signal checks, "TrailingStopPoints", "TrailingStepPoints", "BreakEvenPoints", and "BreakEvenTriggerPoints". The NRTR channel group features "NRTR_ATR_Period", "NRTR_Multiplier", and "NRTR_Show_Price_Label" to customize the indicator directly from our program.

Finally, we declare global variables to initialize key values like current points for stops and trailing, a logging flag, the last trailing timestamp, modes for stop-loss and take-profit, ATR multipliers for dynamic levels, and instances of CTrade and CSymbolInfo objects. We set the trend indicator handle to -1 initially, alongside doubles for storing current and previous trend values, directions, support levels, and NRTR ATR readings. Next, we will initialize the indicator. We intend to always use functions and all the functions when necessary to make the code organized and modular.

//+------------------------------------------------------------------+
//| Initialize expert                                                |
//+------------------------------------------------------------------+
int OnInit() {
   if (!PreInitChecks()) return INIT_FAILED;                     //--- Perform pre-init checks
   SymbolInfo.Name(Symbol());                                    //--- Set symbol name
   if (!InitIndicatorHandles()) return INIT_FAILED;              //--- Initialize indicator handles
   InitTradeObject();                                            //--- Initialize trade object
   return INIT_SUCCEEDED;                                        //--- Return success
}

//+------------------------------------------------------------------+
//| Perform pre-init checks                                          |
//+------------------------------------------------------------------+
bool PreInitChecks() {
   if (MaxLotSize < MinLotSize) {                                //--- Check lot sizes
      Print("MaxLotSize cannot be less than MinLotSize");        //--- Print error
      return false;                                              //--- Return failure
   }
   return true;                                                  //--- Return success
}

//+------------------------------------------------------------------+
//| Initialize indicator handles                                     |
//+------------------------------------------------------------------+
bool InitIndicatorHandles() {
   TrendIndicatorHandle = iCustom(_Symbol, _Period, "Free Indicators\\NRTR Channel", NRTR_ATR_Period, NRTR_Multiplier, NRTR_Show_Price_Label); //--- Create NRTR handle
   if (TrendIndicatorHandle == INVALID_HANDLE) {                 //--- Check handle
      PrintFormat("Error creating NRTR handle - %d", GetLastError()); //--- Print error
      return false;                                              //--- Return failure
   }
   return true;                                                  //--- Return success
}

//+------------------------------------------------------------------+
//| Initialize trade object                                          |
//+------------------------------------------------------------------+
void InitTradeObject() {
   TradeObject.SetExpertMagicNumber(MagicNumber);                //--- Set magic number
}

In the OnInit event handler, we start by calling "PreInitChecks" to validate initial conditions, returning INIT_FAILED if it fails to ensure the program does not proceed with invalid settings. We then set the symbol name in the "SymbolInfo" object using the current symbol. Next, we invoke "InitIndicatorHandles" to load the NRTR channel indicator, returning "INIT_FAILED" on error. We initialize the trade object by calling "InitTradeObject", and finally return INIT_SUCCEEDED to confirm successful setup.

As for the functions that we used, the "PreInitChecks" function verifies that "MaxLotSize" is not less than "MinLotSize", printing an error message and returning false if violated, otherwise returning true to allow initialization to continue. Within "InitIndicatorHandles", we create the "TrendIndicatorHandle" using iCustom with the symbol, period, indicator path, and parameters like "NRTR_ATR_Period", "NRTR_Multiplier", and "NRTR_Show_Price_Label". If the handle is INVALID_HANDLE, we print an error with GetLastError and return false; otherwise, we return true. The "InitTradeObject" function configures the "TradeObject" by setting its magic number to "MagicNumber" for identifying program-specific trades. It is very crucial that you run the program to ensure the indicator is loaded successfully since it is the main source of our signals.

INITIAL INDICATOR LOAD TO THE PROGRAM

In our case, the indicator is loaded successfully. We can now continue to read the indicator buffers and define our signal generation logic. We will start with the data fetching logic.

//+------------------------------------------------------------------+
//| Fetch indicator data                                             |
//+------------------------------------------------------------------+
bool FetchIndicatorData() {
   double atrBuffer[];                                           //--- Declare ATR buffer as dynamic
   double trendBuffer[];                                         //--- Declare trend buffer as dynamic
   double upSupportBuffer[];                                     //--- Declare up support buffer as dynamic
   double downSupportBuffer[];                                   //--- Declare down support buffer as dynamic
   ArrayResize(atrBuffer, 2);                                    //--- Resize to 2
   ArrayResize(trendBuffer, 2);                                  //--- Resize to 2
   ArrayResize(upSupportBuffer, 2);                              //--- Resize to 2
   ArrayResize(downSupportBuffer, 2);                            //--- Resize to 2
   ArraySetAsSeries(atrBuffer, true);                            //--- Set as series
   ArraySetAsSeries(trendBuffer, true);                          //--- Set as series
   ArraySetAsSeries(upSupportBuffer, true);                      //--- Set as series
   ArraySetAsSeries(downSupportBuffer, true);                    //--- Set as series
   int copyCount;                                                //--- Declare copy count
   bool dataReady = false;                                       //--- Set data ready flag
   int maxAttempts = 5;                                          //--- Set max attempts
   int delayMs = 200;                                            //--- Set delay ms
   int attempt = 0;                                              //--- Initialize attempt
   while (!dataReady && attempt < maxAttempts) {                 //--- Loop until ready
      dataReady = true;                                          //--- Assume ready
      copyCount = CopyBuffer(TrendIndicatorHandle, 5, 1, 2, atrBuffer); //--- Copy ATR
      if (copyCount < 2 || atrBuffer[0] == EMPTY_VALUE) {        //--- Check ATR copy
         dataReady = false;                                      //--- Set not ready
      } else {                                                   //--- Set ATR values
         CurrentNrtrAtr = atrBuffer[0];                          //--- Set current ATR (recent)
         PreviousNrtrAtr = atrBuffer[1];                         //--- Set previous ATR (older)
      }
      copyCount = CopyBuffer(TrendIndicatorHandle, 4, 1, 2, trendBuffer); //--- Copy trend
      if (copyCount < 2) {                                       //--- Check trend copy
         dataReady = false;                                      //--- Set not ready
      } else {                                                   //--- Set trend directions
         CurrentTrendDirection = trendBuffer[0];                 //--- Set current direction (recent)
         PreviousTrendDirection = trendBuffer[1];                //--- Set previous direction (older)
      }
      copyCount = CopyBuffer(TrendIndicatorHandle, 1, 1, 2, upSupportBuffer); //--- Copy up support
      if (copyCount < 2) {                                       //--- Check up copy
         dataReady = false;                                      //--- Set not ready
      } else {                                                   //--- Set up supports
         CurrentUpSupport = upSupportBuffer[0];                  //--- Set current up (recent)
         PreviousUpSupport = upSupportBuffer[1];                 //--- Set previous up (older)
      }
      copyCount = CopyBuffer(TrendIndicatorHandle, 2, 1, 2, downSupportBuffer); //--- Copy down support
      if (copyCount < 2) {                                       //--- Check down copy
         dataReady = false;                                      //--- Set not ready
      } else {                                                   //--- Set down supports
         CurrentDownSupport = downSupportBuffer[0];              //--- Set current down (recent)
         PreviousDownSupport = downSupportBuffer[1];             //--- Set previous down (older)
      }
      if (dataReady) {                                           //--- Check ready
         CurrentTrendValue = (CurrentTrendDirection > 0) ? CurrentUpSupport : ((CurrentTrendDirection < 0) ? CurrentDownSupport : EMPTY_VALUE); //--- Set current trend value
         PreviousTrendValue = (PreviousTrendDirection > 0) ? PreviousUpSupport : ((PreviousTrendDirection < 0) ? PreviousDownSupport : EMPTY_VALUE); //--- Set previous trend value
         if (CurrentTrendValue == EMPTY_VALUE || PreviousTrendValue == EMPTY_VALUE) dataReady = false; //--- Check values
      }
      attempt++;                                                 //--- Increment attempt
      Sleep(delayMs);                                            //--- Delay
   }
   if (!dataReady) {                                             //--- Check final ready
      Print("Failed to fetch indicator data");                   //--- Print error
      return false;                                              //--- Return failure
   }
   return true;                                                  //--- Return success
}

We define the "FetchIndicatorData" function to retrieve and store values from the NRTR channel indicator buffers reliably. To begin, we declare dynamic arrays for "atrBuffer", "trendBuffer", "upSupportBuffer", and "downSupportBuffer", resizing each to hold two elements with ArrayResize and setting them as series arrays using ArraySetAsSeries so the most recent data is at index 0. We initialize variables for copy counts, a data ready flag, a maximum retry attempts at 5, a delay of 200 milliseconds, and an attempt counter starting at 0. In a while loop that continues until data is ready or attempts are exhausted, we assume readiness, then use CopyBuffer to fetch the last two bars' data from specific indicator buffers: buffer 5 for ATR values, buffer 4 for trend directions, buffer 1 for up supports, and buffer 2 for down supports.

If any copy fails (less than 2 elements) or the recent ATR is EMPTY_VALUE, we mark the data as not ready. Otherwise, we assign current (recent, index 0) and previous (older, index 1) values to globals like "CurrentNrtrAtr", "PreviousNrtrAtr", "CurrentTrendDirection", "PreviousTrendDirection", "CurrentUpSupport", "PreviousUpSupport", "CurrentDownSupport", and "PreviousDownSupport". Once all buffers are copied successfully, we compute "CurrentTrendValue" as "CurrentUpSupport" if direction is positive, "CurrentDownSupport" if negative, or "EMPTY_VALUE" otherwise, and similarly for "PreviousTrendValue". If either trend value is "EMPTY_VALUE", we reset readiness.

We increment the attempt counter and pause with Sleep before retrying. If data remains unavailable after retries, we print an error and return false; otherwise, we return true to confirm successful data retrieval. After fetching the data, we can proceed to analyse it for signal generation. First, we will define some helper functions as follows.

//+------------------------------------------------------------------+
//| Count open positions                                             |
//+------------------------------------------------------------------+
int CountOpenPositions() {
   int count = 0;                                                //--- Initialize count
   int totalPositions = PositionsTotal();                        //--- Get total positions
   for (int i = 0; i < totalPositions; i++) {                    //--- Loop positions
      if (PositionGetSymbol(i) != Symbol()) continue;            //--- Skip wrong symbol
      if (MagicNumber != 0 && PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue; //--- Skip wrong magic
      count++;                                                   //--- Increment count
   }
   return count;                                                 //--- Return count
}

//+------------------------------------------------------------------+
//| Count open positions by type                                     |
//+------------------------------------------------------------------+
int CountOpenPositionsByType(ENUM_POSITION_TYPE positionType) {
   int count = 0;                                                //--- Initialize count
   int totalPositions = PositionsTotal();                        //--- Get total positions
   for (int i = 0; i < totalPositions; i++) {                    //--- Loop positions
      if (PositionGetSymbol(i) != Symbol()) continue;            //--- Skip wrong symbol
      if (MagicNumber != 0 && PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue; //--- Skip wrong magic
      if (PositionGetInteger(POSITION_TYPE) == positionType) count++; //--- Increment if match
   }
   return count;                                                 //--- Return count
}

//+------------------------------------------------------------------+
//| Open buy position                                                |
//+------------------------------------------------------------------+
bool OpenBuyPosition(string positionType) {
   double askPrice = SymbolInfoDouble(Symbol(), SYMBOL_ASK);               //--- Get ask price
   double stopLossPrice = CalculateStopLoss(ORDER_TYPE_BUY, askPrice);     //--- Calculate SL
   double takeProfitPrice = CalculateTakeProfit(ORDER_TYPE_BUY, askPrice); //--- Calculate TP
   double positionSize = CalculatePositionSize(stopLossPrice, askPrice);   //--- Calculate size
   string orderComment = positionType + " Buy";                            //--- Set comment
   if (!TradeObject.Buy(positionSize, Symbol(), 0, stopLossPrice, takeProfitPrice, orderComment)) { //--- Open buy
      PrintFormat("Failed to open BUY: %d", TradeObject.ResultRetcode());  //--- Print error
      return false;                                                        //--- Return failure
   }
   PrintFormat("%s Buy Position Opened Successfully", positionType);       //--- Print success
   return true;                                                            //--- Return success
}

//+------------------------------------------------------------------+
//| Open sell position                                               |
//+------------------------------------------------------------------+
bool OpenSellPosition(string positionType) {
   double bidPrice = SymbolInfoDouble(Symbol(), SYMBOL_BID);               //--- Get bid price
   double stopLossPrice = CalculateStopLoss(ORDER_TYPE_SELL, bidPrice);    //--- Calculate SL
   double takeProfitPrice = CalculateTakeProfit(ORDER_TYPE_SELL, bidPrice); //--- Calculate TP
   double positionSize = CalculatePositionSize(stopLossPrice, bidPrice);   //--- Calculate size
   string orderComment = positionType + " Sell";                           //--- Set comment
   if (!TradeObject.Sell(positionSize, Symbol(), 0, stopLossPrice, takeProfitPrice, orderComment)) { //--- Open sell
      PrintFormat("Failed to open SELL: %d", TradeObject.ResultRetcode()); //--- Print error
      return false;                                                        //--- Return failure
   }
   PrintFormat("%s Sell Position Opened Successfully", positionType);      //--- Print success
   return true;                                                            //--- Return success
}

//+------------------------------------------------------------------+
//| Close all buy positions                                          |
//+------------------------------------------------------------------+
void CloseAllBuyPositions() {
   int total = PositionsTotal();                                           //--- Get total positions
   for (int i = total - 1; i >= 0; i--) {                                  //--- Loop backward
      if (PositionGetSymbol(i) != Symbol()) continue;                      //--- Skip wrong symbol
      if (PositionGetInteger(POSITION_TYPE) != POSITION_TYPE_BUY) continue; //--- Skip non-buy
      if (PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;     //--- Skip wrong magic
      TradeObject.PositionClose(PositionGetInteger(POSITION_TICKET));      //--- Close position
   }
   Print("All Buy Positions Closed");                                      //--- Print closed
}

//+------------------------------------------------------------------+
//| Close all sell positions                                         |
//+------------------------------------------------------------------+
void CloseAllSellPositions() {
   int total = PositionsTotal();                                           //--- Get total positions
   for (int i = total - 1; i >= 0; i--) {                                  //--- Loop backward
      if (PositionGetSymbol(i) != Symbol()) continue;                      //--- Skip wrong symbol
      if (PositionGetInteger(POSITION_TYPE) != POSITION_TYPE_SELL) continue; //--- Skip non-sell
      if (PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;     //--- Skip wrong magic
      TradeObject.PositionClose(PositionGetInteger(POSITION_TICKET));      //--- Close position
   }
   Print("All Sell Positions Closed");                                     //--- Print closed
}

//+------------------------------------------------------------------+
//| Close all positions                                              |
//+------------------------------------------------------------------+
void CloseAllPositions() {
   int total = PositionsTotal();                                           //--- Get total positions
   for (int i = total - 1; i >= 0; i--) {                                  //--- Loop backward
      if (PositionGetSymbol(i) != Symbol()) continue;                      //--- Skip wrong symbol
      if (PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;     //--- Skip wrong magic
      TradeObject.PositionClose(PositionGetInteger(POSITION_TICKET));      //--- Close position
   }
   Print("All Positions Closed");                                          //--- Print closed
}

//+------------------------------------------------------------------+
//| Calculate stop loss                                              |
//+------------------------------------------------------------------+
double CalculateStopLoss(ENUM_ORDER_TYPE orderType, double entryPrice) {
   double stopLossPrice = 0;                                               //--- Initialize SL price
   if (StopLossMode == SL_FIXED) {                                         //--- Check fixed mode
      if (DefaultStopLossPoints == 0) return 0;                            //--- Return zero if none
      double pointValue = SymbolInfoDouble(Symbol(), SYMBOL_POINT);        //--- Get point value
      if (orderType == ORDER_TYPE_BUY) stopLossPrice = entryPrice - DefaultStopLossPoints * pointValue; //--- Set buy SL
      if (orderType == ORDER_TYPE_SELL) stopLossPrice = entryPrice + DefaultStopLossPoints * pointValue; //--- Set sell SL
   } else {                                                                //--- Handle auto mode
      if (orderType == ORDER_TYPE_BUY) stopLossPrice = entryPrice - PreviousNrtrAtr * AtrMultiplierForStopLoss; //--- Set buy SL
      if (orderType == ORDER_TYPE_SELL) stopLossPrice = entryPrice + PreviousNrtrAtr * AtrMultiplierForStopLoss; //--- Set sell SL
   }
   return NormalizeDouble(stopLossPrice, _Digits);                         //--- Normalize SL
}

//+------------------------------------------------------------------+
//| Calculate take profit                                            |
//+------------------------------------------------------------------+
double CalculateTakeProfit(ENUM_ORDER_TYPE orderType, double entryPrice) {
   double takeProfitPrice = 0;                                             //--- Initialize TP price
   if (TakeProfitMode == TP_FIXED) {                                       //--- Check fixed mode
      if (DefaultTakeProfitPoints == 0) return 0;                          //--- Return zero if none
      double pointValue = SymbolInfoDouble(Symbol(), SYMBOL_POINT);        //--- Get point value
      if (orderType == ORDER_TYPE_BUY) takeProfitPrice = entryPrice + DefaultTakeProfitPoints * pointValue; //--- Set buy TP
      if (orderType == ORDER_TYPE_SELL) takeProfitPrice = entryPrice - DefaultTakeProfitPoints * pointValue; //--- Set sell TP
   } else {                                                                //--- Handle auto mode
      if (orderType == ORDER_TYPE_BUY) takeProfitPrice = entryPrice + PreviousNrtrAtr * AtrMultiplierForTakeProfit; //--- Set buy TP
      if (orderType == ORDER_TYPE_SELL) takeProfitPrice = entryPrice - PreviousNrtrAtr * AtrMultiplierForTakeProfit; //--- Set sell TP
   }
   return NormalizeDouble(takeProfitPrice, _Digits);                       //--- Normalize TP
}

//+------------------------------------------------------------------+
//| Calculate position size                                          |
//+------------------------------------------------------------------+
double CalculatePositionSize(double stopLossPrice, double entryPrice) {
   double size = DefaultLotSize;                                          //--- Set default size
   if (RiskDefaultSize == RISK_DEFAULT_AUTO) {                            //--- Check auto risk
      if (stopLossPrice == 0) stopLossPrice = 200;                        //--- Set default SL if zero
      double riskBaseAmount = 0;                                          //--- Initialize base amount
      double tickValue = SymbolInfoDouble(Symbol(), SYMBOL_TRADE_TICK_VALUE); //--- Get tick value
      if (RiskBase == RISK_BASE_BALANCE) riskBaseAmount = AccountInfoDouble(ACCOUNT_BALANCE); //--- Set balance base
      else if (RiskBase == RISK_BASE_EQUITY) riskBaseAmount = AccountInfoDouble(ACCOUNT_EQUITY); //--- Set equity base
      else if (RiskBase == RISK_BASE_FREEMARGIN) riskBaseAmount = AccountInfoDouble(ACCOUNT_MARGIN_FREE); //--- Set free margin base
      double stopLossDistancePoints = MathAbs(entryPrice - stopLossPrice) / SymbolInfoDouble(Symbol(), SYMBOL_POINT); //--- Compute distance
      size = (riskBaseAmount * MaxRiskPerTrade / 100) / (stopLossDistancePoints * tickValue); //--- Compute size
   }
   double lotStep = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_STEP);       //--- Get lot step
   double maxLot = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MAX);         //--- Get max lot
   double minLot = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN);         //--- Get min lot
   size = MathFloor(size / lotStep) * lotStep;                            //--- Step size
   if (size > MaxLotSize) size = MaxLotSize;                              //--- Clamp max size
   if (size > maxLot) size = maxLot;                                      //--- Clamp symbol max
   if (size < MinLotSize || size < minLot) size = 0;                      //--- Clamp min size
   return size;                                                           //--- Return size
}

For the helper functions, we create the "CountOpenPositions" function to tally all open positions matching the current symbol and magic number. Inside, we initialize a counter, retrieve the total positions with PositionsTotal, and loop from 0 to that total, using PositionGetSymbol to skip mismatched symbols and PositionGetInteger for POSITION_MAGIC to filter by "MagicNumber" if set, incrementing the count for valid ones before returning it. Similarly, the "CountOpenPositionsByType" function counts positions of a specific type, like buy or sell, following the same loop structure but adding a check with "PositionGetInteger" for "POSITION_TYPE" to match the input "positionType".

For opening trades, we define "OpenBuyPosition," which takes a "positionType" string for commenting. We fetch the ask price via SymbolInfoDouble with "SYMBOL_ASK", calculate stop-loss and take-profit using dedicated functions, determine the position size, and form a comment as "positionType + ' Buy'". We attempt to open the buy with "TradeObject.Buy", printing failure with the retcode if unsuccessful or success otherwise, and return the outcome. The "OpenSellPosition" mirrors this for sells, using bid price from SYMBOL_BID, calculating stops accordingly, and executing "TradeObject.Sell" with a "Sell" comment.

To manage closures, "CloseAllBuyPositions" loops backward from "PositionsTotal" minus 1 to 0 for safe deletion, skipping non-matching symbols, non-buy types via "POSITION_TYPE_BUY", and wrong magic, closing valid ones with "TradeObject.PositionClose" on the ticket from "POSITION_TICKET", and prints confirmation. "CloseAllSellPositions" does the same for sells, checking POSITION_TYPE_SELL. "CloseAllPositions" closes everything matching symbol and magic, without type filtering, and prints the result.

In "CalculateStopLoss", we compute the price based on the order type and entry. For fixed mode in "StopLossMode", if points are zero, we return zero; otherwise, we get the point value and subtract or add points times point for buy or sell. In auto mode, we adjust by subtracting or adding "PreviousNrtrAtr" times "AtrMultiplierForStopLoss", normalizing to digits. "CalculateTakeProfit" follows suit, using "TakeProfitMode" and "AtrMultiplierForTakeProfit" for auto adjustments. Finally, "CalculatePositionSize" starts with a default lot, but if auto risk, sets a default distance if stop-loss is zero, determines the risk base amount, gets tick value, computes distance in points, and calculates size as risk amount over (distance times tick value). We then step it to a lot step with MathFloor, clamp between min and max lots including symbol limits, and return the size. We can now comfortably use these functions to check for signals and open positions.

//+------------------------------------------------------------------+
//| Check for entry signals                                          |
//+------------------------------------------------------------------+
void CheckForEntrySignals() {
   bool buySignal = (CurrentUpSupport != EMPTY_VALUE) && (PreviousUpSupport == EMPTY_VALUE);      //--- Check buy signal (start of long support)
   bool sellSignal = (CurrentDownSupport != EMPTY_VALUE) && (PreviousDownSupport == EMPTY_VALUE); //--- Check sell signal (start of short support)
   if (buySignal) {                                                                               //--- Handle buy signal
      string currUpStr = DoubleToString(CurrentUpSupport, _Digits);
      string prevUpStr = (PreviousUpSupport == EMPTY_VALUE) ? "EMPTY" : DoubleToString(PreviousUpSupport, _Digits);
      PrintFormat("Buy Signal Detected: Start of Long Support (Buffer 1 Recent: %s, Older: %s)", currUpStr, prevUpStr); //--- Print buy signal
      if ((CountOpenPositionsByType(POSITION_TYPE_BUY) == 0 && HedgeMode) || CountOpenPositions() < MaxPositions) {     //--- Check open buys
         OpenBuyPosition("Initial Signal");                                                       //--- Open buy
      } else {                                                                                    //--- Handle rejected
         Print("Buy Trade Rejected: Maximum positions reached or hedge mode restricts");          //--- Print rejected
      }
   }
   if (sellSignal) {                                                                              //--- Handle sell signal
      string currDownStr = DoubleToString(CurrentDownSupport, _Digits);
      string prevDownStr = (PreviousDownSupport == EMPTY_VALUE) ? "EMPTY" : DoubleToString(PreviousDownSupport, _Digits);
      PrintFormat("Sell Signal Detected: Start of Short Support (Buffer 2 Recent: %s, Older: %s)", currDownStr, prevDownStr); //--- Print sell signal
      if ((CountOpenPositionsByType(POSITION_TYPE_SELL) == 0 && HedgeMode) || CountOpenPositions() < MaxPositions) {          //--- Check open sells
         OpenSellPosition("Initial Signal");                                                      //--- Open sell
      } else {                                                                                    //--- Handle rejected
         Print("Sell Trade Rejected: Maximum positions reached or hedge mode restricts");         //--- Print rejected
      }
   }
}

Here, we implement the "CheckForEntrySignals" function to detect and act on potential trade entries based on the NRTR channel transitions. In the function, we define a buy signal as true when "CurrentUpSupport" is not EMPTY_VALUE but "PreviousUpSupport" is, indicating the start of a long support level. Similarly, a sell signal triggers if "CurrentDownSupport" is valid while "PreviousDownSupport" is empty, marking the onset of a short support.

If a buy signal is detected, we convert the support values to strings (handling "EMPTY_VALUE" as "EMPTY" for the previous), print a formatted message detailing the detection from buffer 1, and check conditions: if hedging is enabled and no buys are open, or if total positions are below "MaxPositions", we call "OpenBuyPosition" with "Initial Signal"; otherwise, we print a rejection message. For a sell signal, we follow the same pattern: format and print details from buffer 2, verify if hedging allows no existing sells or if under the position limit, then open a sell via "OpenSellPosition" or print rejection. We can now call this function in the tick event handler to get the results.

//+------------------------------------------------------------------+
//| Handle tick event                                                |
//+------------------------------------------------------------------+
void OnTick() {
   ProcessEachTick();                                            //--- Process tick
}

//+------------------------------------------------------------------+
//| Process each tick                                                |
//+------------------------------------------------------------------+
void ProcessEachTick() {
   if (!FetchIndicatorData()) return;                            //--- Fetch indicator data

   static datetime lastBarTime = WRONG_VALUE;                    //--- Initialize last bar time
   datetime currentBarTime = iTime(Symbol(), Period(), 0);       //--- Get current bar time
   static int newBarTicks = 0;                                   //--- Initialize new bar ticks
   if (currentBarTime == lastBarTime) {                          //--- Check same bar
      newBarTicks++;                                             //--- Increment ticks
      if (newBarTicks > 1) return;                               //--- Skip if more than 1
   } else {                                                      //--- New bar
      newBarTicks = 0;                                           //--- Reset ticks
      lastBarTime = currentBarTime;                              //--- Update last time
   }
   CheckForEntrySignals();                                       //--- Check entry signals
}

We handle incoming ticks in the OnTick event handler by simply calling "ProcessEachTick" to centralize the logic for each price update. In "ProcessEachTick", we first attempt to retrieve fresh indicator data with "FetchIndicatorData", exiting early if it fails to ensure decisions are based on valid information. To optimize processing and focus on new bars, we use a static "lastBarTime" initialized to WRONG_VALUE and fetch the current bar's open time via iTime for the symbol and period. A static "newBarTicks" starts at 0 to track ticks within the same bar.

If the current bar time matches "lastBarTime", indicating the same bar, we increment "newBarTicks" and return without further action if it exceeds 1, limiting unnecessary checks on subsequent ticks. Otherwise, for a new bar, we reset "newBarTicks" to 0, update "lastBarTime", and proceed. We then invoke "CheckForEntrySignals" to evaluate and potentially execute new trade entries based on the updated data. Upon compilation, we get the following outcome.

SIGNAL GENERATION AND TRADING

From the image, we can see that we create the signal and trade upon the signal generation instance. What now remains is handling the exit strategy, where we exit on opposing signals and handle virtual closures.

//+------------------------------------------------------------------+
//| Check for exit signals                                           |
//+------------------------------------------------------------------+
void CheckForExitSignals() {
   bool exitBuySignal = false;                                   //--- Initialize buy exit
   bool exitSellSignal = false;                                  //--- Initialize sell exit
   if (CloseOnReversalSignal) {                                  //--- Check reversal close
      exitBuySignal = (CurrentDownSupport != EMPTY_VALUE) && (PreviousDownSupport == EMPTY_VALUE); //--- Set buy exit (start of short)
      exitSellSignal = (CurrentUpSupport != EMPTY_VALUE) && (PreviousUpSupport == EMPTY_VALUE); //--- Set sell exit (start of long)
   }
   if (exitBuySignal) {                                          //--- Handle buy exit
      string currDownStr = DoubleToString(CurrentDownSupport, _Digits);
      string prevDownStr = (PreviousDownSupport == EMPTY_VALUE) ? "EMPTY" : DoubleToString(PreviousDownSupport, _Digits);
      PrintFormat("Exit Buy Signal Detected (Reversal): Start of Short Support (Buffer 2 Recent: %s, Older: %s)", currDownStr, prevDownStr); //--- Print buy exit
      CloseAllBuyPositions();                                    //--- Close buys
   }
   if (exitSellSignal) {                                         //--- Handle sell exit
      string currUpStr = DoubleToString(CurrentUpSupport, _Digits);
      string prevUpStr = (PreviousUpSupport == EMPTY_VALUE) ? "EMPTY" : DoubleToString(PreviousUpSupport, _Digits);
      PrintFormat("Exit Sell Signal Detected (Reversal): Start of Long Support (Buffer 1 Recent: %s, Older: %s)", currUpStr, prevUpStr); //--- Print sell exit
      CloseAllSellPositions();                                   //--- Close sells
   }
}

//+------------------------------------------------------------------+
//| Close on virtual take profit                                     |
//+------------------------------------------------------------------+
void CloseOnVirtualTakeProfit(ulong ticket) {
   if (!PositionSelectByTicket(ticket)) return;                  //--- Select position
   double entryPrice = PositionGetDouble(POSITION_PRICE_OPEN);   //--- Get entry price
   double virtualTakeProfit = 0;                                 //--- Initialize virtual TP
   double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);          //--- Get ask
   double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);          //--- Get bid
   if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { //--- Check buy
      virtualTakeProfit = entryPrice + DefaultTakeProfitPoints * SymbolInfoDouble(Symbol(), SYMBOL_POINT); //--- Set buy TP
      if (virtualTakeProfit <= bid && CloseOnTakeProfitHit) {    //--- Check hit
         TradeObject.PositionClose(ticket);                      //--- Close position
         PrintFormat("Position Closed on Virtual TP: Ticket %llu, Price Hit %.5f", ticket, virtualTakeProfit); //--- Print closed
      }
   } else {                                                      //--- Handle sell
      virtualTakeProfit = entryPrice - DefaultTakeProfitPoints * SymbolInfoDouble(Symbol(), SYMBOL_POINT); //--- Set sell TP
      if (virtualTakeProfit >= ask && CloseOnTakeProfitHit) {    //--- Check hit
         TradeObject.PositionClose(ticket);                      //--- Close position
         PrintFormat("Position Closed on Virtual TP: Ticket %llu, Price Hit %.5f", ticket, virtualTakeProfit); //--- Print closed
      }
   }
}

//+------------------------------------------------------------------+
//| Close on virtual stop loss                                       |
//+------------------------------------------------------------------+
void CloseOnVirtualStopLoss(ulong ticket) {
   if (!PositionSelectByTicket(ticket)) return;                  //--- Select position
   double entryPrice = PositionGetDouble(POSITION_PRICE_OPEN);   //--- Get entry price
   double virtualStopLoss = 0;                                   //--- Initialize virtual SL
   double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);          //--- Get ask
   double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);          //--- Get bid
   if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { //--- Check buy
      virtualStopLoss = entryPrice - DefaultStopLossPoints * SymbolInfoDouble(Symbol(), SYMBOL_POINT); //--- Set buy SL
      if (virtualStopLoss >= ask && CloseOnStopLossHit) {        //--- Check hit
         TradeObject.PositionClose(ticket);                      //--- Close position
         PrintFormat("Position Closed on Virtual SL: Ticket %llu, Price Hit %.5f", ticket, virtualStopLoss); //--- Print closed
      }
   } else {                                                      //--- Handle sell
      virtualStopLoss = entryPrice + DefaultStopLossPoints * SymbolInfoDouble(Symbol(), SYMBOL_POINT); //--- Set sell SL
      if (virtualStopLoss <= bid && CloseOnStopLossHit) {        //--- Check hit
         TradeObject.PositionClose(ticket);                      //--- Close position
         PrintFormat("Position Closed on Virtual SL: Ticket %llu, Price Hit %.5f", ticket, virtualStopLoss); //--- Print closed
      }
   }
}

//+------------------------------------------------------------------+
//| Handle virtual closures                                          |
//+------------------------------------------------------------------+
void HandleVirtualClosures() {
   int total = PositionsTotal();                                 //--- Get total positions
   for (int i = total - 1; i >= 0; i--) {                        //--- Loop backward
      if (PositionGetSymbol(i) == "") continue;                  //--- Skip invalid
      ulong ticket = PositionGetInteger(POSITION_TICKET);        //--- Get ticket
      if (PositionGetString(POSITION_SYMBOL) != Symbol() || PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue; //--- Skip wrong symbol/magic
      if (CloseOnStopLossHit) CloseOnVirtualStopLoss(ticket);    //--- Check virtual SL
      if (CloseOnTakeProfitHit) CloseOnVirtualTakeProfit(ticket); //--- Check virtual TP
   }
}

First, we develop the "CheckForExitSignals" function to monitor for conditions that warrant closing existing positions. Here, we initialize the boolean flags for buy and sell exits to false. If "CloseOnReversalSignal" is enabled, we set "exitBuySignal" to true upon detecting the start of a short support (current down support valid, previous empty), and "exitSellSignal" for the onset of a long support. When an exit buy signal occurs, we format strings for the down support values (handling EMPTY_VALUE as "EMPTY"), print a detailed message from buffer 2, and call "CloseAllBuyPositions" to terminate all buys. Similarly, for an exit sell signal, we print from buffer 1 and invoke the "CloseAllSellPositions" function.

The "CloseOnVirtualTakeProfit" function manages programmatic take-profit closures for a given ticket. We select the position with PositionSelectByTicket, exiting early if unsuccessful, then retrieve the entry price. For buys, we compute "virtualTakeProfit" by adding "DefaultTakeProfitPoints" times the point value to entry; for sells, we subtract it. We fetch ask and bid prices, and if the virtual level is hit (TP <= bid for buy, >= ask for sell) and "CloseOnTakeProfitHit" is true, we close the position via "TradeObject.PositionClose" and print confirmation with the ticket and hit price.

Likewise, "CloseOnVirtualStopLoss" handles virtual stop-losses. After selecting the position and getting entry, we calculate "virtualStopLoss" by subtracting points for buys or adding for sells. Using ask and bid, if hit (>= ask for buy SL, <= bid for sell) and "CloseOnStopLossHit" enabled, we close and print details. In "HandleVirtualClosures", we loop backward over all positions from "PositionsTotal" minus 1 to 0 for safe iteration. For each, if the symbol is invalid, we skip; we get the ticket and continue if the symbol or magic mismatches. If "CloseOnStopLossHit" is set, we call "CloseOnVirtualStopLoss"; if "CloseOnTakeProfitHit", we invoke "CloseOnVirtualTakeProfit" to apply virtual checks across open trades. We will also need to handle trailing types when enabled, either by points or by the support levels.

//+------------------------------------------------------------------+
//| Adjust to break even                                             |
//+------------------------------------------------------------------+
bool AdjustToBreakEven(ulong ticket) {
   if (TrailingType == TRAILING_NONE) return false;              //--- Check trailing type
   if (!PositionSelectByTicket(ticket)) return false;            //--- Select position
   MqlTradeRequest request = {};                                 //--- Declare request
   MqlTradeResult result = {};                                   //--- Declare result
   request.action = TRADE_ACTION_SLTP;                           //--- Set action SLTP
   request.position = ticket;                                    //--- Set position ticket
   long positionType = PositionGetInteger(POSITION_TYPE);        //--- Get type
   double entryPrice = PositionGetDouble(POSITION_PRICE_OPEN);   //--- Get entry price
   double currentStopLoss = PositionGetDouble(POSITION_SL);      //--- Get current SL
   double currentTakeProfit = PositionGetDouble(POSITION_TP);    //--- Get current TP
   string symbol = PositionGetString(POSITION_SYMBOL);           //--- Get symbol
   double point = SymbolInfoDouble(symbol, SYMBOL_POINT);        //--- Get point
   double currentPrice = (positionType == POSITION_TYPE_BUY) ? SymbolInfoDouble(symbol, SYMBOL_BID) : SymbolInfoDouble(symbol, SYMBOL_ASK); //--- Get current price
   double currentProfitPoints = (positionType == POSITION_TYPE_BUY) ? (currentPrice - entryPrice) / point : (entryPrice - currentPrice) / point; //--- Compute profit points
   double newStopLoss = 0;                                       //--- Initialize new SL
   if (currentProfitPoints <= 0) return false;                   //--- Skip if no profit

   switch (TrailingType) {                                       //--- Switch trailing type
   case TRAILING_POINTS: {                                       //--- Handle points
      if (currentProfitPoints >= BreakEvenTriggerPoints) {       //--- Check BE trigger
         double bePoints = BreakEvenPoints;                      //--- Set BE points
         newStopLoss = (positionType == POSITION_TYPE_BUY) ? NormalizeDouble(entryPrice + (bePoints * point), _Digits) : NormalizeDouble(entryPrice - (bePoints * point), _Digits); //--- Compute new SL
         if ((positionType == POSITION_TYPE_BUY && newStopLoss > currentStopLoss) || (positionType == POSITION_TYPE_SELL && newStopLoss < currentStopLoss)) { //--- Check improvement
            if (NormalizeDouble(newStopLoss, _Digits) != NormalizeDouble(currentStopLoss, _Digits)) { //--- Check different
               request.sl = newStopLoss;                            //--- Set SL
               request.tp = currentTakeProfit;                      //--- Set TP
               if (OrderSend(request, result)) {                    //--- Send order
                  PrintFormat("Breakeven SL Adjusted for %s Position: Ticket %llu, New SL = %.5f (Triggered at %.0f points profit)", (positionType == POSITION_TYPE_BUY) ? "Buy" : "Sell", ticket, newStopLoss, currentProfitPoints); //--- Print adjusted
                  return true;                                      //--- Return success
               }
            }
         }
      }
      if (currentProfitPoints >= CurrentTrailingStopPoints) {       //--- Check trailing trigger
         double trailPoints = currentProfitPoints - CurrentTrailingStepPoints; //--- Compute trail points
         newStopLoss = (positionType == POSITION_TYPE_BUY) ? NormalizeDouble(currentPrice - (CurrentTrailingStopPoints * point), _Digits) : NormalizeDouble(currentPrice + (CurrentTrailingStopPoints * point), _Digits); //--- Compute new SL
         if ((positionType == POSITION_TYPE_BUY && newStopLoss > currentStopLoss) || (positionType == POSITION_TYPE_SELL && newStopLoss < currentStopLoss)) { //--- Check improvement
            if (NormalizeDouble(newStopLoss, _Digits) != NormalizeDouble(currentStopLoss, _Digits)) { //--- Check different
               request.sl = newStopLoss;                            //--- Set SL
               request.tp = currentTakeProfit;                      //--- Set TP
               if (OrderSend(request, result)) {                    //--- Send order
                  PrintFormat("Trailing SL Adjusted for %s Position: Ticket %llu, New SL = %.5f (Current Profit = %.0f points)", (positionType == POSITION_TYPE_BUY) ? "Buy" : "Sell", ticket, newStopLoss, currentProfitPoints); //--- Print adjusted
                  return true;                                      //--- Return success
               }
            }
         }
      }
      break;
   }

   case TRAILING_SUPPORT_LEVELS: {                                 //--- Handle support levels
      double currentSupport = (positionType == POSITION_TYPE_BUY) ? CurrentUpSupport : CurrentDownSupport; //--- Get current support
      double previousSupport = (positionType == POSITION_TYPE_BUY) ? PreviousUpSupport : PreviousDownSupport; //--- Get previous support
      if (currentSupport == EMPTY_VALUE) return false;             //--- Skip if invalid support
      if (currentProfitPoints >= BreakEvenTriggerPoints) {         //--- Check BE trigger
         newStopLoss = currentSupport;                             //--- Set new SL to support
         bool isProfitable = (positionType == POSITION_TYPE_BUY && newStopLoss >= entryPrice) || (positionType == POSITION_TYPE_SELL && newStopLoss <= entryPrice); //--- Ensure beyond entry
         if (isProfitable && ((positionType == POSITION_TYPE_BUY && newStopLoss > currentStopLoss) || (positionType == POSITION_TYPE_SELL && newStopLoss < currentStopLoss))) { //--- Check improvement and profitable
            if (NormalizeDouble(newStopLoss, _Digits) != NormalizeDouble(currentStopLoss, _Digits)) { //--- Check different
               request.sl = newStopLoss;                            //--- Set SL
               request.tp = currentTakeProfit;                      //--- Set TP
               if (OrderSend(request, result)) {                    //--- Send order
                  PrintFormat("Breakeven SL Adjusted to Support Level for %s Position: Ticket %llu, New SL = %.5f (Triggered at %.0f points profit)", (positionType == POSITION_TYPE_BUY) ? "Buy" : "Sell", ticket, newStopLoss, currentProfitPoints); //--- Print adjusted
                  return true;                                      //--- Return success
               }
            }
         }
      }
      if ((positionType == POSITION_TYPE_BUY && currentSupport > previousSupport) || (positionType == POSITION_TYPE_SELL && currentSupport < previousSupport)) { //--- Check support moved
         newStopLoss = currentSupport;                              //--- Set new SL to support
         bool isProfitable = (positionType == POSITION_TYPE_BUY && newStopLoss >= entryPrice) || (positionType == POSITION_TYPE_SELL && newStopLoss <= entryPrice); //--- Ensure beyond entry
         if (isProfitable && ((positionType == POSITION_TYPE_BUY && newStopLoss > currentStopLoss) || (positionType == POSITION_TYPE_SELL && newStopLoss < currentStopLoss))) { //--- Check improvement and profitable
            if (NormalizeDouble(newStopLoss, _Digits) != NormalizeDouble(currentStopLoss, _Digits)) { //--- Check different
               request.sl = newStopLoss;                            //--- Set SL
               request.tp = currentTakeProfit;                      //--- Set TP
               if (OrderSend(request, result)) {                    //--- Send order
                  PrintFormat("SL Trailed to Support Level for %s Position: Ticket %llu, New SL = %.5f (Support Moved from %.5f to %.5f)", (positionType == POSITION_TYPE_BUY) ? "Buy" : "Sell", ticket, newStopLoss, previousSupport, currentSupport); //--- Print trailed
                  return true;                                      //--- Return success
               }
            }
         }
      }
      break;
   }

   default:
      break;
   }
   return false;                                                    //--- Return no adjustment
}

//+------------------------------------------------------------------+
//| Adjust all stop losses                                           |
//+------------------------------------------------------------------+
void AdjustAllStopLosses() {
   for (int i = PositionsTotal() - 1; i >= 0; i--) {                //--- Loop backward
      if (PositionGetSymbol(i) == "") continue;                     //--- Skip invalid
      ulong ticket = PositionGetInteger(POSITION_TICKET);           //--- Get ticket
      AdjustToBreakEven(ticket);                                    //--- Adjust to BE
   }
}

Here, we establish the "AdjustToBreakEven" function to manage trailing and break-even adjustments for a specific position identified by its ticket. If the "TrailingType" is "TRAILING_NONE", or if we cannot select the position with PositionSelectByTicket, we return false immediately. We prepare MqlTradeRequest and MqlTradeResult structures, set the request action to TRADE_ACTION_SLTP for modifying stop-loss and take-profit, and assign the position ticket. We retrieve the position type, entry price, current stop-loss, take-profit, symbol, and point value. The current price is bid for buys or ask for sells, and we calculate "currentProfitPoints" as the difference from entry normalized by point. If no profit exists, we skip and return false.

Using a switch on "TrailingType", for "TRAILING_POINTS", we first check if profit meets "BreakEvenTriggerPoints"; if so, we compute a new stop-loss at entry plus or minus "BreakEvenPoints" times point for buys or sells, normalized. We verify if this improves the current stop (higher for buys, lower for sells) and differs, then set request stop-loss and take-profit, sending with OrderSend and printing success if accepted, returning true. Still in points mode, if profit exceeds "CurrentTrailingStopPoints", we calculate a trailing stop based on current price minus or plus "CurrentTrailingStopPoints" times point, check improvement and difference, send the modification, print if successful, and return true.

For "TRAILING_SUPPORT_LEVELS", we get current and previous supports based on type, skipping if current is empty. If profit triggers break-even, we set a new stop to the current support, ensure it's profitable (at or beyond entry), check improvement, and if different, send and print on success. If the support has moved favorably (higher for buys, lower for sells), we update to the current support, confirm profitable and improved, send the request if different, print the trail details, and return true. By default, we break without action, returning false if no adjustment occurred. The "AdjustAllStopLosses" function iterates backward over all positions from PositionsTotal minus 1 to 0, skipping invalid symbols, retrieving each ticket, and calling "AdjustToBreakEven" on it to apply adjustments across open trades. We can now call all these functions in the management function to handle all tick events. The final function looks as follows.

//+------------------------------------------------------------------+
//| Process each tick                                                |
//+------------------------------------------------------------------+
void ProcessEachTick() {
   if (!FetchIndicatorData()) return;                            //--- Fetch indicator data
   int positionCount = CountOpenPositions();                     //--- Count positions
   if (positionCount > 0) {                                      //--- Check positions exist
      CheckForExitSignals();                                     //--- Check exit signals
   }
   static datetime lastBarTime = WRONG_VALUE;                    //--- Initialize last bar time
   datetime currentBarTime = iTime(Symbol(), Period(), 0);       //--- Get current bar time
   static int newBarTicks = 0;                                   //--- Initialize new bar ticks
   if (currentBarTime == lastBarTime) {                          //--- Check same bar
      newBarTicks++;                                             //--- Increment ticks
      if (newBarTicks > 1) return;                               //--- Skip if more than 1
   } else {                                                      //--- New bar
      newBarTicks = 0;                                           //--- Reset ticks
      lastBarTime = currentBarTime;                              //--- Update last time
   }
   CheckForEntrySignals();                                       //--- Check entry signals
   datetime currentTime = TimeCurrent();                         //--- Get current time
   if (currentTime - LastTrailingTime >= TrailingFrequencySeconds) { //--- Check trailing time
      AdjustAllStopLosses();                                     //--- Adjust SLs
      LastTrailingTime = currentTime;                            //--- Update trailing time
   }
   HandleVirtualClosures();                                      //--- Handle virtual closures
}

We just conditionally call our respective functions, and upon compilation, we get the following outcome.

NRTR TRAILING

From the image, we can see that we detect, trade, and manage the NRTR signal setups, hence achieving our objectives. The thing that remains is backtesting the program, and that is handled in the next section.


Backtesting

After thorough backtesting, we have the following results.

Backtest graph:

GRAPH

Backtest report:

REPORT


Conclusion

In conclusion, we’ve developed a Nick Rypock Trailing Reverse (NRTR) trading system in MQL5 that detects reversal signals via dynamic channels, supports trend-following entries with hedging for buys and sells, and integrates position limits for controlled exposure. We added risk features such as auto lot sizing based on equity, balance, or free margin, along with fixed or ATR-adjusted stop-loss and take-profit levels.

Disclaimer: This article is for educational purposes only. Trading carries significant financial risks, and market volatility may result in losses. Thorough backtesting and careful risk management are crucial before deploying this program in live markets.

With this NRTR reversal strategy, you’re equipped to pursue trend opportunities with hedging and trailing safeguards, ready for further refinement in your trading journey. Happy trading!

Triangular and Sawtooth Waves: Analytical Tools for Traders Triangular and Sawtooth Waves: Analytical Tools for Traders
Wave analysis is one of the methods used in technical analysis. This article focuses on two less conventional wave patterns: triangular and sawtooth waves. These formations underpin a number of technical indicators designed for market price analysis.
Price Action Analysis Toolkit Development (Part 57): Developing a Market State Classification Module in MQL5 Price Action Analysis Toolkit Development (Part 57): Developing a Market State Classification Module in MQL5
This article develops a market state classification module for MQL5 that interprets price behavior using completed price data. By examining volatility contraction, expansion, and structural consistency, the tool classifies market conditions as compression, transition, expansion, or trend, providing a clear contextual framework for price action analysis.
Features of Experts Advisors Features of Experts Advisors
Creation of expert advisors in the MetaTrader trading system has a number of features.
MQL5 Trading Tools (Part 14): Pixel-Perfect Scrollable Text Canvas with Antialiasing and Rounded Scrollbar MQL5 Trading Tools (Part 14): Pixel-Perfect Scrollable Text Canvas with Antialiasing and Rounded Scrollbar
In this article, we enhance the canvas-based price dashboard in MQL5 by adding a pixel-perfect scrollable text panel for usage guides, overcoming native scrolling limitations through custom antialiasing and a rounded scrollbar design with hover-expand functionality. The text panel supports themed backgrounds with opacity, dynamic line wrapping for content like instructions and contacts, and interactive navigation via up/down buttons, slider dragging, and mouse wheel scrolling within the body area.