Automating Trading Strategies in MQL5 (Part 47): Nick Rypock Trailing Reverse (NRTR) with Hedging Features
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:
- Examining the Nick Rypock Trailing Reverse Strategy and Its Components
- Implementation in MQL5
- Backtesting
- 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.

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.

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.

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.

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:

Backtest 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!
Warning: All rights to these materials are reserved by MetaQuotes Ltd. Copying or reprinting of these materials in whole or in part is prohibited.
This article was written by a user of the site and reflects their personal views. MetaQuotes Ltd is not responsible for the accuracy of the information presented, nor for any consequences resulting from the use of the solutions, strategies or recommendations described.
Triangular and Sawtooth Waves: Analytical Tools for Traders
Price Action Analysis Toolkit Development (Part 57): Developing a Market State Classification Module in MQL5
Features of Experts Advisors
MQL5 Trading Tools (Part 14): Pixel-Perfect Scrollable Text Canvas with Antialiasing and Rounded Scrollbar
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use