MQL5 SL/TP Settings: How to Set Stop Loss, Take Profit, and Fix invalid stops

目次

1. Basics of Setting SL and TP in MQL5

Key points in this section
– The roles of SL (Stop Loss) and TP (Take Profit)
– The overall approach to setting them in MQL5
– Why beginners often get stuck


1.1 What Are SL (Stop Loss) and TP (Take Profit)?

SL (Stop Loss) is an order that automatically fixes a loss at a specified price.
TP (Take Profit) is an order that locks in profit at a specified price.

For example, the basic idea is as follows.

  • For BUY orders
    • SL: Set below the current price (loss cut)
    • TP: Set above the current price (take profit)
  • For SELL orders
    • SL: Set above the current price
    • TP: Set below the current price

These two settings are not just optional helpers. They are the core of risk management in an EA (Expert Advisor).
If SL is not set, especially when the market moves against the position, there is a risk that losses can expand without a defined limit.


1.2 Why SL/TP Settings Matter for Risk Management

In MQL5, SL/TP settings are not just a “feature.” They are essential controls for expected value and drawdown (maximum loss).

The main purposes are:

  • Fix the maximum possible loss (risk control)
  • Automate profit-taking (remove emotion)
  • Make EA behavior reproducible (rule-based trading)

For example, an EA without SL has these problems:

  • Unexpected growth in drawdown
  • Inconsistency with lot management
  • A gap between forward testing and live trading

On the other hand, setting appropriate SL/TP can help with:

  • More stable PF (Profit Factor)
  • Lower DD (Drawdown)
  • Better strategy reproducibility

These are practical benefits for real EA operation.


1.3 Overview of the 3 Ways to Set SL/TP in MQL5

In MQL5, there are three main ways to set SL/TP.

1. Set SL/TP when placing the order (most basic)

This method sets SL/TP at the same time the order is sent.

  • Use the sl / tp fields of OrderSend
  • This is the simplest and recommended method
request.sl = price - 100 * _Point;
request.tp = price + 100 * _Point;

2. Set or modify SL/TP after the order

This method sets or changes SL/TP for an already open position.

  • Use PositionModify
  • Useful for dynamic strategies that change conditions during a trade

3. Trailing stop

This method moves SL as the price moves.

  • It helps extend profits while limiting losses
  • It is a different concept from fixed TP

Common Sticking Points and Notes

Beginners most often get stuck on the following points.

■ BUY and SELL use opposite directions

  • BUY: SL is below, TP is above
  • SELL: SL is above, TP is below
    → If this is wrong, an invalid stops error occurs

■ Confusing pips and points

  • _Point: The minimum price unit
  • pips: The conventional unit used for each currency pair
    → This is the No. 1 cause of calculation mistakes

■ Sending an order without SL/TP

  • You can set them later, but the risk is high
    → As a rule, setting them at order time is safer

■ Ignoring broker restrictions

  • A minimum stop distance (StopLevel) exists
    → If SL/TP is too close, an error occurs

These values differ by environment, broker, and currency pair, so check them before execution.


Common Mistakes

  • Setting the SL/TP direction backward
  • Using a price range that is too small, causing order rejection
  • Not considering Ask/Bid
  • Writing price calculations with fixed values

2. Three Ways to Set SL and TP in MQL5

Key points in this section
– The difference between setting them at order time, modifying them later, and trailing
– Which method beginners should use first


2.1 Set SL/TP at the Same Time as the Order (Basic)

The most basic and recommended method is to set SL/TP at the same time as the order.
In MQL5, specify prices in the sl / tp fields of the MqlTradeRequest structure.

Procedure (basic flow)

  • Get the current price (Ask / Bid)
  • Calculate the SL / TP prices
  • Set them in request.sl / request.tp
  • Run OrderSend

Code example (BUY)

double price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

MqlTradeRequest request;
MqlTradeResult result;

ZeroMemory(request);

