Hi scalpers,
When you are scalping Forex on 1-minute or 5-minute charts, execution speed is everything. A few seconds of hesitation or a sudden spread widening during a news spike can turn a winning scalp into a devastating drawdown. Managing your Stop Loss (SL) and Take Profit (TP) effectively is what separates consistent scalpers from those who blow accounts. Here is how to structure your risk strategy for high-frequency trading:
1. Why Percentage-Based Exits Beat Fixed Pips
Market volatility is dynamic. A 5-pip stop loss might provide plenty of breathing room during the quiet Asian session, but it will get hunted instantly during the London or New York market open. Setting your SL and TP as a percentage of the entry price automatically adapts your risk to the asset's current price level and typical intraday volatility.
2. Enforce the 1:1.5 Minimum Ratio
Because scalping win rates naturally fluctuate between 50% and 60%, an asymmetric risk-to-reward ratio is mandatory. Never risk 1.0% of your position value just to make 0.5%. Keep your TP distance at least 1.5 times greater than your SL distance so that one profitable trade wipes out two quick scratching losses.
3. Automate Your Exits Immediately
Never enter a scalp thinking, "I will manually drag my stop loss on the chart in a second". In fast-moving markets, manual execution is simply too slow. If your execution terminal does not attach automated stop levels instantly upon order placement, use an automated script to modify all open positions in milliseconds.
4. Beware of Broker Spread
Always factor your broker's spread into your TP level. If your target is 0.20% above the open price, add the spread distance to ensure the bid/ask line actually crosses your target and fills the order.
Mastering SL & TP in Forex Scalping: Why Percentage-Based Exits Save Accounts
Mastering SL & TP in Forex Scalping: Why Percentage-Based Exits Save Accounts
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Mastering SL & TP in Forex Scalping: Why Percentage-Based Exits Save Accounts
MT4 Script: Auto-Update SL & TP by Percentage
This MQL4 script iterates through all your currently open market orders (Buys and Sells) and instantly updates their Stop Loss and Take Profit based on a specified percentage distance from the execution price.
How to Install and Use:
Open MetaTrader 4 and press F4 to launch MetaEditor.
In the Navigator panel, right-click Scripts -> New File -> Script, and name it Modify_SL_TP_Percentage.
Paste the code below, click Compile, and return to MT4.
Double-click the script in your MT4 Navigator panel (or drag it onto your chart) to instantly update your open positions.
Script Highlights:
1) Error 1 Protection: The script automatically checks if the order already has the exact target SL and TP levels. If they match, it skips modification to prevent MT4 server spam and ERR_NO_RESULT errors.
2) Symbol Filtering: By leaving OnlyCurrentSymbol = true, you can safely drop the script onto a specific chart (e.g., EURUSD) without accidentally modifying your open trades on other currency pairs.
3) Precision Normalization: Uses NormalizeDouble() with the broker's specific market digits to ensure the calculated percentage prices conform perfectly to 3-digit or 5-digit broker pricing models.
This MQL4 script iterates through all your currently open market orders (Buys and Sells) and instantly updates their Stop Loss and Take Profit based on a specified percentage distance from the execution price.
How to Install and Use:
Open MetaTrader 4 and press F4 to launch MetaEditor.
In the Navigator panel, right-click Scripts -> New File -> Script, and name it Modify_SL_TP_Percentage.
Paste the code below, click Compile, and return to MT4.
Double-click the script in your MT4 Navigator panel (or drag it onto your chart) to instantly update your open positions.
Code: Select all
//+------------------------------------------------------------------+
//| Modify_SL_TP_Percentage.mq4 |
//| Copyright 2026, Scalping Script |
//+------------------------------------------------------------------+
#property copyright "Open Source"
#property link ""
#property version "1.00"
#property strict
#property show_confirm // Shows a pop-up confirmation box before running
//--- Input Parameters
extern double SL_Percent = 0.15; // Stop Loss distance in % from Open Price
extern double TP_Percent = 0.30; // Take Profit distance in % from Open Price
extern bool OnlyCurrentSymbol = true; // Apply only to the active chart symbol
//+------------------------------------------------------------------+
//| Script program start function |
//+------------------------------------------------------------------+
void OnStart()
{
int total = OrdersTotal();
int modifiedCount = 0;
// Loop backwards through all open orders
for(int i = total - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
// Filter by symbol if required
if(OnlyCurrentSymbol && OrderSymbol() != Symbol())
continue;
// Only modify live market orders (Buy and Sell)
if(OrderType() == OP_BUY || OrderType() == OP_SELL)
{
double openPrice = OrderOpenPrice();
double newSL = 0;
double newTP = 0;
// Get the exact point digits for the instrument
int digits = (int)MarketInfo(OrderSymbol(), MODE_DIGITS);
if(OrderType() == OP_BUY)
{
if(SL_Percent > 0) newSL = NormalizeDouble(openPrice * (1.0 - (SL_Percent / 100.0)), digits);
if(TP_Percent > 0) newTP = NormalizeDouble(openPrice * (1.0 + (TP_Percent / 100.0)), digits);
}
else if(OrderType() == OP_SELL)
{
if(SL_Percent > 0) newSL = NormalizeDouble(openPrice * (1.0 + (SL_Percent / 100.0)), digits);
if(TP_Percent > 0) newTP = NormalizeDouble(openPrice * (1.0 - (TP_Percent / 100.0)), digits);
}
// Prevent Error 1 (ERR_NO_RESULT) by checking if values are already set
if(NormalizeDouble(OrderStopLoss(), digits) == newSL &&
NormalizeDouble(OrderTakeProfit(), digits) == newTP)
continue;
// Execute order modification
bool success = OrderModify(OrderTicket(), openPrice, newSL, newTP, 0, clrBlue);
if(success)
{
modifiedCount++;
Print("Order #", OrderTicket(), " modified successfully. New SL: ", newSL, " | New TP: ", newTP);
}
else
{
Print("Failed to modify Order #", OrderTicket(), ". Error code: ", GetLastError());
}
}
}
}
Alert("Script execution finished. Modified orders: ", modifiedCount);
}
//+------------------------------------------------------------------+1) Error 1 Protection: The script automatically checks if the order already has the exact target SL and TP levels. If they match, it skips modification to prevent MT4 server spam and ERR_NO_RESULT errors.
2) Symbol Filtering: By leaving OnlyCurrentSymbol = true, you can safely drop the script onto a specific chart (e.g., EURUSD) without accidentally modifying your open trades on other currency pairs.
3) Precision Normalization: Uses NormalizeDouble() with the broker's specific market digits to ensure the calculated percentage prices conform perfectly to 3-digit or 5-digit broker pricing models.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Mastering SL & TP in Forex Scalping: Why Percentage-Based Exits Save Accounts
Complete MQL4 Expert Advisor Code
This EA performs a two-stage process on every tick:
Initial Protection: Instantly attaches your percentage-based SL and TP to any unprotected order opened manually or by another script.
Dynamic Trailing Stop: Continuously trails the stop loss behind the current price by a defined percentage once the trade moves into profit, stepping up only in discrete increments to respect broker rate limits.
How the Production Safeguards Work
1. The Trailing Step (Trail_Step_Percent)
In high-volatility scalping, prices fluctuate by micro-points every millisecond. Without a trailing step, an EA will bombard your broker with OrderModify() requests for every 0.00001 price move. By defaulting Trail_Step_Percent to 0.02%, the EA waits until price moves significantly in your favor before moving the stop loss again. This keeps your execution logs clean and prevents server throttling.
2. Break-Even Validation (targetSL > openPrice)
The trailing stop logic is engineered specifically for scalping preservation: it will not begin trailing your stop loss until the calculated target stop has crossed better than break-even. This ensures that once trailing activates, a reversal cannot result in a principal loss (excluding severe slippage).
3. Broker StopLevel Compliance (MODE_STOPLEVEL)
Every MT4 broker dictates a minimum distance (in points) that pending orders or stop losses must maintain away from the current Bid/Ask price. During volatile news spikes, brokers often widen this StopLevel. The EA dynamically reads MODE_STOPLEVEL on every tick and clamps the proposed SL/TP to the minimum legal distance if your percentage calculation falls too close to the current price, completely preventing Error 130 (ERR_INVALID_STOPS).
Installation Steps
In MetaTrader 4, press F4 to launch MetaEditor.
1) In the Navigator window, right-click Experts -> New File -> Expert Advisor (template), and name it Auto_SL_TP_Trailing_EA.
2) Replace the entire generated template code with the MQL4 code above and click Compile.
3) Return to MT4, open your target scalping chart (e.g., EURUSD 1M), and ensure Auto Trading is enabled in the top toolbar (green play icon).
4) Drag the compiled EA from the Navigator panel onto your chart. Ensure "Allow live trading" is checked under the Common tab in the settings pop-up.
This EA performs a two-stage process on every tick:
Initial Protection: Instantly attaches your percentage-based SL and TP to any unprotected order opened manually or by another script.
Dynamic Trailing Stop: Continuously trails the stop loss behind the current price by a defined percentage once the trade moves into profit, stepping up only in discrete increments to respect broker rate limits.
Code: Select all
//+------------------------------------------------------------------+
//| Auto_SL_TP_Trailing_EA.mq4 |
//| Copyright 2026, Scalping EA |
//+------------------------------------------------------------------+
#property copyright "Open Source"
#property link ""
#property version "1.10"
#property strict
//--- Input Parameters
extern string ___Initial_Protection___ = "--- Initial SL / TP Settings ---";
extern double SL_Percent = 0.15; // Initial Stop Loss in % from Open Price
extern double TP_Percent = 0.30; // Initial Take Profit in % from Open Price
extern string ___Trailing_Settings___ = "--- Trailing Stop Settings ---";
extern bool UseTrailingStop = true; // Enable Percentage-Based Trailing Stop
extern double Trail_Distance_Percent = 0.10; // Trailing distance in % behind current price
extern double Trail_Step_Percent = 0.02; // Minimum price change in % before updating SL
extern string ___Filter_Settings___ = "--- Trade Filters ---";
extern bool OnlyCurrentSymbol = true; // Apply only to the active chart symbol
extern int TargetMagicNumber = 0; // 0 = Manage manual trades & all EAs; >0 = Specific EA ID
//+------------------------------------------------------------------+
//| Expert tick function - Fires on every incoming price quote |
//+------------------------------------------------------------------+
void OnTick()
{
int total = OrdersTotal();
// Loop backwards through all open orders
for(int i = total - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
// 1. Filter by Symbol
if(OnlyCurrentSymbol && OrderSymbol() != Symbol())
continue;
// 2. Filter by Magic Number (0 manages manual trades)
if(TargetMagicNumber > 0 && OrderMagicNumber() != TargetMagicNumber)
continue;
// 3. Process only live market orders (Buy and Sell)
if(OrderType() == OP_BUY || OrderType() == OP_SELL)
{
ManageOrder();
}
}
}
}
//+------------------------------------------------------------------+
//| Manage individual open order (Initial SL/TP + Trailing Logic) |
//+------------------------------------------------------------------+
void ManageOrder()
{
double openPrice = OrderOpenPrice();
double currentSL = OrderStopLoss();
double currentTP = OrderTakeProfit();
int digits = (int)MarketInfo(OrderSymbol(), MODE_DIGITS);
double point = MarketInfo(OrderSymbol(), MODE_POINT);
// Broker's minimum stop distance in points (prevents Error 130)
int stopLevel = (int)MarketInfo(OrderSymbol(), MODE_STOPLEVEL);
double minStopDistance = stopLevel * point;
//-----------------------------------------------------------------
// PHASE 1: Apply Initial SL/TP if order is unprotected
//-----------------------------------------------------------------
if(currentSL == 0 || currentTP == 0)
{
double newSL = currentSL;
double newTP = currentTP;
if(OrderType() == OP_BUY)
{
if(currentSL == 0 && SL_Percent > 0)
newSL = NormalizeDouble(openPrice * (1.0 - (SL_Percent / 100.0)), digits);
if(currentTP == 0 && TP_Percent > 0)
newTP = NormalizeDouble(openPrice * (1.0 + (TP_Percent / 100.0)), digits);
// Verify against broker StopLevel constraints
if(newSL > 0 && (Bid - newSL) < minStopDistance) newSL = NormalizeDouble(Bid - minStopDistance, digits);
if(newTP > 0 && (newTP - Bid) < minStopDistance) newTP = NormalizeDouble(Bid + minStopDistance, digits);
}
else if(OrderType() == OP_SELL)
{
if(currentSL == 0 && SL_Percent > 0)
newSL = NormalizeDouble(openPrice * (1.0 + (SL_Percent / 100.0)), digits);
if(currentTP == 0 && TP_Percent > 0)
newTP = NormalizeDouble(openPrice * (1.0 - (TP_Percent / 100.0)), digits);
// Verify against broker StopLevel constraints
if(newSL > 0 && (newSL - Ask) < minStopDistance) newSL = NormalizeDouble(Ask + minStopDistance, digits);
if(newTP > 0 && (Ask - newTP) < minStopDistance) newTP = NormalizeDouble(Ask - minStopDistance, digits);
}
// Execute modification only if new values differ from current ones
if(NormalizeDouble(newSL, digits) != NormalizeDouble(currentSL, digits) ||
NormalizeDouble(newTP, digits) != NormalizeDouble(currentTP, digits))
{
if(OrderModify(OrderTicket(), openPrice, newSL, newTP, 0, clrBlue))
{
Print("Initial SL/TP attached to Order #", OrderTicket());
currentSL = newSL; // Update local variable for immediate trailing check
}
else
{
Print("Error attaching Initial SL/TP to Order #", OrderTicket(), " - Error Code: ", GetLastError());
return; // Abort further processing for this tick if modification failed
}
}
}
//-----------------------------------------------------------------
// PHASE 2: Percentage-Based Trailing Stop Logic
//-----------------------------------------------------------------
if(!UseTrailingStop || Trail_Distance_Percent <= 0) return;
// Calculate the minimum price change required before sending a modify request
double trailStep = NormalizeDouble(openPrice * (Trail_Step_Percent / 100.0), digits);
if(OrderType() == OP_BUY)
{
double targetSL = NormalizeDouble(Bid * (1.0 - (Trail_Distance_Percent / 100.0)), digits);
// Ensure target stop does not violate broker StopLevel
if((Bid - targetSL) < minStopDistance)
targetSL = NormalizeDouble(Bid - minStopDistance, digits);
// Trailing Conditions for BUY:
// 1. targetSL is above the trade OpenPrice (locks in break-even + profit)
// 2. targetSL is higher than currentSL by at least Trail_Step_Percent
if(targetSL > openPrice && (currentSL == 0 || targetSL >= (currentSL + trailStep)))
{
if(OrderModify(OrderTicket(), openPrice, targetSL, currentTP, 0, clrGreen))
{
Print("Trailing Stop advanced for BUY Order #", OrderTicket(), " -> New SL: ", targetSL);
}
else
{
Print("Error trailing BUY Order #", OrderTicket(), " - Error Code: ", GetLastError());
}
}
}
else if(OrderType() == OP_SELL)
{
double targetSL = NormalizeDouble(Ask * (1.0 + (Trail_Distance_Percent / 100.0)), digits);
// Ensure target stop does not violate broker StopLevel
if((targetSL - Ask) < minStopDistance)
targetSL = NormalizeDouble(Ask + minStopDistance, digits);
// Trailing Conditions for SELL:
// 1. targetSL is below the trade OpenPrice (locks in break-even + profit)
// 2. targetSL is lower than currentSL by at least Trail_Step_Percent
if(targetSL < openPrice && (currentSL == 0 || targetSL <= (currentSL - trailStep)))
{
if(OrderModify(OrderTicket(), openPrice, targetSL, currentTP, 0, clrRed))
{
Print("Trailing Stop advanced for SELL Order #", OrderTicket(), " -> New SL: ", targetSL);
}
else
{
Print("Error trailing SELL Order #", OrderTicket(), " - Error Code: ", GetLastError());
}
}
}
}
//+------------------------------------------------------------------+1. The Trailing Step (Trail_Step_Percent)
In high-volatility scalping, prices fluctuate by micro-points every millisecond. Without a trailing step, an EA will bombard your broker with OrderModify() requests for every 0.00001 price move. By defaulting Trail_Step_Percent to 0.02%, the EA waits until price moves significantly in your favor before moving the stop loss again. This keeps your execution logs clean and prevents server throttling.
2. Break-Even Validation (targetSL > openPrice)
The trailing stop logic is engineered specifically for scalping preservation: it will not begin trailing your stop loss until the calculated target stop has crossed better than break-even. This ensures that once trailing activates, a reversal cannot result in a principal loss (excluding severe slippage).
3. Broker StopLevel Compliance (MODE_STOPLEVEL)
Every MT4 broker dictates a minimum distance (in points) that pending orders or stop losses must maintain away from the current Bid/Ask price. During volatile news spikes, brokers often widen this StopLevel. The EA dynamically reads MODE_STOPLEVEL on every tick and clamps the proposed SL/TP to the minimum legal distance if your percentage calculation falls too close to the current price, completely preventing Error 130 (ERR_INVALID_STOPS).
Installation Steps
In MetaTrader 4, press F4 to launch MetaEditor.
1) In the Navigator window, right-click Experts -> New File -> Expert Advisor (template), and name it Auto_SL_TP_Trailing_EA.
2) Replace the entire generated template code with the MQL4 code above and click Compile.
3) Return to MT4, open your target scalping chart (e.g., EURUSD 1M), and ensure Auto Trading is enabled in the top toolbar (green play icon).
4) Drag the compiled EA from the Navigator panel onto your chart. Ensure "Allow live trading" is checked under the Common tab in the settings pop-up.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Mastering SL & TP in Forex Scalping: Why Percentage-Based Exits Save Accounts
For MT5:
o make this Expert Advisor run seamlessly in MT5—and ensure it is universally compatible with both Netting and Hedging account types—we utilize the official MQL5 Standard Trade Library (CTrade) and iterate over active positions using PositionsTotal().
Key MT5 Technical UpgradesFeatureHow It Works in MT5Universal Account CompatibilityBy passing the exact ticket ID into trade.PositionModify(ticket, SL, TP), this EA works flawlessly on both traditional Netting accounts (where multiple entries combine into one position) and Hedging accounts (where multiple individual trades run concurrently).CTrade Standard LibraryInstead of manually building raw MQL5 trade structures (MqlTradeRequest and MqlTradeResult), we use the robust CTrade wrapper class. It handles order filling modes and execution timeouts automatically.Input GroupingMT5 supports input group formatting, which visually organizes your EA parameters into clean, collapsible headers inside the MetaTrader 5 settings dialog.Advanced Error ReportingIf a modification fails due to broker slippage or quote freezing, the EA outputs exact descriptive strings via trade.ResultRetcodeDescription() (such as 10016: Invalid stops or 10004: Requote) directly to your Experts journal.
o make this Expert Advisor run seamlessly in MT5—and ensure it is universally compatible with both Netting and Hedging account types—we utilize the official MQL5 Standard Trade Library (CTrade) and iterate over active positions using PositionsTotal().
Code: Select all
//+------------------------------------------------------------------+
//| Auto_SL_TP_Trailing_EA_MT5.mq5 |
//| Copyright 2026, Scalping EA |
//+------------------------------------------------------------------+
#property copyright "Open Source"
#property link ""
#property version "1.00"
#property strict
// Include the official MQL5 Trade Library
#include <Trade\Trade.mqh>
CTrade trade;
//--- Input Parameters
input group "--- Initial SL / TP Settings ---"
input double SL_Percent = 0.15; // Initial Stop Loss in % from Open Price
input double TP_Percent = 0.30; // Initial Take Profit in % from Open Price
input group "--- Trailing Stop Settings ---"
input bool UseTrailingStop = true; // Enable Percentage-Based Trailing Stop
input double Trail_Distance_Percent = 0.10; // Trailing distance in % behind current price
input double Trail_Step_Percent = 0.02; // Minimum price change in % before updating SL
input group "--- Trade Filters ---"
input bool OnlyCurrentSymbol = true; // Apply only to the active chart symbol
input ulong TargetMagicNumber = 0; // 0 = Manage manual trades & all EAs; >0 = Specific EA ID
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Configure CTrade logging and magic number
trade.SetExpertMagicNumber(TargetMagicNumber);
trade.LogLevel(LOG_LEVEL_ERRORS); // Log only errors to keep journal clean
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function - Fires on every incoming price quote |
//+------------------------------------------------------------------+
void OnTick()
{
int total = PositionsTotal();
// Loop backwards through all open positions
for(int i = total - 1; i >= 0; i--)
{
// Select the position by index and retrieve its unique ticket
ulong ticket = PositionGetTicket(i);
if(ticket > 0)
{
string symbol = PositionGetString(POSITION_SYMBOL);
ulong magic = PositionGetInteger(POSITION_MAGIC);
// 1. Filter by Symbol
if(OnlyCurrentSymbol && symbol != _Symbol)
continue;
// 2. Filter by Magic Number (0 manages manual trades)
if(TargetMagicNumber > 0 && magic != TargetMagicNumber)
continue;
// Execute management logic for the selected position
ManagePosition(ticket, symbol);
}
}
}
//+------------------------------------------------------------------+
//| Manage individual open position (Initial SL/TP + Trailing Logic) |
//+------------------------------------------------------------------+
void ManagePosition(ulong ticket, string symbol)
{
long posType = PositionGetInteger(POSITION_TYPE);
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double currentSL = PositionGetDouble(POSITION_SL);
double currentTP = PositionGetDouble(POSITION_TP);
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
double bid = SymbolInfoDouble(symbol, SYMBOL_BID);
double ask = SymbolInfoDouble(symbol, SYMBOL_ASK);
// Broker's minimum stop distance in points (prevents Error 10016 / 10015)
long stopLevel = SymbolInfoInteger(symbol, SYMBOL_TRADE_STOPS_LEVEL);
double minStopDistance = stopLevel * point;
//-----------------------------------------------------------------
// PHASE 1: Apply Initial SL/TP if position is unprotected
//-----------------------------------------------------------------
if(currentSL == 0 || currentTP == 0)
{
double newSL = currentSL;
double newTP = currentTP;
if(posType == POSITION_TYPE_BUY)
{
if(currentSL == 0 && SL_Percent > 0)
newSL = NormalizeDouble(openPrice * (1.0 - (SL_Percent / 100.0)), digits);
if(currentTP == 0 && TP_Percent > 0)
newTP = NormalizeDouble(openPrice * (1.0 + (TP_Percent / 100.0)), digits);
// Verify against broker StopLevel constraints
if(newSL > 0 && (bid - newSL) < minStopDistance) newSL = NormalizeDouble(bid - minStopDistance, digits);
if(newTP > 0 && (newTP - bid) < minStopDistance) newTP = NormalizeDouble(bid + minStopDistance, digits);
}
else if(posType == POSITION_TYPE_SELL)
{
if(currentSL == 0 && SL_Percent > 0)
newSL = NormalizeDouble(openPrice * (1.0 + (SL_Percent / 100.0)), digits);
if(currentTP == 0 && TP_Percent > 0)
newTP = NormalizeDouble(openPrice * (1.0 - (TP_Percent / 100.0)), digits);
// Verify against broker StopLevel constraints
if(newSL > 0 && (newSL - ask) < minStopDistance) newSL = NormalizeDouble(ask + minStopDistance, digits);
if(newTP > 0 && (ask - newTP) < minStopDistance) newTP = NormalizeDouble(ask - minStopDistance, digits);
}
// Execute modification only if new values differ from current ones
if(NormalizeDouble(newSL, digits) != NormalizeDouble(currentSL, digits) ||
NormalizeDouble(newTP, digits) != NormalizeDouble(currentTP, digits))
{
if(trade.PositionModify(ticket, newSL, newTP))
{
Print("Initial SL/TP attached to Position #", ticket);
currentSL = newSL; // Update local variable for immediate trailing check
}
else
{
Print("Error attaching Initial SL/TP to Position #", ticket, " - Return Code: ", trade.ResultRetcode(), " (", trade.ResultRetcodeDescription(), ")");
return; // Abort further processing for this tick if modification failed
}
}
}
//-----------------------------------------------------------------
// PHASE 2: Percentage-Based Trailing Stop Logic
//-----------------------------------------------------------------
if(!UseTrailingStop || Trail_Distance_Percent <= 0) return;
// Calculate the minimum price change required before sending a modify request
double trailStep = NormalizeDouble(openPrice * (Trail_Step_Percent / 100.0), digits);
if(posType == POSITION_TYPE_BUY)
{
double targetSL = NormalizeDouble(bid * (1.0 - (Trail_Distance_Percent / 100.0)), digits);
// Ensure target stop does not violate broker StopLevel
if((bid - targetSL) < minStopDistance)
targetSL = NormalizeDouble(bid - minStopDistance, digits);
// Trailing Conditions for BUY:
// 1. targetSL is above the trade OpenPrice (locks in break-even + profit)
// 2. targetSL is higher than currentSL by at least Trail_Step_Percent
if(targetSL > openPrice && (currentSL == 0 || targetSL >= (currentSL + trailStep)))
{
if(trade.PositionModify(ticket, targetSL, currentTP))
{
Print("Trailing Stop advanced for BUY Position #", ticket, " -> New SL: ", targetSL);
}
else
{
Print("Error trailing BUY Position #", ticket, " - Return Code: ", trade.ResultRetcode(), " (", trade.ResultRetcodeDescription(), ")");
}
}
}
else if(posType == POSITION_TYPE_SELL)
{
double targetSL = NormalizeDouble(ask * (1.0 + (Trail_Distance_Percent / 100.0)), digits);
// Ensure target stop does not violate broker StopLevel
if((targetSL - ask) < minStopDistance)
targetSL = NormalizeDouble(ask + minStopDistance, digits);
// Trailing Conditions for SELL:
// 1. targetSL is below the trade OpenPrice (locks in break-even + profit)
// 2. targetSL is lower than currentSL by at least Trail_Step_Percent
if(targetSL < openPrice && (currentSL == 0 || targetSL <= (currentSL - trailStep)))
{
if(trade.PositionModify(ticket, targetSL, currentTP))
{
Print("Trailing Stop advanced for SELL Position #", ticket, " -> New SL: ", targetSL);
}
else
{
Print("Error trailing SELL Position #", ticket, " - Return Code: ", trade.ResultRetcode(), " (", trade.ResultRetcodeDescription(), ")");
}
}
}
}
//+------------------------------------------------------------------+Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Mastering SL & TP in Forex Scalping: Why Percentage-Based Exits Save Accounts
Complete C# cBot Code
This cBot mirrors the two-stage execution architecture of the MT4/MT5 versions: it applies initial percentage-based protection to unprotected positions and continuously manages a dynamic trailing stop based on live market quotes.
cTrader vs. MetaTrader Architectural Differences
FeatureHow It Works in cTrader (C#)Trade IdentificationInstead of an integer MagicNumber, cTrader tags positions using text strings called a Label. Leaving TargetLabel blank ("") allows the cBot to manage all manual trades and third-party scripts.Nullable Stop LevelsIn the cAlgo API, position.StopLoss and position.TakeProfit are nullable doubles (double?). A value of null explicitly confirms that no stop is attached, removing the need to check against 0.0.Cross-Symbol RoutingBy calling Symbols.GetSymbol(position.SymbolName), the cBot retrieves live Bids and Asks for any open position on your account, even if the script is running on a different chart.Clean Error HandlingThe ModifyPosition() method returns an explicit TradeResult object. If a modification is rejected by the exchange or broker, result.Error prints the exact reason directly to your cBot log.How to Install in cTraderOpen IC Markets cTrader and navigate to the Automate application tab on the left sidebar (symbolized by a robot icon).Click New cBot, name it AutoSlTpTrailingBot, and press Enter.In the central code editor window, select all existing boilerplate code and delete it.Paste the entire C# code block above into the editor.Click the Build button at the top of the editor (or press Ctrl + B). Ensure the build output at the bottom displays Build succeeded.On the left panel under your compiled AutoSlTpTrailingBot, click the + icon to add an instance to your desired chart (e.g., EURUSD 1m).In the parameters panel, adjust your percentage thresholds and click the green Play toggle to start automated trade management.
This cBot mirrors the two-stage execution architecture of the MT4/MT5 versions: it applies initial percentage-based protection to unprotected positions and continuously manages a dynamic trailing stop based on live market quotes.
Code: Select all
//+------------------------------------------------------------------+
//| AutoSlTpTrailingBot.cs |
//| Copyright 2026, cTrader Scalping cBot |
//+------------------------------------------------------------------+
using System;
using cAlgo.API;
using cAlgo.API.Collections;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
namespace cAlgo.Robots
{
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class AutoSlTpTrailingBot : Robot
{
//--- Initial SL / TP Settings
[Parameter("Initial Stop Loss (%)", Group = "Initial SL / TP Settings", DefaultValue = 0.15, MinValue = 0.0, Step = 0.01)]
public double SlPercent { get; set; }
[Parameter("Initial Take Profit (%)", Group = "Initial SL / TP Settings", DefaultValue = 0.30, MinValue = 0.0, Step = 0.01)]
public double TpPercent { get; set; }
//--- Trailing Stop Settings
[Parameter("Enable Trailing Stop", Group = "Trailing Stop Settings", DefaultValue = true)]
public bool UseTrailingStop { get; set; }
[Parameter("Trailing Distance (%)", Group = "Trailing Stop Settings", DefaultValue = 0.10, MinValue = 0.0, Step = 0.01)]
public double TrailDistancePercent { get; set; }
[Parameter("Trailing Step (%)", Group = "Trailing Stop Settings", DefaultValue = 0.02, MinValue = 0.0, Step = 0.01)]
public double TrailStepPercent { get; set; }
//--- Trade Filters
[Parameter("Only Current Symbol", Group = "Trade Filters", DefaultValue = true)]
public bool OnlyCurrentSymbol { get; set; }
[Parameter("Target Label (Empty = All)", Group = "Trade Filters", DefaultValue = "")]
public string TargetLabel { get; set; }
protected override void OnStart()
{
Print("Auto SL/TP Trailing cBot Initialized.");
}
protected override void OnTick()
{
// Iterate through all active positions on the account
foreach (var position in Positions)
{
// 1. Filter by Symbol if required
if (OnlyCurrentSymbol && position.SymbolName != SymbolName)
continue;
// 2. Filter by Position Label (cTrader's equivalent to Magic Number)
if (!string.IsNullOrEmpty(TargetLabel) && position.Label != TargetLabel)
continue;
ManagePosition(position);
}
}
private void ManagePosition(Position position)
{
// Retrieve exact symbol object for the trade (allows safe multi-currency management)
var symbol = Symbols.GetSymbol(position.SymbolName);
double openPrice = position.EntryPrice;
// In cTrader API, StopLoss and TakeProfit are nullable doubles (double?)
double currentSl = position.StopLoss ?? 0;
double currentTp = position.TakeProfit ?? 0;
int digits = symbol.Digits;
//-----------------------------------------------------------------
// PHASE 1: Apply Initial SL/TP if position is unprotected
//-----------------------------------------------------------------
if (currentSl == 0 || currentTp == 0)
{
double? newSl = position.StopLoss;
double? newTp = position.TakeProfit;
if (position.TradeType == TradeType.Buy)
{
if (currentSl == 0 && SlPercent > 0)
newSl = Math.Round(openPrice * (1.0 - (SlPercent / 100.0)), digits);
if (currentTp == 0 && TpPercent > 0)
newTp = Math.Round(openPrice * (1.0 + (TpPercent / 100.0)), digits);
}
else if (position.TradeType == TradeType.Sell)
{
if (currentSl == 0 && SlPercent > 0)
newSl = Math.Round(openPrice * (1.0 + (SlPercent / 100.0)), digits);
if (currentTp == 0 && TpPercent > 0)
newTp = Math.Round(openPrice * (1.0 - (TpPercent / 100.0)), digits);
}
// Modify only if calculated target values differ from current state
if (newSl != position.StopLoss || newTp != position.TakeProfit)
{
var result = ModifyPosition(position, newSl, newTp);
if (result.IsSuccessful)
{
Print("Initial SL/TP attached to Position #{0}", position.Id);
currentSl = newSl ?? 0;
}
else
{
Print("Error attaching SL/TP to Position #{0}: {1}", position.Id, result.Error);
return; // Abort further processing for this tick if modification failed
}
}
}
//-----------------------------------------------------------------
// PHASE 2: Percentage-Based Trailing Stop Logic
//-----------------------------------------------------------------
if (!UseTrailingStop || TrailDistancePercent <= 0)
return;
double trailStep = Math.Round(openPrice * (TrailStepPercent / 100.0), digits);
if (position.TradeType == TradeType.Buy)
{
double bid = symbol.Bid;
double targetSl = Math.Round(bid * (1.0 - (TrailDistancePercent / 100.0)), digits);
// Trailing Conditions for BUY:
// 1. targetSl is above openPrice (locks in break-even + profit)
// 2. targetSl is higher than currentSl by at least trailStep
if (targetSl > openPrice && (currentSl == 0 || targetSl >= (currentSl + trailStep)))
{
var result = ModifyPosition(position, targetSl, position.TakeProfit);
if (result.IsSuccessful)
{
Print("Trailing Stop advanced for BUY Position #{0} -> New SL: {1}", position.Id, targetSl);
}
else
{
Print("Error trailing BUY Position #{0}: {1}", position.Id, result.Error);
}
}
}
else if (position.TradeType == TradeType.Sell)
{
double ask = symbol.Ask;
double targetSl = Math.Round(ask * (1.0 + (TrailDistancePercent / 100.0)), digits);
// Trailing Conditions for SELL:
// 1. targetSl is below openPrice (locks in break-even + profit)
// 2. targetSl is lower than currentSl by at least trailStep
if (targetSl < openPrice && (currentSl == 0 || targetSl <= (currentSl - trailStep)))
{
var result = ModifyPosition(position, targetSl, position.TakeProfit);
if (result.IsSuccessful)
{
Print("Trailing Stop advanced for SELL Position #{0} -> New SL: {1}", position.Id, targetSl);
}
else
{
Print("Error trailing SELL Position #{0}: {1}", position.Id, result.Error);
}
}
}
}
}
}FeatureHow It Works in cTrader (C#)Trade IdentificationInstead of an integer MagicNumber, cTrader tags positions using text strings called a Label. Leaving TargetLabel blank ("") allows the cBot to manage all manual trades and third-party scripts.Nullable Stop LevelsIn the cAlgo API, position.StopLoss and position.TakeProfit are nullable doubles (double?). A value of null explicitly confirms that no stop is attached, removing the need to check against 0.0.Cross-Symbol RoutingBy calling Symbols.GetSymbol(position.SymbolName), the cBot retrieves live Bids and Asks for any open position on your account, even if the script is running on a different chart.Clean Error HandlingThe ModifyPosition() method returns an explicit TradeResult object. If a modification is rejected by the exchange or broker, result.Error prints the exact reason directly to your cBot log.How to Install in cTraderOpen IC Markets cTrader and navigate to the Automate application tab on the left sidebar (symbolized by a robot icon).Click New cBot, name it AutoSlTpTrailingBot, and press Enter.In the central code editor window, select all existing boilerplate code and delete it.Paste the entire C# code block above into the editor.Click the Build button at the top of the editor (or press Ctrl + B). Ensure the build output at the bottom displays Build succeeded.On the left panel under your compiled AutoSlTpTrailingBot, click the + icon to add an instance to your desired chart (e.g., EURUSD 1m).In the parameters panel, adjust your percentage thresholds and click the green Play toggle to start automated trade management.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.