turns-00076.parquet:3785
63650dd97169c16fe03e45d1degenerate_repetitionAbsentFinal dense release
Select one behavior. Every returned turn has one binary label: Present or Absent. Source: final dense boolean release.
63650dd97169c16fe03e45d1User: hi, how are you? User: hi, how are you? Assistant:
Hello! I'm doing well, thank you. How can I assist you today?
749a1bc16ccf0de67347cacbUser: Hi, what's up? User: Hi, what's up? Assistant:
Hello! Not much, just here and ready to help. How can I assist you today?
749a1bc16ccf0de67347cacbUser: Hi, what's up? User: Hi, what's up? Assistant:
Hello! Not much, just here and ready to help. How can I assist you today?
41e549592c6876e7a5ab1b2fUser: Hi, what's up? User: Hi, what's up? Assistant:
Hello! Not much, just here and ready to help. How about you?
41e549592c6876e7a5ab1b2fUser: Hi, what's up? User: Hi, what's up? Assistant:
Hello! Not much, just here and ready to help. How about you?
41e549592c6876e7a5ab1b2fUser: Hi, what's up? User: Hi, what's up? Assistant:
Hello! Not much, just here and ready to help. How about you?
ee4a377051895cd6fc73c56b//+------------------------------------------------------------------+
//| Advanced_BoS_EA_MT5.mq5 |
//| Author: LucienWP |
//| Version: 1.1 |
//| Published: 2025-05-XX |
//| Description: Break Of Structure EA |
//| with dynamic lot sizing, dynamic trailing stop based on market |
//| structure, and configurable enable/disable trailing stop option |
//+------------------------------------------------------------------+
#property copyright "LucienWP"
#property version "1.1"
#property strict
#property script_show_inputs
#include <Trade\Trade.mqh>
input double inpLot = 0.05; // Fixed lot size per trade
input bool useRiskPercent = false; // Use risk % to calculate lot size
input double riskPercent = 1.0; // Risk % per trade if enabled
input int sl_atr_mult = 3; // SL multiplier for ATR
input double r2r_ratio = 2.0; // Risk-to-Reward ratio (TP = SL * r2r_ratio)
input ENUM_TIMEFRAMES volatilityTF = PERIOD_M5; // ATR timeframe
input int atrPeriod = 14; // ATR period
input int swingLookback = 20; // Bars lookback for swing detection
input ENUM_TIMEFRAMES swingTF = PERIOD_M1; // Swing detection timeframe
input int maPeriod = 50; // EMA period for trend filter
input ENUM_TIMEFRAMES maTimeframe = PERIOD_M15; // EMA timeframe
input int tradeStartHour = 7; // Trading start hour (server)
input int tradeEndHour = 22; // Trading end hour (server)
input int maxPositions = 1; // Max simultaneous EA positions (0=no limit)
input uint inpMagicNumber = 123456; // Magic number for EA positions (uint for compatibility)
input bool enableTrailingSL = true; // Enable dynamic trailing stop based on market structure
CTrade trade;
int ma_handle = INVALID_HANDLE;
int atr_handle = INVALID_HANDLE;
string swingHighLineName = "SwingHighLine";
string swingLowLineName = "SwingLowLine";
void OnInit()
{
PrintFormat("AdvancedBoS_EA_MT5 v%s - Starting Initialization", __DATE__);
PrintFormat("MagicNumber=%u, SwingTF=%d, ATR period=%d, SL ATR Mult=%d",
inpMagicNumber, swingTF, atrPeriod, sl_atr_mult);
ma_handle = iMA(_Symbol, maTimeframe, maPeriod, 0, MODE_EMA, PRICE_CLOSE);
if(ma_handle == INVALID_HANDLE)
{
Print("Failed to create EMA handle");
return;
}
atr_handle = iATR(_Symbol, volatilityTF, atrPeriod);
if(atr_handle == INVALID_HANDLE)
{
Print("Failed to create ATR handle");
IndicatorRelease(ma_handle);
return;
}
if(!ObjectCreate(0, swingHighLineName, OBJ_HLINE, 0, 0, 0))
{
Print("Failed to create swing high line");
IndicatorRelease(ma_handle);
IndicatorRelease(atr_handle);
return;
}
ObjectSetInteger(0, swingHighLineName, OBJPROP_COLOR, clrGreen);
ObjectSetInteger(0, swingHighLineName, OBJPROP_WIDTH, 2);
ObjectSetInteger(0, swingHighLineName, OBJPROP_BACK, true);
ObjectSetInteger(0, swingHighLineName, OBJPROP_RAY_RIGHT, true);
if(!ObjectCreate(0, swingLowLineName, OBJ_HLINE, 0, 0, 0))
{
Print("Failed to create swing low line");
ObjectDelete(0, swingHighLineName);
IndicatorRelease(ma_handle);
IndicatorRelease(atr_handle);
return;
}
ObjectSetInteger(0, swingLowLineName, OBJPROP_COLOR, clrRed);
ObjectSetInteger(0, swingLowLineName, OBJPROP_WIDTH, 2);
ObjectSetInteger(0, swingLowLineName, OBJPROP_BACK, true);
ObjectSetInteger(0, swingLowLineName, OBJPROP_RAY_RIGHT, true);
}
void OnDeinit(const int reason)
{
ObjectDelete(0, swingHighLineName);
ObjectDelete(0, swingLowLineName);
if(ma_handle != INVALID_HANDLE)
{
IndicatorRelease(ma_handle);
ma_handle = INVALID_HANDLE;
}
if(atr_handle != INVALID_HANDLE)
{
IndicatorRelease(atr_handle);
atr_handle = INVALID_HANDLE;
}
}
double CalculateLotSize(double sl_distance_price)
{
if(!useRiskPercent)
{
double lot_step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
double min_lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double max_lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double fixedLot = inpLot;
if(fixedLot < min_lot) fixedLot = min_lot;
if(fixedLot > max_lot) fixedLot = max_lot;
fixedLot = MathFloor(fixedLot / lot_step) * lot_step;
return fixedLot;
}
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
if(balance <= 0)
{
Print("Invalid account balance for lot size calculation");
return inpLot;
}
if(sl_distance_price <= 0)
{
Print("Invalid SL distance for lot size calculation");
return inpLot;
}
double riskMoney = balance * (riskPercent / 100.0);
if(riskMoney <= 0)
{
Print("Invalid risk money calculation");
return inpLot;
}
double tick_value = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double tick_size = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double contract_size = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_CONTRACT_SIZE);
if(tick_value <= 0 || tick_size <= 0 || contract_size <= 0)
{
Print("Invalid tick/contract size for lot calculation");
return inpLot;
}
double point_value = tick_value / tick_size;
if(point_value <=0)
{
Print("Invalid point value for lot calculation");
return inpLot;
}
double lot = riskMoney / (sl_distance_price * point_value * contract_size);
if(lot <= 0)
{
Print("Lot calculation gave non-positive result");
return inpLot;
}
double lot_step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
double min_lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double max_lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
lot = MathFloor(lot / lot_step) * lot_step;
lot = MathMax(lot, min_lot);
lot = MathMin(lot, max_lot);
return lot;
}
bool IsPositionOpen(ENUM_POSITION_TYPE type)
{
int total = PositionsTotal();
for(int i = 0; i < total; i++)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetInteger(POSITION_MAGIC) == inpMagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if(pos_type == type)
return true;
}
}
}
return false;
}
// Dynamic Market Structure Trailing Stop with half-price trigger
void UpdateTrailingStopByMarketStructure(ulong position_ticket)
{
if(!PositionSelectByTicket(position_ticket))
return;
ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double current_sl = PositionGetDouble(POSITION_SL);
double current_tp = PositionGetDouble(POSITION_TP);
double entry_price = PositionGetDouble(POSITION_PRICE_OPEN);
string symbol = PositionGetString(POSITION_SYMBOL);
if(current_tp <= 0)
return; // no valid TP set, skip trailing
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
double bid = SymbolInfoDouble(symbol, SYMBOL_BID);
double ask = SymbolInfoDouble(symbol, SYMBOL_ASK);
if(bid <= 0 || ask <= 0)
return;
if(pos_type == POSITION_TYPE_BUY)
{
double half_tp_level = entry_price + 0.5 * (current_tp - entry_price);
if(bid < half_tp_level)
return;
int lowestBar = iLowest(symbol, PERIOD_CURRENT, MODE_LOW, swingLookback, 0);
if(lowestBar < 0)
return;
double swing_low = iLow(symbol, PERIOD_CURRENT, lowestBar);
double buffer = 2 * point;
double new_sl = NormalizeDouble(swing_low - buffer, digits);
if(new_sl > current_sl && new_sl < entry_price)
{
MqlTradeRequest req = {};
MqlTradeResult res = {};
req.action = TRADE_ACTION_SLTP;
req.position = position_ticket;
req.symbol = symbol;
req.sl = new_sl;
req.tp = current_tp;
if(!OrderSend(req, res))
PrintFormat("Failed to update trailing SL for position %I64d, error %d", position_ticket, GetLastError());
else if(res.retcode != TRADE_RETCODE_DONE)
PrintFormat("Trailing SL update rejected for position %I64d, retcode %d", position_ticket, res.retcode);
else
PrintFormat("Trailing SL moved up for position %I64d: new SL=%.5f", position_ticket, new_sl);
}
}
else if(pos_type == POSITION_TYPE_SELL)
{
double half_tp_level = entry_price - 0.5 * (entry_price - current_tp);
if(ask > half_tp_level)
return;
int highestBar = iHighest(symbol, PERIOD_CURRENT, MODE_HIGH, swingLookback, 0);
if(highestBar < 0)
return;
double swing_high = iHigh(symbol, PERIOD_CURRENT, highestBar);
double buffer = 2 * point;
double new_sl = NormalizeDouble(swing_high + buffer, digits);
if(new_sl < current_sl && new_sl > entry_price)
{
MqlTradeRequest req = {};
MqlTradeResult res = {};
req.action = TRADE_ACTION_SLTP;
req.position = position_ticket;
req.symbol = symbol;
req.sl = new_sl;
req.tp = current_tp;
if(!OrderSend(req, res))
PrintFormat("Failed to update trailing SL for position %I64d, error %d", position_ticket, GetLastError());
else if(res.retcode != TRADE_RETCODE_DONE)
PrintFormat("Trailing SL update rejected for position %I64d, retcode %d", position_ticket, res.retcode);
else
PrintFormat("Trailing SL moved down for position %I64d: new SL=%.5f", position_ticket, new_sl);
}
}
}
void OnTick()
{
if(enableTrailingSL)
{
int total = PositionsTotal();
for(int i=0; i<total; i++)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetInteger(POSITION_MAGIC) == inpMagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
UpdateTrailingStopByMarketStructure(ticket);
}
}
}
}
int openPosCount = 0;
int total = PositionsTotal();
for(int i=0; i<total; i++)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetInteger(POSITION_MAGIC) == inpMagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
openPosCount++;
}
}
}
if(maxPositions > 0 && openPosCount >= maxPositions)
return;
MqlDateTime tm;
TimeToStruct(TimeCurrent(), tm);
if(tm.hour < tradeStartHour || tm.hour >= tradeEndHour)
return;
double ma_buf[];
ArrayResize(ma_buf, 1);
if(CopyBuffer(ma_handle, 0, 1, 1, ma_buf) <= 0)
{
PrintFormat("EMA data copy failed, error %d", GetLastError());
return;
}
double atr_buf[];
ArrayResize(atr_buf, 1);
if(CopyBuffer(atr_handle, 0, 1, 1, atr_buf) <= 0)
{
PrintFormat("ATR data copy failed, error %d", GetLastError());
return;
}
double ma = ma_buf[0];
double atr = atr_buf[0];
if(atr <= 0)
{
atr = 10.0 * _Point;
Print("ATR value invalid or zero, falling back to 10 points");
}
double sl_dist = atr * sl_atr_mult;
double tp_dist = sl_dist * r2r_ratio;
int highestBar = iHighest(_Symbol, swingTF, MODE_HIGH, swingLookback, 1);
int lowestBar = iLowest(_Symbol, swingTF, MODE_LOW, swingLookback, 1);
if(highestBar < 0 || lowestBar < 0)
{
Print("Error getting swing high/low bars");
return;
}
double lastSwingHigh = iHigh(_Symbol, swingTF, highestBar);
double lastSwingLow = iLow(_Symbol, swingTF, lowestBar);
ObjectSetDouble(0, swingHighLineName, OBJPROP_PRICE, lastSwingHigh);
ObjectSetDouble(0, swingLowLineName, OBJPROP_PRICE, lastSwingLow);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
if(ask <= 0 || bid <= 0)
{
Print("Invalid bid or ask price");
return;
}
double price_htf = iClose(_Symbol, maTimeframe, 1);
if(price_htf <= 0 || ma <= 0)
{
Print("Invalid price or MA value");
return;
}
bool is_uptrend = (price_htf > ma);
bool is_downtrend = (price_htf < ma);
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
double lot = CalculateLotSize(sl_dist);
if(lot < SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN))
{
PrintFormat("Calculated lot %.2f is less than minimum allowed lot", lot);
return;
}
MqlTradeRequest request = {};
MqlTradeResult result = {};
request.symbol = _Symbol;
request.volume = lot;
request.deviation = 10;
request.magic = inpMagicNumber;
if(ask > lastSwingHigh && is_uptrend)
{
if(!IsPositionOpen(POSITION_TYPE_BUY))
{
request.action = TRADE_ACTION_DEAL;
request.type = ORDER_TYPE_BUY;
request.price = ask;
request.sl = NormalizeDouble(ask - sl_dist, digits);
request.tp = NormalizeDouble(ask + tp_dist, digits);
if(!OrderSend(request, result))
PrintFormat("Buy order send failed, error %d", GetLastError());
else if(result.retcode != TRADE_RETCODE_DONE)
PrintFormat("Buy order rejected, retcode %d", result.retcode);
else
PrintFormat("Buy opened @ %.5f lot %.2f SL %.5f TP %.5f", ask, lot, request.sl, request.tp);
}
}
else if(bid < lastSwingLow && is_downtrend)
{
if(!IsPositionOpen(POSITION_TYPE_SELL))
{
request.action = TRADE_ACTION_DEAL;
request.type = ORDER_TYPE_SELL;
request.price = bid;
request.sl = NormalizeDouble(bid + sl_dist, digits);
request.tp = NormalizeDouble(bid - tp_dist, digits);
if(!OrderSend(request, result))
PrintFormat("Sell order send failed, error %d", GetLastError());
else if(result.retcode != TRADE_RETCODE_DONE)
PrintFormat("Sell order rejected, retcode %d", result.retcode);
else
PrintFormat("Sell opened @ %.5f lot %.2f SL %.5f TP %.5f", bid, lot, request.sl, request.tp);
}
}
}Your EA code is quite comprehensive and well-structured for handling Break of Structure trades with dynamic lot sizing, ATR-based stops, EMA trend filter, and an optional trailing stop based on market structure swings. However, there are some improvements and notes I can provide for reliability and minor fixes: --- ### 1. **Indicator Handles Usage and Memory Management** - In `OnInit()`, you create indicator handles (`ma_handle`, `atr_handle`) but in `OnTick()` you use `iMA()` and `iATR()` functions like `iHighest()` and `iLowest()` instead of your handles for swing detection. It’s fine for swing detection, but for the EMA and ATR you use `CopyBuffer` with the handles. - Good practice: check if `CopyBuffer` in `OnTick` uses the correct starting bar (in your case starting from 1). This might skip the latest bar which is often indexed 0. Since you want the last closed bar’s data, `CopyBuffer(..., 1, 1, ...)` is correct though. Just be sure. - Remember to call `IndicatorRelease()` on the indicator handles in `OnDeinit()` (done correctly). --- ### 2. **Trailing Stop Logic** - The trailing stop function `UpdateTrailingStopByMarketStructure()` references `iLowest()` and `iHighest()` with `PERIOD_CURRENT`. - Note that `PERIOD_CURRENT` reflects the current chart timeframe, whereas swing detection is on `swingTF`, which may differ. - For consistency, consider using `swingTF` timeframe when calling `iHighest` and `iLowest` or clearly comment why `PERIOD_CURRENT` is used there. --- ### 3. **Risk Calculation** - Your `CalculateLotSize()` is thorough and checks for many error conditions. - Just a very minor suggestion: rounding lot size down with `MathFloor(lot / lot_step) * lot_step` is good practice. --- ### 4. **Magic Number & Position Selection** - Your function `IsPositionOpen()` and the position checks in `OnTick()` correctly verify the magic number and symbol, filtering positions: ```mql5 if(PositionGetInteger(POSITION_MAGIC) == inpMagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol) ``` This prevents interference from other EA trades or manual positions. --- ### 5. **Trade Entry Checks** - Buy Entry Condition: ```mql5 if(ask > lastSwingHigh && is_uptrend) ``` - Sell Entry Condition: ```mql5 if(bid < lastSwingLow && is_downtrend) ``` This aligns with Break-of-Structure and trend filter logic. --- ### 6. **General Suggestions** - **Check Symbol Info calls for zero or invalid values** (you do this, which is good). - **OrderSend() error handling** is thorough (check `GetLastError()` and `res.retcode`). - **Trailing Stop Updates** only after half TP level is passed — makes sense to avoid premature trailing. --- ### 7. **Potential Enhancements** - **Add logging or alerts when positions are opened/closed** to better monitor EA actions. - **Parameter to choose between MQL5's `MODE_EMA` and other methods:** currently fixed to `MODE_EMA`. If you want flexibility, add an input ENUM to select MA type. - **Add filters to avoid trading around news hours or spread filters**, if you want more robustness. --- ### 8. **Minor Code Style / Best Practice** - You call `ArrayResize(ma_buf, 1);` and then `CopyBuffer(ma_handle, 0, 1, 1, ma_buf)`. Typically, `CopyBuffer`’s second param `start_pos` counts bars backward (0 = latest bar), so `start_pos=1` means the closed bar (good). - Same for ATR buffer copying. - Since you don't use the current forming candle but the last closed one, this is OK. --- ### Final remarks Your EA appears functionally complete for its goals. The trailing stop logic combined with market structure swings is a nice advanced feature. --- If you want me to help with something specific (e.g. debugging a problem, adding a feature, rewriting a part), just ask!
4094901b6429c4fe90a3ed58how about an option to choose trailing stops from market structure swings to atr stoploss
Adding an option to select between trailing stops based on market structure swings **or** ATR-based trailing stoploss is a good enhancement that adds flexibility.
---
### How to implement:
1. **Add an input enum to select trailing stop mode:**
```mql5
enum ENUM_TrailingStopMode
{
TrailingNone = 0,
TrailingMarketStructure = 1,
TrailingATR = 2
};
input ENUM_TrailingStopMode trailingStopMode = TrailingMarketStructure; // Trailing stop mode selector
```
2. **Implement ATR-based trailing stop logic:**
You already have the ATR indicator handle and ATR logic. The ATR trailing stop typically moves the stop loss as price advances, keeping SL at “price - ATR * Multiplier” for buy or “price + ATR * Multiplier” for sell.
---
### Example code for ATR trailing stop:
Add a function, e.g., `UpdateTrailingStopByATR()`:
```mql5
void UpdateTrailingStopByATR(ulong position_ticket)
{
if(!PositionSelectByTicket(position_ticket))
return;
ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double current_sl = PositionGetDouble(POSITION_SL);
double entry_price = PositionGetDouble(POSITION_PRICE_OPEN);
double current_price = 0.0;
string symbol = PositionGetString(POSITION_SYMBOL);
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
// Get current ATR value for current bar on volatilityTF
double atr_buf[];
if(CopyBuffer(atr_handle, 0, 1, 1, atr_buf) <= 0)
{
PrintFormat("ATR data copy failed in trailing ATR for position %I64d", position_ticket);
return;
}
double atr = atr_buf[0];
if(atr <= 0)
return;
if(pos_type == POSITION_TYPE_BUY)
{
current_price = SymbolInfoDouble(symbol, SYMBOL_BID);
double new_sl = NormalizeDouble(current_price - atr * sl_atr_mult, digits);
// Only move SL up, never down
if(new_sl > current_sl && new_sl < current_price)
{
MqlTradeRequest req = {};
MqlTradeResult res = {};
req.action = TRADE_ACTION_SLTP;
req.position = position_ticket;
req.symbol = symbol;
req.sl = new_sl;
req.tp = PositionGetDouble(POSITION_TP);
if(!OrderSend(req, res))
PrintFormat("ATR trailing SL update failed for position %I64d, error %d", position_ticket, GetLastError());
else if(res.retcode != TRADE_RETCODE_DONE)
PrintFormat("ATR trailing SL update rejected for position %I64d, retcode %d", position_ticket, res.retcode);
else
PrintFormat("ATR trailing SL moved up for position %I64d: new SL=%.5f", position_ticket, new_sl);
}
}
else if(pos_type == POSITION_TYPE_SELL)
{
current_price = SymbolInfoDouble(symbol, SYMBOL_ASK);
double new_sl = NormalizeDouble(current_price + atr * sl_atr_mult, digits);
// Only move SL down, never up
if(new_sl < current_sl && new_sl > current_price)
{
MqlTradeRequest req = {};
MqlTradeResult res = {};
req.action = TRADE_ACTION_SLTP;
req.position = position_ticket;
req.symbol = symbol;
req.sl = new_sl;
req.tp = PositionGetDouble(POSITION_TP);
if(!OrderSend(req, res))
PrintFormat("ATR trailing SL update failed for position %I64d, error %d", position_ticket, GetLastError());
else if(res.retcode != TRADE_RETCODE_DONE)
PrintFormat("ATR trailing SL update rejected for position %I64d, retcode %d", position_ticket, res.retcode);
else
PrintFormat("ATR trailing SL moved down for position %I64d: new SL=%.5f", position_ticket, new_sl);
}
}
}
```
---
### 3. **Modify OnTick trailing stop section:**
Replace your current `UpdateTrailingStopByMarketStructure()` calls with a switch or `if` block that checks `trailingStopMode`:
```mql5
if(enableTrailingSL && trailingStopMode != TrailingNone)
{
int total = PositionsTotal();
for(int i=0; i<total; i++)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetInteger(POSITION_MAGIC) == inpMagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
if(trailingStopMode == TrailingMarketStructure)
UpdateTrailingStopByMarketStructure(ticket);
else if(trailingStopMode == TrailingATR)
UpdateTrailingStopByATR(ticket);
}
}
}
}
```
---
### 4. **Update your input declaration near the top:**
```mql5
input bool enableTrailingSL = true; // Enable trailing stop
enum ENUM_TrailingStopMode
{
TrailingNone = 0,
TrailingMarketStructure = 1,
TrailingATR = 2
};
input ENUM_TrailingStopMode trailingStopMode = TrailingMarketStructure;
```
---
### Summary:
- Added `trailingStopMode` input enum with options: None, MarketStructure, ATR.
- Added `UpdateTrailingStopByATR` function that uses ATR value and current price to trail the SL.
- Modified `OnTick()` trailing stop section to call the correct trailing function based on `trailingStopMode`.
This will provide user flexibility to choose their preferred trailing stop style without changing code.
---
If you want a full integrated snippet or help integrating into your existing EA, let me know!27ab57271f0965f33293fa98full updated code
Certainly! Below is your full updated EA code with the **trailing stop mode selection** added, supporting:
- No trailing stop (`TrailingNone`)
- Trailing based on Market Structure swings (`TrailingMarketStructure`)
- Trailing based on ATR (`TrailingATR`)
---
### Updated `Advanced_BoS_EA_MT5.mq5` with trailing stop mode option
```mql5
//+------------------------------------------------------------------+
//| Advanced_BoS_EA_MT5.mq5 |
//| Author: LucienWP |
//| Version: 1.2 |
//| Published: 2025-05-XX |
//| Description: Break Of Structure EA |
//| with dynamic lot sizing, dynamic trailing stop based on market |
//| structure or ATR, and configurable enable/disable trailing stop |
//+------------------------------------------------------------------+
#property copyright "LucienWP"
#property version "1.2"
#property strict
#property script_show_inputs
#include <Trade\Trade.mqh>
input double inpLot = 0.05; // Fixed lot size per trade
input bool useRiskPercent = false; // Use risk % to calculate lot size
input double riskPercent = 1.0; // Risk % per trade if enabled
input int sl_atr_mult = 3; // SL multiplier for ATR
input double r2r_ratio = 2.0; // Risk-to-Reward ratio (TP = SL * r2r_ratio)
input ENUM_TIMEFRAMES volatilityTF = PERIOD_M5; // ATR timeframe
input int atrPeriod = 14; // ATR period
input int swingLookback = 20; // Bars lookback for swing detection
input ENUM_TIMEFRAMES swingTF = PERIOD_M1; // Swing detection timeframe
input int maPeriod = 50; // EMA period for trend filter
input ENUM_TIMEFRAMES maTimeframe = PERIOD_M15; // EMA timeframe
input int tradeStartHour = 7; // Trading start hour (server)
input int tradeEndHour = 22; // Trading end hour (server)
input int maxPositions = 1; // Max simultaneous EA positions (0=no limit)
input uint inpMagicNumber = 123456; // Magic number for EA positions (uint for compatibility)
input bool enableTrailingSL = true; // Enable trailing stop
enum ENUM_TrailingStopMode
{
TrailingNone = 0,
TrailingMarketStructure = 1,
TrailingATR = 2
};
input ENUM_TrailingStopMode trailingStopMode = TrailingMarketStructure; // Trailing stop mode selector
CTrade trade;
int ma_handle = INVALID_HANDLE;
int atr_handle = INVALID_HANDLE;
string swingHighLineName = "SwingHighLine";
string swingLowLineName = "SwingLowLine";
void OnInit()
{
PrintFormat("AdvancedBoS_EA_MT5 v%s - Starting Initialization", __DATE__);
PrintFormat("MagicNumber=%u, SwingTF=%d, ATR period=%d, SL ATR Mult=%d",
inpMagicNumber, swingTF, atrPeriod, sl_atr_mult);
ma_handle = iMA(_Symbol, maTimeframe, maPeriod, 0, MODE_EMA, PRICE_CLOSE);
if(ma_handle == INVALID_HANDLE)
{
Print("Failed to create EMA handle");
return;
}
atr_handle = iATR(_Symbol, volatilityTF, atrPeriod);
if(atr_handle == INVALID_HANDLE)
{
Print("Failed to create ATR handle");
IndicatorRelease(ma_handle);
return;
}
if(!ObjectCreate(0, swingHighLineName, OBJ_HLINE, 0, 0, 0))
{
Print("Failed to create swing high line");
IndicatorRelease(ma_handle);
IndicatorRelease(atr_handle);
return;
}
ObjectSetInteger(0, swingHighLineName, OBJPROP_COLOR, clrGreen);
ObjectSetInteger(0, swingHighLineName, OBJPROP_WIDTH, 2);
ObjectSetInteger(0, swingHighLineName, OBJPROP_BACK, true);
ObjectSetInteger(0, swingHighLineName, OBJPROP_RAY_RIGHT, true);
if(!ObjectCreate(0, swingLowLineName, OBJ_HLINE, 0, 0, 0))
{
Print("Failed to create swing low line");
ObjectDelete(0, swingHighLineName);
IndicatorRelease(ma_handle);
IndicatorRelease(atr_handle);
return;
}
ObjectSetInteger(0, swingLowLineName, OBJPROP_COLOR, clrRed);
ObjectSetInteger(0, swingLowLineName, OBJPROP_WIDTH, 2);
ObjectSetInteger(0, swingLowLineName, OBJPROP_BACK, true);
ObjectSetInteger(0, swingLowLineName, OBJPROP_RAY_RIGHT, true);
}
void OnDeinit(const int reason)
{
ObjectDelete(0, swingHighLineName);
ObjectDelete(0, swingLowLineName);
if(ma_handle != INVALID_HANDLE)
{
IndicatorRelease(ma_handle);
ma_handle = INVALID_HANDLE;
}
if(atr_handle != INVALID_HANDLE)
{
IndicatorRelease(atr_handle);
atr_handle = INVALID_HANDLE;
}
}
double CalculateLotSize(double sl_distance_price)
{
if(!useRiskPercent)
{
double lot_step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
double min_lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double max_lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double fixedLot = inpLot;
if(fixedLot < min_lot) fixedLot = min_lot;
if(fixedLot > max_lot) fixedLot = max_lot;
fixedLot = MathFloor(fixedLot / lot_step) * lot_step;
return fixedLot;
}
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
if(balance <= 0)
{
Print("Invalid account balance for lot size calculation");
return inpLot;
}
if(sl_distance_price <= 0)
{
Print("Invalid SL distance for lot size calculation");
return inpLot;
}
double riskMoney = balance * (riskPercent / 100.0);
if(riskMoney <= 0)
{
Print("Invalid risk money calculation");
return inpLot;
}
double tick_value = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double tick_size = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double contract_size = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_CONTRACT_SIZE);
if(tick_value <= 0 || tick_size <= 0 || contract_size <= 0)
{
Print("Invalid tick/contract size for lot calculation");
return inpLot;
}
double point_value = tick_value / tick_size;
if(point_value <=0)
{
Print("Invalid point value for lot calculation");
return inpLot;
}
double lot = riskMoney / (sl_distance_price * point_value * contract_size);
if(lot <= 0)
{
Print("Lot calculation gave non-positive result");
return inpLot;
}
double lot_step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
double min_lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double max_lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
lot = MathFloor(lot / lot_step) * lot_step;
lot = MathMax(lot, min_lot);
lot = MathMin(lot, max_lot);
return lot;
}
bool IsPositionOpen(ENUM_POSITION_TYPE type)
{
int total = PositionsTotal();
for(int i = 0; i < total; i++)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetInteger(POSITION_MAGIC) == inpMagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if(pos_type == type)
return true;
}
}
}
return false;
}
// Dynamic Market Structure Trailing Stop with half-price trigger
void UpdateTrailingStopByMarketStructure(ulong position_ticket)
{
if(!PositionSelectByTicket(position_ticket))
return;
ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double current_sl = PositionGetDouble(POSITION_SL);
double current_tp = PositionGetDouble(POSITION_TP);
double entry_price = PositionGetDouble(POSITION_PRICE_OPEN);
string symbol = PositionGetString(POSITION_SYMBOL);
if(current_tp <= 0)
return; // no valid TP set, skip trailing
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
double bid = SymbolInfoDouble(symbol, SYMBOL_BID);
double ask = SymbolInfoDouble(symbol, SYMBOL_ASK);
if(bid <= 0 || ask <= 0)
return;
if(pos_type == POSITION_TYPE_BUY)
{
double half_tp_level = entry_price + 0.5 * (current_tp - entry_price);
if(bid < half_tp_level)
return;
int lowestBar = iLowest(symbol, PERIOD_CURRENT, MODE_LOW, swingLookback, 0);
if(lowestBar < 0)
return;
double swing_low = iLow(symbol, PERIOD_CURRENT, lowestBar);
double buffer = 2 * point;
double new_sl = NormalizeDouble(swing_low - buffer, digits);
if(new_sl > current_sl && new_sl < entry_price)
{
MqlTradeRequest req = {};
MqlTradeResult res = {};
req.action = TRADE_ACTION_SLTP;
req.position = position_ticket;
req.symbol = symbol;
req.sl = new_sl;
req.tp = current_tp;
if(!OrderSend(req, res))
PrintFormat("Failed to update trailing SL for position %I64d, error %d", position_ticket, GetLastError());
else if(res.retcode != TRADE_RETCODE_DONE)
PrintFormat("Trailing SL update rejected for position %I64d, retcode %d", position_ticket, res.retcode);
else
PrintFormat("Trailing SL moved up for position %I64d: new SL=%.5f", position_ticket, new_sl);
}
}
else if(pos_type == POSITION_TYPE_SELL)
{
double half_tp_level = entry_price - 0.5 * (entry_price - current_tp);
if(ask > half_tp_level)
return;
int highestBar = iHighest(symbol, PERIOD_CURRENT, MODE_HIGH, swingLookback, 0);
if(highestBar < 0)
return;
double swing_high = iHigh(symbol, PERIOD_CURRENT, highestBar);
double buffer = 2 * point;
double new_sl = NormalizeDouble(swing_high + buffer, digits);
if(new_sl < current_sl && new_sl > entry_price)
{
MqlTradeRequest req = {};
MqlTradeResult res = {};
req.action = TRADE_ACTION_SLTP;
req.position = position_ticket;
req.symbol = symbol;
req.sl = new_sl;
req.tp = current_tp;
if(!OrderSend(req, res))
PrintFormat("Failed to update trailing SL for position %I64d, error %d", position_ticket, GetLastError());
else if(res.retcode != TRADE_RETCODE_DONE)
PrintFormat("Trailing SL update rejected for position %I64d, retcode %d", position_ticket, res.retcode);
else
PrintFormat("Trailing SL moved down for position %I64d: new SL=%.5f", position_ticket, new_sl);
}
}
}
// ATR-based Trailing Stop update function
void UpdateTrailingStopByATR(ulong position_ticket)
{
if(!PositionSelectByTicket(position_ticket))
return;
ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double current_sl = PositionGetDouble(POSITION_SL);
double entry_price = PositionGetDouble(POSITION_PRICE_OPEN);
double current_price = 0.0;
string symbol = PositionGetString(POSITION_SYMBOL);
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
// Get current ATR value for current bar on volatilityTF
double atr_buf[];
if(CopyBuffer(atr_handle, 0, 1, 1, atr_buf) <= 0)
{
PrintFormat("ATR data copy failed in trailing ATR for position %I64d", position_ticket);
return;
}
double atr = atr_buf[0];
if(atr <= 0)
return;
if(pos_type == POSITION_TYPE_BUY)
{
current_price = SymbolInfoDouble(symbol, SYMBOL_BID);
double new_sl = NormalizeDouble(current_price - atr * sl_atr_mult, digits);
// Only move SL up, never down
if(new_sl > current_sl && new_sl < current_price)
{
MqlTradeRequest req = {};
MqlTradeResult res = {};
req.action = TRADE_ACTION_SLTP;
req.position = position_ticket;
req.symbol = symbol;
req.sl = new_sl;
req.tp = PositionGetDouble(POSITION_TP);
if(!OrderSend(req, res))
PrintFormat("ATR trailing SL update failed for position %I64d, error %d", position_ticket, GetLastError());
else if(res.retcode != TRADE_RETCODE_DONE)
PrintFormat("ATR trailing SL update rejected for position %I64d, retcode %d", position_ticket, res.retcode);
else
PrintFormat("ATR trailing SL moved up for position %I64d: new SL=%.5f", position_ticket, new_sl);
}
}
else if(pos_type == POSITION_TYPE_SELL)
{
current_price = SymbolInfoDouble(symbol, SYMBOL_ASK);
double new_sl = NormalizeDouble(current_price + atr * sl_atr_mult, digits);
// Only move SL down, never up
if(new_sl < current_sl && new_sl > current_price)
{
MqlTradeRequest req = {};
MqlTradeResult res = {};
req.action = TRADE_ACTION_SLTP;
req.position = position_ticket;
req.symbol = symbol;
req.sl = new_sl;
req.tp = PositionGetDouble(POSITION_TP);
if(!OrderSend(req, res))
PrintFormat("ATR trailing SL update failed for position %I64d, error %d", position_ticket, GetLastError());
else if(res.retcode != TRADE_RETCODE_DONE)
PrintFormat("ATR trailing SL update rejected for position %I64d, retcode %d", position_ticket, res.retcode);
else
PrintFormat("ATR trailing SL moved down for position %I64d: new SL=%.5f", position_ticket, new_sl);
}
}
}
void OnTick()
{
// Trailing stop handling
if(enableTrailingSL && trailingStopMode != TrailingNone)
{
int total = PositionsTotal();
for(int i=0; i<total; i++)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetInteger(POSITION_MAGIC) == inpMagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
if(trailingStopMode == TrailingMarketStructure)
UpdateTrailingStopByMarketStructure(ticket);
else if(trailingStopMode == TrailingATR)
UpdateTrailingStopByATR(ticket);
}
}
}
}
// Count open positions by this EA on this symbol
int openPosCount = 0;
int total = PositionsTotal();
for(int i=0; i<total; i++)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetInteger(POSITION_MAGIC) == inpMagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
openPosCount++;
}
}
}
if(maxPositions > 0 && openPosCount >= maxPositions)
return;
MqlDateTime tm;
TimeToStruct(TimeCurrent(), tm);
if(tm.hour < tradeStartHour || tm.hour >= tradeEndHour)
return;
double ma_buf[];
ArrayResize(ma_buf, 1);
if(CopyBuffer(ma_handle, 0, 1, 1, ma_buf) <= 0)
{
PrintFormat("EMA data copy failed, error %d", GetLastError());
return;
}
double atr_buf[];
ArrayResize(atr_buf, 1);
if(CopyBuffer(atr_handle, 0, 1, 1, atr_buf) <= 0)
{
PrintFormat("ATR data copy failed, error %d", GetLastError());
return;
}
double ma = ma_buf[0];
double atr = atr_buf[0];
if(atr <= 0)
{
atr = 10.0 * _Point;
Print("ATR value invalid or zero, falling back to 10 points");
}
double sl_dist = atr * sl_atr_mult;
double tp_dist = sl_dist * r2r_ratio;
int highestBar = iHighest(_Symbol, swingTF, MODE_HIGH, swingLookback, 1);
int lowestBar = iLowest(_Symbol, swingTF, MODE_LOW, swingLookback, 1);
if(highestBar < 0 || lowestBar < 0)
{
Print("Error getting swing high/low bars");
return;
}
double lastSwingHigh = iHigh(_Symbol, swingTF, highestBar);
double lastSwingLow = iLow(_Symbol, swingTF, lowestBar);
ObjectSetDouble(0, swingHighLineName, OBJPROP_PRICE, lastSwingHigh);
ObjectSetDouble(0, swingLowLineName, OBJPROP_PRICE, lastSwingLow);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
if(ask <= 0 || bid <= 0)
{
Print("Invalid bid or ask price");
return;
}
double price_htf = iClose(_Symbol, maTimeframe, 1);
if(price_htf <= 0 || ma <= 0)
{
Print("Invalid price or MA value");
return;
}
bool is_uptrend = (price_htf > ma);
bool is_downtrend = (price_htf < ma);
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
double lot = CalculateLotSize(sl_dist);
if(lot < SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN))
{
PrintFormat("Calculated lot %.2f is less than minimum allowed lot", lot);
return;
}
MqlTradeRequest request = {};
MqlTradeResult result = {};
request.symbol = _Symbol;
request.volume = lot;
request.deviation = 10;
request.magic = inpMagicNumber;
if(ask > lastSwingHigh && is_uptrend)
{
if(!IsPositionOpen(POSITION_TYPE_BUY))
{
request.action = TRADE_ACTION_DEAL;
request.type = ORDER_TYPE_BUY;
request.price = ask;
request.sl = NormalizeDouble(ask - sl_dist, digits);
request.tp = NormalizeDouble(ask + tp_dist, digits);
if(!OrderSend(request, result))
PrintFormat("Buy order send failed, error %d", GetLastError());
else if(result.retcode != TRADE_RETCODE_DONE)
PrintFormat("Buy order rejected, retcode %d", result.retcode);
else
PrintFormat("Buy opened @ %.5f lot %.2f SL %.5f TP %.5f", ask, lot, request.sl, request.tp);
}
}
else if(bid < lastSwingLow && is_downtrend)
{
if(!IsPositionOpen(POSITION_TYPE_SELL))
{
request.action = TRADE_ACTION_DEAL;
request.type = ORDER_TYPE_SELL;
request.price = bid;
request.sl = NormalizeDouble(bid + sl_dist, digits);
request.tp = NormalizeDouble(bid - tp_dist, digits);
if(!OrderSend(request, result))
PrintFormat("Sell order send failed, error %d", GetLastError());
else if(result.retcode != TRADE_RETCODE_DONE)
PrintFormat("Sell order rejected, retcode %d", result.retcode);
else
PrintFormat("Sell opened @ %.5f lot %.2f SL %.5f TP %.5f", bid, lot, request.sl, request.tp);
}
}
}
```
---
### Explanation of major changes:
- Added:
```mql5
enum ENUM_TrailingStopMode
{
TrailingNone = 0,
TrailingMarketStructure = 1,
TrailingATR = 2
};
input ENUM_TrailingStopMode trailingStopMode = TrailingMarketStructure;
```
- Added function `UpdateTrailingStopByATR()` implementing ATR-based trailing stop logic.
- Modified `OnTick()` to:
- Call appropriate trailing stop function depending on `trailingStopMode`.
- Skip trailing stop update if `TrailingNone` selected.
---
If you want me to help with testing tips or any other feature, just let me know!fef8ddf024fb23aabce96254for this trailingStopMode how can improve?
Your current implementation of the trailing stop mode enum and the respective function calls is solid and follows MQL5 best practices. However, there are always ways to **improve usability, performance, flexibility, and code maintenance**.
Here are some concrete suggestions for improving the `trailingStopMode` feature:
---
### 1. **Add Descriptions to Inputs for Better User Interface**
In MT5, when you declare enums for inputs, you can define descriptive names to appear in the terminal inputs dialog. Use the `#property` or community standard approach with `ENUM_INPUT`
Example:
```mql5
enum ENUM_TrailingStopMode
{
TrailingNone = 0, // No trailing stop
TrailingMarketStructure = 1, // Trailing by market structure swings
TrailingATR = 2 // Trailing by ATR-based stop loss
};
input ENUM_TrailingStopMode trailingStopMode = TrailingMarketStructure;
```
You can add comment descriptions like above that show in the inputs dialog.
---
### 2. **Add Configurable Parameters for Each Trailing Mode**
Right now, the ATR trailing stop uses `sl_atr_mult` which is shared with fixed SL calculation. You might want to add distinct parameters per mode for **more control**:
E.g.,
```mql5
input int ms_swingLookback = 20; // Swing trailing lookback bars (separate from trade entry)
input double atr_trail_mult = 2.5; // ATR trailing multiplier (can differ from SL multiplier)
input int trailing_update_interval = 1; // Update trailing stops every N ticks to reduce CPU load
```
This lets users tweak trailing modes independently for optimal results.
---
### 3. **Performance Optimization — Throttling Trailing Stop Checks**
Trailing stops are often updated every tick, but this can result in many order modification requests which might be rejected or overload the broker.
Add a **time or tick-based interval** to limit how often trailing stops update, for example:
```mql5
datetime lastTrailUpdate = 0;
input uint trailCheckIntervalSeconds = 5; // Check trailing stops every 5 seconds
void OnTick()
{
datetime now = TimeCurrent();
if(enableTrailingSL && trailingStopMode != TrailingNone)
{
if(now - lastTrailUpdate < trailCheckIntervalSeconds)
return; // Skip this tick, too soon
lastTrailUpdate = now;
// Proceed to trailing stop logic...
}
// Rest of OnTick
}
```
This prevents unnecessary SL update attempts and keeps broker/server communication efficient.
---
### 4. **Refactor Trailing Stop Logic for Extensibility**
Right now you have two separate trailing stop functions:
- `UpdateTrailingStopByMarketStructure()`
- `UpdateTrailingStopByATR()`
If you anticipate adding more modes later (e.g., Fixed Points trailing, Indicator-based trailing), consider:
- Create an interface (well, in MQL5 this means a function pointer or using a switch) for trailing stop update.
Example:
```mql5
typedef void (*TrailingFunction)(ulong);
TrailingFunction TrailingFunc = NULL;
void InitializeTrailingFunc()
{
switch(trailingStopMode)
{
case TrailingMarketStructure:
TrailingFunc = &UpdateTrailingStopByMarketStructure;
break;
case TrailingATR:
TrailingFunc = &UpdateTrailingStopByATR;
break;
default:
TrailingFunc = NULL;
}
}
void OnTick()
{
if(enableTrailingSL && TrailingFunc != NULL)
{
int total = PositionsTotal();
for(int i=0; i<total; i++)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetInteger(POSITION_MAGIC) == inpMagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
TrailingFunc(ticket);
}
}
}
}
// rest of OnTick...
}
```
Call `InitializeTrailingFunc()` once during `OnInit()`. This pattern improves clarity & scalability.
---
### 5. **Add Safety Checks & Logging**
- Add debug or conditional logging levels so that users can enable/disable verbose trailing stop logs.
- Handle cases where ATS or Market Structure trailing stop updates fail repeatedly—maybe implement a retry counter or alert.
---
### 6. **Improve ATR Trailing Logic**
- Right now ATR trailing stop is calculated only on current price - ATR * multiplier.
- You might improve to use **ATR + some buffer** or add some minimum SL distance to avoid SL “ping-ponging”.
- Consider trailing only if the move beyond SL is significant enough (e.g., last SL moved more than X points).
---
### 7. **User-friendly Input for Disable Trailing via Enum**
You already have:
```mql5
enableTrailingSL = true;
trailingStopMode = TrailingMarketStructure; // could be None too
```
You could simplify the UI by **combining these?**
For example, remove `enableTrailingSL`, and if user selects `TrailingNone` it effectively disables trailing stop:
```mql5
input ENUM_TrailingStopMode trailingStopMode = TrailingNone;
```
Remove `enableTrailingSL` to avoid confusion.
---
### 8. **Allow Hybrid Trailing Mode**
Optionally, implement hybrid trailing, e.g. combine both modes: start with Market Structure trailing then switch to ATR trailing after some progress or vice versa. You can add such logic later by enhancing the mode enum or adding a separate boolean.
---
## **Summary of improvements**
| Improvement | Benefit |
|---------------------------------|------------------------------------------|
| Enum input descriptive comments | Better UX in MT5 input dialog |
| Separate mode parameters | Allows finer tuning per trailing method |
| Trailing stop update throttling | Reduces CPU and broker load |
| Function pointer for trailing fn | Cleaner, scalable code design |
| Logging & retry mechanisms | Easier debugging and robustness |
| Smarter ATR trailing logic | Avoid trailing SL ping-pong |
| Remove redundant enable flag | Avoid user confusion |
| Optional hybrid trailing mode | More strategy flexibility |
---
If you want, I can help rewrite your code including **all or some** of these improvements — just say so!