request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = 0.1;
request.type   = ORDER_TYPE_BUY;
request.price  = price;

// Set SL / TP
request.sl = price - 100 * _Point;
request.tp = price + 100 * _Point;

OrderSend(request, result);

Key points

  • _Point is the minimum price unit (for example, 0.00001)
  • For BUY, “SL is below and TP is above”
  • For SELL, the direction is reversed

Conclusion for beginners

Start with this method first
It is the simplest, has fewer errors, and is suitable for live operation.


2.2 Modify SL/TP After the Order (PositionModify)

This is the method of setting or changing SL/TP after a position already exists.
It is used for dynamic strategies where values change based on conditions.

Procedure

  • Select the target position (PositionSelect)
  • Get the current SL/TP
  • Run PositionModify with the new values

Code example

if(PositionSelect(_Symbol))
{
    double new_sl = SymbolInfoDouble(_Symbol, SYMBOL_BID) - 100 * _Point;
    double new_tp = SymbolInfoDouble(_Symbol, SYMBOL_BID) + 100 * _Point;

    trade.PositionModify(_Symbol, new_sl, new_tp);
}

trade is an instance of the CTrade class.

Notes

  • It fails if no position is selected
  • In some cases, specifying a ticket is safer than using _Symbol
  • If changes are too frequent, they may be rejected depending on the environment

2.3 Difference from a Trailing Stop

A trailing stop is a mechanism that automatically raises SL as the price moves.
Unlike TP, it is a dynamic SL used to extend profits.

Comparison

ItemFixed SL/TPTrailing
NatureFixedDynamic
Profit-takingFixed by TPFollows with SL
Implementation difficultyLowMedium

Basic logic

if(PositionSelect(_Symbol))
{
    double current_price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
    double new_sl = current_price - 50 * _Point;

    // Update only when it is more favorable than the current SL
    if(new_sl > PositionGetDouble(POSITION_SL))
    {
        trade.PositionModify(_Symbol, new_sl, PositionGetDouble(POSITION_TP));
    }
}

Common Sticking Points and Notes

■ Not setting SL/TP at order time

→ It is easy to think “I can set it later”
→ In live trading, a sudden crash while SL is not set can be fatal

■ Confusing trailing stop with TP

→ TP is fixed, while trailing updates SL
→ Their roles are completely different

■ PositionModify failures

  • No position selected
  • The value is too close (StopLevel violation)
  • The update is invalid because the value is unchanged

Common Mistakes

  • Sending an order with SL/TP left at 0 (not set)
  • Setting SL above the price on a BUY order
  • Lowering SL with a trailing stop (wrong-direction update)
  • Creating an infinite loop because no update condition is used

Conclusion for Beginners

  • First, use only order-time settings with OrderSend
  • After you are comfortable, use PositionModify
  • Use trailing stops last

3. Implementation Details for Setting SL and TP with OrderSend

Key points in this section
– The structure and required fields of MqlTradeRequest
– The correct way to write SL/TP settings
– Code that can be used directly in practice


3.1 Basics of the MqlTradeRequest Structure

In MQL5, order information is placed into the MqlTradeRequest structure and passed to OrderSend.
SL/TP are also specified inside this structure.

Main fields

FieldDescription
actionOrder action type, such as market or pending order
symbolCurrency pair
volumeLot size
typeBUY / SELL
priceOrder price
slStop-loss price
tpTake-profit price

Minimum required fields

request.action
request.symbol
request.volume
request.type
request.price

SL/TP are optional in the structure, but in real trading they should be treated as required.


3.2 Correct SL/TP Settings for BUY and SELL

SL/TP are specified as “prices.”
The important point is that they are price values, not point counts.

For BUY

request.sl = price - 100 * _Point;
request.tp = price + 100 * _Point;

For SELL

request.sl = price + 100 * _Point;
request.tp = price - 100 * _Point;

Why it works this way

  • BUY: Price increase creates profit → TP is above
  • SELL: Price decrease creates profit → TP is below

If the direction is wrong, an invalid stops error occurs immediately


3.3 Complete Practical Code Example

The following is practical code that includes basic error prevention.

#include <Trade/Trade.mqh>
CTrade trade;

void OpenBuy()
{
    double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

    // Get the minimum stop distance
    int stopLevel = (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);

    double sl = ask - (stopLevel + 10) * _Point;
    double tp = ask + (stopLevel + 10) * _Point;

    MqlTradeRequest request;
    MqlTradeResult result;

    ZeroMemory(request);

    request.action = TRADE_ACTION_DEAL;
    request.symbol = _Symbol;
    request.volume = 0.1;
    request.type   = ORDER_TYPE_BUY;
    request.price  = ask;
    request.sl     = NormalizeDouble(sl, _Digits);
    request.tp     = NormalizeDouble(tp, _Digits);
    request.deviation = 10;

    if(!OrderSend(request, result))
    {
        Print("OrderSend failed: ", result.retcode);
    }
}

3.4 Why NormalizeDouble and Digits Matter

Prices must always be rounded to match the number of digits used by the currency pair.

NormalizeDouble(price, _Digits);

Reason

  • Invalid number of digits → order rejection
  • Behavior depends on broker specifications

Examples

  • EURUSD (5 digits) → _Digits = 5
  • USDJPY (3 digits) → _Digits = 3

Common Sticking Points and Notes

■ Specifying SL/TP as points

→ Always specify them as “prices”
→ Multiply by _Point to convert

■ Ignoring stopLevel

→ Values closer than the minimum distance are rejected
→ The value differs by environment

■ Using Ask/Bid incorrectly

  • BUY: Ask
  • SELL: Bid
    → Using the wrong one causes a price mismatch

■ Not using NormalizeDouble

→ Some orders fail because of digit errors


Common Mistakes

  • Sending SL/TP as 0
  • Getting an error because digits are not adjusted
  • Order rejection because stopLevel is not considered
  • Losing consistency between price and SL/TP

Practical Best Practices

  • Always set SL/TP at order time
  • Add a margin with stopLevel + alpha
  • Format prices with NormalizeDouble
  • Always log error codes

4. Causes and Fixes for the “invalid stops” Error

Key points in this section
– When invalid stops occurs
– Specific categories of causes
– Reproducible ways to fix it


4.1 What Is invalid stops?

invalid stops is an error that occurs when the SL (Stop Loss) or TP (Take Profit) setting is invalid.
In MQL5, it is returned when running OrderSend or PositionModify.

Main characteristics:

  • The order itself may be valid, but the SL/TP is invalid
  • It often means broker-side restrictions were violated
  • It is one of the most common errors beginners encounter
Invalid stops error in MQL5 shown in MetaTrader terminal with BUY/SELL SL and TP direction rules, StopLevel distance constraints, and code validation steps for OrderSend to prevent trade execution failure

4.2 Main Causes (5 Patterns)

1. Wrong SL/TP direction

This is the most common cause.

  • SL is above the price on a BUY order
  • TP is above the price on a SELL order

If the price relationship is reversed, the error occurs immediately


2. Minimum stop distance (StopLevel) violation

Brokers have a restriction that SL/TP must be placed at least a certain distance away from the current price.

int stopLevel = (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
  • Unit: points
  • Different for each currency pair and broker

→ If the distance is below this value, invalid stops occurs


3. Not considering the spread

This problem is especially common with BUY orders.

  • BUY orders are executed at Ask
  • SL/TP may be judged based on Bid

→ If the spread is not considered, the distance may be insufficient


4. Wrong number of price digits

NormalizeDouble(price, _Digits);
  • Digit mismatch → treated as an invalid price
  • This is especially common with pairs such as USDJPY

5. Too close to the current price

Even if the logic is correct, the following cases can still fail.

  • Setting SL very close immediately after execution
  • Fast market movement shifts the price

This is an execution-timing-dependent error


4.3 Fix Template for Practical Use

The following countermeasure code can be used directly in practice.

double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);

int stopLevel = (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);

// Add a safety margin
double buffer = (stopLevel + 10) * _Point;

// BUY example
double sl = NormalizeDouble(ask - buffer, _Digits);
double tp = NormalizeDouble(ask + buffer, _Digits);

4.4 Practical Checklist

Checking the following before sending an order prevents most issues.

  • SL < price < TP (for BUY)
  • TP < price < SL (for SELL)
  • The distance is at least stopLevel
  • NormalizeDouble has been applied
  • Ask/Bid relationship is consistent

Common Sticking Points and Notes

■ StopLevel is not always the same

→ It may change by time of day or symbol
→ Getting it every time is safer

■ Malfunction when spreads widen

→ Spreads can widen sharply around economic news
→ SL may become invalid

■ Market and pending orders behave differently

→ Pending orders often have stricter conditions


Common Mistakes

  • Assuming stopLevel is 0 even though internal restrictions exist
  • Setting SL/TP too close to the limit
  • Ignoring the spread
  • Not checking debug logs

Practical Best Practices

  • Set SL/TP at stopLevel + alpha (10 to 20 points)
  • Always use NormalizeDouble
  • Log the error code (result.retcode)
  • Design with environment differences, including broker differences, in mind

5. Difference Between pips, points, and Price, and the Correct SL/TP Calculation Method

Key points in this section
– Clarify the difference between pips and points
– Organize the confusing parts of SL/TP calculation
– Provide a safe calculation method for practical use


5.1 Difference Between pips and points (Most Important)

One of the most confusing parts of MQL5 is the difference between pips and points.

Definitions

  • point: The minimum price unit (_Point)
  • pips: The common price movement unit, which differs by currency pair

Examples

Currency pair_Point1 pip
EURUSD (5 digits)0.000010.0001
USDJPY (3 digits)0.0010.01

In other words:

  • EURUSD: 1 pip = 10 points
  • USDJPY: 1 pip = 10 points

→ In many environments, “1 pip = 10 points,” but there are exceptions, so check carefully


5.2 Why Confusion Happens

MQL5 processes everything based on points.
However, traders usually think in pips.

This mismatch can cause:

  • Settings that are off by a factor of 10
  • SL that is too close and causes an error
  • TP that is too far and unrealistic

These issues come directly from mixing the two units.


5.3 Safe SL/TP Calculation Using pips

In practice, the safe flow is pip input → point conversion → price calculation.

Example: Set 20-pip SL/TP

double pips = 20;

// 1 pip = 10 points (common assumption)
double pip_value = 10 * _Point;

double sl = ask - pips * pip_value;
double tp = ask + pips * pip_value;

5.4 General-purpose pip calculation function (recommended)

To absorb differences between currency pairs, it is safer to prepare a function like this.

double GetPipValue()
{
    if(_Digits == 3 || _Digits == 5)
        return 10 * _Point;
    else
        return _Point;
}

Usage example

double pip = GetPipValue();

double sl = NormalizeDouble(ask - 20 * pip, _Digits);
double tp = NormalizeDouble(ask + 20 * pip, _Digits);

5.5 SELL Calculation (Important)

For SELL, the direction is reversed.

double pip = GetPipValue();

double sl = NormalizeDouble(bid + 20 * pip, _Digits);
double tp = NormalizeDouble(bid - 20 * pip, _Digits);

Common Sticking Points and Notes

■ Using pips directly

→ This does not work correctly in MQL5
→ You must convert to points

■ Ignoring differences by currency pair

→ JPY pairs and non-JPY pairs use different digit formats
→ Always check _Digits

■ Writing fixed values

→ Hard-coded values such as 0.0001 are risky
→ The logic can break when the symbol changes


Common Mistakes

  • Expecting 20 pips but actually getting 2 pips
  • SL is too close and triggers invalid stops
  • TP is too far to be realistic
  • The logic breaks when the currency pair changes

Practical Best Practices

  • Always prepare a pips-to-points conversion function
  • Design based on _Digits
  • Format with NormalizeDouble
  • Make SL/TP calculation a shared function

6. Ready-to-use SL and TP Setting Template for Practice (Reusable Code)

Key points in this section
– Reusable function design
– A template that supports both BUY and SELL
– An implementation that is less likely to break in live operation


6.1 Template Design Concept

If you write SL/TP settings separately every time, these problems occur:

  • Bugs, such as direction mistakes and digit mistakes
  • Logic breaks when the currency pair changes
  • Lower maintainability

→ The solution is to move the logic into shared functions, or templates.


6.2 General-purpose SL/TP Calculation Function

First, define a function that safely calculates values based on pips.

double GetPipValue()
{
    if(_Digits == 3 || _Digits == 5)
        return 10 * _Point;
    else
        return _Point;
}

6.3 SL/TP Calculation Template for BUY and SELL

bool CalculateSLTP(bool isBuy, double price, double sl_pips, double tp_pips, double &amp;sl, double &amp;tp)
{
    double pip = GetPipValue();

    if(isBuy)
    {
        sl = price - sl_pips * pip;
        tp = price + tp_pips * pip;
    }
    else
    {
        sl = price + sl_pips * pip;
        tp = price - tp_pips * pip;
    }

    sl = NormalizeDouble(sl, _Digits);
    tp = NormalizeDouble(tp, _Digits);

    return true;
}

6.4 Template with StopLevel Handling for Practical Use

To improve safety further, include the minimum stop distance (StopLevel).

bool AdjustToStopLevel(double price, double &amp;sl, double &amp;tp)
{
    int stopLevel = (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
    double min_distance = (stopLevel + 10) * _Point;

    // Check SL distance
    if(MathAbs(price - sl) &lt; min_distance)
    {
        if(sl &lt; price)
            sl = price - min_distance;
        else
            sl = price + min_distance;
    }

    // Check TP distance
    if(MathAbs(price - tp) &lt; min_distance)
    {
        if(tp &gt; price)
            tp = price + min_distance;
        else
            tp = price - min_distance;
    }

    sl = NormalizeDouble(sl, _Digits);
    tp = NormalizeDouble(tp, _Digits);

    return true;
}

6.5 Actual Order Code (Complete Version)

void OpenTrade(bool isBuy)
{
    double price = isBuy ? 
        SymbolInfoDouble(_Symbol, SYMBOL_ASK) : 
        SymbolInfoDouble(_Symbol, SYMBOL_BID);

    double sl, tp;

    // Calculate SL/TP
    CalculateSLTP(isBuy, price, 20, 40, sl, tp);

    // Adjust for StopLevel
    AdjustToStopLevel(price, sl, tp);

    MqlTradeRequest request;
    MqlTradeResult result;

    ZeroMemory(request);

    request.action = TRADE_ACTION_DEAL;
    request.symbol = _Symbol;
    request.volume = 0.1;
    request.type   = isBuy ? ORDER_TYPE_BUY : ORDER_TYPE_SELL;
    request.price  = price;
    request.sl     = sl;
    request.tp     = tp;
    request.deviation = 10;

    if(!OrderSend(request, result))
    {
        Print("Order failed: ", result.retcode);
    }
}

Common Sticking Points and Notes

■ Writing SL/TP directly outside functions

→ This easily creates bugs
→ Always gather the logic into shared functions

■ Not supporting StopLevel

→ Environment-dependent errors can suddenly occur
→ This handling is required for live operation

■ Skipping pip conversion

→ The logic breaks when the currency pair changes


Common Mistakes

  • Wrong BUY/SELL conditional branching
  • Forgetting NormalizeDouble
  • Not considering StopLevel
  • Copying the same code into many places and managing it separately

Practical Best Practices

  • Make SL/TP reusable functions
  • Design in pips
  • Always include StopLevel + buffer
  • Apply NormalizeDouble to all prices

7. Beginner Version: A Simple Minimum Implementation That Runs Quickly

Key points in this section
– A minimum setup with unnecessary parts removed
– An implementation that prioritizes getting it running first
– What should be improved later


7.1 How to Think About a Minimum Setup

Beginners should first start with minimum code that works correctly.
If you add generalization and optimization from the beginning, these problems can occur:

  • The cause of errors becomes hard to identify
  • The logic becomes complex
  • The learning cost increases

→ At first, “one symbol, fixed pips, and order-time settings” is enough.


7.2 Minimum BUY Order Code with SL/TP

#include &lt;Trade/Trade.mqh&gt;

void OnTick()
{
    static bool executed = false;
    if(executed) return;

    double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

    // Fixed values (20 pips / 40 pips)
    double sl = ask - 200 * _Point;
    double tp = ask + 400 * _Point;

    MqlTradeRequest request;
    MqlTradeResult result;

    ZeroMemory(request);

    request.action = TRADE_ACTION_DEAL;
    request.symbol = _Symbol;
    request.volume = 0.1;
    request.type   = ORDER_TYPE_BUY;
    request.price  = ask;
    request.sl     = NormalizeDouble(sl, _Digits);
    request.tp     = NormalizeDouble(tp, _Digits);
    request.deviation = 10;

    OrderSend(request, result);

    executed = true;
}

7.3 Key Points in This Code

  • Runs only once using a static variable
  • SL/TP use fixed values, prioritizing understanding first
  • Prices are formatted with NormalizeDouble

Why 200 and 400?

  • In a 5-digit environment
    • 200 points = 20 pips
    • 400 points = 40 pips

→ This simply represents “20 pips / 40 pips.”


7.4 What You Do Not Need at This Stage

Beginners do not need to force the following into the first version.

  • General-purpose functions
  • StopLevel adjustment
  • Trailing stop
  • Complex conditional branching

→ Getting it to run first is the top priority.


Common Sticking Points and Notes

■ Orders are sent repeatedly

OnTick runs on every tick
→ Control with a static flag is required

■ SL/TP are in the wrong direction

→ BUY: SL below, TP above

■ Confusing points and pips

→ 200 = 20 pips in a 5-digit environment

■ Differences between demo and live environments

→ Spread and StopLevel can differ


Common Mistakes

  • Orders are issued endlessly
  • Orders are sent without SL/TP
  • Copying numbers without understanding what they mean
  • Trying to build a complex EA immediately

Next Improvements

After you understand this code, the next steps are:

  • Add a pip conversion function
  • Support StopLevel
  • Support SELL
  • Add PositionModify

8. Strategic Thinking for SL/TP Design in EA Development

Key points in this section
– SL/TP are not just setting values; they are part of strategy design
– Relationship with expected value, reproducibility, and drawdown control
– How to choose between fixed and dynamic widths


8.1 SL/TP Are Not Add-on Order Settings; They Are Core Strategy Parameters

When setting SL (Stop Loss) and TP (Take Profit) in MQL5, it is easy to treat them as values required when sending an order.
In practice, however, SL/TP are strategy parameters that are as important as, or more important than, entry conditions.

This is because final profit and loss are determined by the combination of:

  • Where you enter
  • Where you exit for a loss (SL)
  • Where you take profit (TP)
  • What lot size you use

Among these, SL/TP directly determine the profit and loss distribution.
Even with the same entry accuracy, different SL/TP designs can greatly change PF (Profit Factor) and DD (Drawdown).

For example, even with the same BUY signal:

  • SL 10 pips / TP 20 pips
  • SL 30 pips / TP 30 pips
  • SL 50 pips / TP 100 pips

These settings require very different win rates and create very different profit and loss swings.


8.2 Decide the Risk-Reward Ratio First

The first thing to consider in SL/TP design is the RR (Risk Reward Ratio), or the ratio of potential profit to potential loss.

Examples

  • SL 20 pips / TP 40 pips → RR 1:2
  • SL 30 pips / TP 30 pips → RR 1:1
  • SL 40 pips / TP 20 pips → RR 2:1

In general, a higher RR increases the profit per winning trade, but the TP hit rate tends to drop.
Conversely, a lower RR can raise the win rate, but each loss carries more weight.

Practical view

  • Short-term benefits
    • Fixed RR makes backtest comparison easier
    • EA design becomes simpler
  • Long-term benefits
    • It connects more easily with lot management
    • Profit and loss become more reproducible

Beginner guideline

It is easier to start with one of the following.

  • Conservative type: 1:1 to 1:1.5
  • Profit-extension type: around 1:2

However, there is no fixed correct answer.
The optimal value changes depending on win rate, spread, slippage, and the characteristics of the currency pair.


8.3 Fixed SL/TP vs Dynamic SL/TP

SL/TP design can be divided into two major types.

Fixed-width type

Example: Always SL 20 pips and TP 40 pips

Benefits

  • Easy to implement
  • Backtest comparison is clear
  • Easy for beginners to manage

Drawbacks

  • Weak against changes in market volatility
  • May be too far in low volatility and too close in high volatility

Dynamic type

Example: Change SL/TP based on ATR (average price range)

Benefits

  • Adapts more easily to market conditions
  • Helps avoid abnormally close SL settings
  • Absorbs differences between currency pairs more easily

Drawbacks

  • Implementation is more complex
  • Test conditions are harder to align
  • Beginners may find cause analysis difficult

Recommended practical order

  • Start with the fixed-width type
  • Then move to a dynamic type such as ATR-based settings if needed

This order makes it easier to track where performance changed.


8.4 How to Evaluate the Effect on PF and DD

SL/TP design is directly connected not only to the visible win rate, but also to the shape of PF and DD.

When fixed SL is too narrow

  • Small noise can trigger stop loss
  • Win rate decreases
  • Losing streaks become more likely

When fixed SL is too wide

  • The apparent win rate may improve
  • Each loss becomes heavier
  • DD tends to become deeper

When TP is too close

  • Small profits are easier to take
  • Costs such as spread and commission have more impact
  • PF is harder to improve

When TP is too far

  • Single-trade profit can be large
  • The hit rate decreases
  • Profit variation becomes larger

In short, SL/TP are not a simple choice between “safe” and “dangerous.” They are a question of how to design the profit and loss distribution.


8.5 Design Mistakes Beginners Should Avoid

■ Judging only by win rate

Even with a 70% win rate, expected value gets worse if one loss cancels three wins.

■ Using the same width after changing currency pairs

EURUSD and GBPJPY have different price movement characteristics.
If you reuse fixed pips as-is, the design can break easily.

■ Optimizing only SL or only TP

If you adjust only one side, the overall balance is lost.
Always test SL and TP as a pair.

■ Using the best backtest value as-is

Excessive optimization, or overfitting, can reduce future reproducibility.


8.6 Recommended Practical Design Procedure

If you are unsure how to design SL/TP, proceed in this order.

  • First prototype with fixed SL/TP
  • Compare RR values such as 1:1, 1:1.5, and 1:2
  • Check PF, DD, and win rate together
  • Check differences by currency pair
  • Move to ATR-based settings if needed

This process lets you improve while maintaining reproducibility.


Common Sticking Points and Notes

  • A higher RR is not always better
  • A higher win rate is not always better either
  • The optimal value changes by currency pair and timeframe
  • The best backtest value may not reproduce in the future

Common Mistakes

  • Adjusting roughly in 10-pip steps and stopping there
  • Judging only by PF without checking DD
  • Switching to dynamic SL immediately after fixed SL loses
  • Not deciding risk tolerance in the first place

9. MQL5 SL/TP Setting Checklist and Final Summary

Key points in this section
– Checklist items to prevent practical mistakes
– Confirmation points for reproducibility
– A short summary of the main points in this article


9.1 Pre-order Checklist (Required)

In practice, SL/TP setting mistakes can lead directly to losses or order rejection.
By making the following checks routine, you can prevent almost all common problems.

Basic checks

  • SL and TP are not 0
  • BUY: SL < price < TP
  • SELL: TP < price < SL
  • NormalizeDouble has been applied for _Digits

Distance checks

  • The distance is at least stopLevel
  • A buffer (+10 to 20 points) is secured
  • The spread is considered

Calculation checks

  • pips have been converted to points
  • The design is not dependent on one currency pair
  • Hard-coded values are not used

9.2 Post-execution Checks (Debugging)

Even when the order succeeds, it is important to check whether the values are exactly as intended.

Log output example

Print("price=", request.price,
      " SL=", request.sl,
      " TP=", request.tp);

Points to check

  • Are the values as expected?
  • Are the digits correct?
  • Is the SL/TP direction correct?

9.3 Troubleshooting Procedure When an Error Occurs

When an error occurs, do not fix it by guesswork. Check it in order.

Procedure

  1. Check result.retcode
  2. Log the SL/TP prices
  3. Check stopLevel
  4. Check the position relative to Ask/Bid

Example

Print("retcode=", result.retcode);

9.4 Most Important Practical Rule: Prioritize Reproducibility

The most important thing in SL/TP settings is reproducibility and consistency.

Rules to follow

  • Always use functions
  • Design based on pips
  • Use NormalizeDouble thoroughly
  • Assume stopLevel + buffer

Why this matters

  • You can absorb environment differences, including broker differences
  • You can reduce the gap between backtesting and live operation
  • You can localize bugs more easily

9.5 Summary of This Article

Here is a concise practical summary of this article.

Technical points

  • Set SL/TP as “prices”
  • Handle _Point and _Digits correctly
  • stopLevel violations are a major cause of errors

Implementation points

  • Order-time settings with OrderSend are the basic method
  • You can modify values later with PositionModify
  • Shared functions help prevent bugs

Strategy points

  • SL/TP are risk design itself
  • Decide RR (Risk Reward) first
  • Improve in the order fixed → dynamic

9.6 Conclusion: The Shortest Path to Stability

For beginners to intermediate users, the most stable setup is:

  • Fixed pips, such as SL20 / TP40
  • Set SL/TP at order time
  • Support stopLevel + buffer
  • Apply NormalizeDouble thoroughly

First stabilize operation in this state, then move to optimization. This is the most reproducible way to proceed.


Common Sticking Points and Notes

  • Fixing only one part can break overall consistency
  • Ignoring errors and moving on
  • A design that fails when the currency pair changes
  • Overtrusting backtest results

Common Mistakes

  • Running live trading without SL/TP
  • Changing code without checking logs
  • Repeated errors caused by ignoring StopLevel
  • Moving to optimization before the design is fixed

FAQ

Q1. Should SL/TP always be set?

A. Yes. In practice, SL/TP should be set because leaving them unset creates the risk of losses expanding without a defined limit.


Q2. How can I prevent the invalid stops error?

A. You can prevent most cases by keeping enough distance from stopLevel, using the correct direction, and applying NormalizeDouble consistently.


Q3. What is the difference between pips and points?

A. A point is the minimum price unit used by MQL5, while a pip is a conventional trading unit. MQL5 calculations should be based on points.


Q4. Can I change SL/TP after placing an order?

A. Yes. You can use PositionModify to change SL/TP for an existing position.


Q5. What is the difference between a trailing stop and TP?

A. TP is a fixed profit-taking price, while a trailing stop dynamically updates SL as price moves.


Q6. How should I decide the best SL/TP?

A. Decide based on RR (Risk Reward), backtest results, and the balance between PF and DD. The best value depends on the strategy, symbol, and trading environment.


Q7. Should settings differ by currency pair?

A. Yes. Because volatility differs by currency pair, using the same setting for every pair is not recommended.