ASSISTANT
Certainly! Here is the entire updated EA code integrating your existing logic along with the **hybrid trailing stop mode** featuring:
- Trailing stop update throttling (15 seconds by default)
- Minimum SL buffer (1 ATR multiple)
- Trailing only triggers once price moves 50% into profit
- You can select trailing mode: None, Market Structure, ATR, or Hybrid
---
```mql5
//+------------------------------------------------------------------+
//| Advanced_BoS_EA_MT5.mq5 |
//| Author: LucienWP |
//| Version: 1.4 |
//| Published: 2025-05-XX |
//| Description: Break Of Structure EA |
//| with dynamic lot sizing, dynamic trailing stops (market structure|
//| or ATR or hybrid), throttling and safety buffers |
//+------------------------------------------------------------------+
#property copyright "LucienWP"
#property version "1.4"
#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 (initial SL distance)
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 (trade entries)
input ENUM_TIMEFRAMES swingTF = PERIOD_M1; // Swing detection timeframe for entries
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)
// Trailing stop enable and mode
input bool enableTrailingSL = true; // Enable trailing stop
enum ENUM_TrailingStopMode
{
TrailingNone = 0, // No trailing stop
TrailingMarketStructure = 1, // Trailing by market structure swings
TrailingATR = 2, // Trailing by ATR-based stop loss
TrailingHybrid = 3 // Hybrid trailing: market structure + ATR
};
input ENUM_TrailingStopMode trailingStopMode = TrailingHybrid; // Trailing stop mode selector
// New inputs for trailing settings (separate from entry/trade)
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)
// Trailing enhancements
input uint TrailUpdateIntervalSec = 15; // Minimum seconds between trailing stop updates per position
input double MinSLBufferATRMult = 1.0; // Minimum SL distance buffer in ATR multiples
input double TrailingProfitTriggerPerc = 0.5; // Trailing activates once price moves this fraction of SL distance in profit
CTrade trade;
int ma_handle = INVALID_HANDLE;
int atr_handle = INVALID_HANDLE;
string swingHighLineName = "SwingHighLine";
string swingLowLineName = "SwingLowLine";
struct TrailingUpdateInfo
{
ulong position_ticket;
datetime last_update;
};
TrailingUpdateInfo g_trailingUpdateInfo[];
//+------------------------------------------------------------------+
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;
}
//+------------------------------------------------------------------+
bool CanUpdateTrailing(ulong ticket)
{
datetime now = TimeCurrent();
int count = ArraySize(g_trailingUpdateInfo);
for(int i=0; i<count; i++)
{
if(g_trailingUpdateInfo[i].position_ticket == ticket)
{
if(now - g_trailingUpdateInfo[i].last_update < (int)TrailUpdateIntervalSec)
return false;
g_trailingUpdateInfo[i].last_update = now;
return true;
}
}
// If ticket not tracked yet
ArrayResize(g_trailingUpdateInfo, count + 1);
g_trailingUpdateInfo[count].position_ticket = ticket;
g_trailingUpdateInfo[count].last_update = now;
return true;
}
//+------------------------------------------------------------------+
// Market Structure trailing stop update function
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
if(!CanUpdateTrailing(position_ticket))
return; // throttle updates
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, ms_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, ms_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 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);
string symbol = PositionGetString(POSITION_SYMBOL);
if(!CanUpdateTrailing(position_ticket))
return; // throttle updates
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
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;
double current_price = (pos_type == POSITION_TYPE_BUY) ? SymbolInfoDouble(symbol, SYMBOL_BID)
: SymbolInfoDouble(symbol, SYMBOL_ASK);
if(current_price <= 0) return;
double new_sl = 0;
if(pos_type == POSITION_TYPE_BUY)
{
new_sl = NormalizeDouble(current_price - atr * atr_trail_mult, digits);
double min_sl_dist = atr * MinSLBufferATRMult;
if(new_sl < (current_price - min_sl_dist))
new_sl = current_price - min_sl_dist;
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)
{
new_sl = NormalizeDouble(current_price + atr * atr_trail_mult, digits);
double min_sl_dist = atr * MinSLBufferATRMult;
if(new_sl > (current_price + min_sl_dist))
new_sl = current_price + min_sl_dist;
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);
}
}
}
//+------------------------------------------------------------------+
// Hybrid trailing stop update combining Market Structure and ATR stops
void UpdateTrailingStopHybrid(ulong position_ticket)
{
if(!PositionSelectByTicket(position_ticket))
return;
if(!CanUpdateTrailing(position_ticket))
return; // throttle trailing updates
ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double sl = PositionGetDouble(POSITION_SL);
double tp = PositionGetDouble(POSITION_TP);
double entry_price = PositionGetDouble(POSITION_PRICE_OPEN);
string symbol = PositionGetString(POSITION_SYMBOL);
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
double atr_buf[];
if(CopyBuffer(atr_handle, 0, 1, 1, atr_buf) <= 0)
{
PrintFormat("ATR data copy failed in trailing update for position %I64d", position_ticket);
return;
}
double atr = atr_buf[0];
if(atr <= 0)
return;
double min_sl_distance = atr * MinSLBufferATRMult;
double initial_sl_dist = (pos_type == POSITION_TYPE_BUY) ? (entry_price - sl) : (sl - entry_price);
if(initial_sl_dist <= 0) initial_sl_dist = atr * sl_atr_mult; // fallback
double current_price = (pos_type == POSITION_TYPE_BUY) ? SymbolInfoDouble(symbol, SYMBOL_BID)
: SymbolInfoDouble(symbol, SYMBOL_ASK);
if(current_price <= 0) return;
double move_in_profit = (pos_type == POSITION_TYPE_BUY) ? (current_price - entry_price) : (entry_price - current_price);
if(move_in_profit < TrailingProfitTriggerPerc * initial_sl_dist)
return; // not enough profit to start trailing
// Market Structure trailing SL candidate
double ms_new_sl = 0;
bool ms_sl_valid = false;
if(pos_type == POSITION_TYPE_BUY)
{
int lowestBar = iLowest(symbol, PERIOD_CURRENT, MODE_LOW, ms_swingLookback, 0);
if(lowestBar >= 0)
{
double swing_low = iLow(symbol, PERIOD_CURRENT, lowestBar);
ms_new_sl = NormalizeDouble(swing_low - 2*point, digits);
ms_sl_valid = (ms_new_sl > sl) && (ms_new_sl < entry_price);
}
}
else if(pos_type == POSITION_TYPE_SELL)
{
int highestBar = iHighest(symbol, PERIOD_CURRENT, MODE_HIGH, ms_swingLookback, 0);
if(highestBar >= 0)
{
double swing_high = iHigh(symbol, PERIOD_CURRENT, highestBar);
ms_new_sl = NormalizeDouble(swing_high + 2*point, digits);
ms_sl_valid = (ms_new_sl < sl) && (ms_new_sl > entry_price);
}
}
// ATR trailing SL candidate
double atr_new_sl = 0;
if(pos_type == POSITION_TYPE_BUY)
{
atr_new_sl = NormalizeDouble(current_price - atr * atr_trail_mult, digits);
atr_new_sl = MathMax(atr_new_sl, current_price - min_sl_distance);
atr_new_sl = (atr_new_sl > sl && atr_new_sl < current_price) ? atr_new_sl : 0;
}
else if(pos_type == POSITION_TYPE_SELL)
{
atr_new_sl = NormalizeDouble(current_price + atr * atr_trail_mult, digits);
atr_new_sl = MathMin(atr_new_sl, current_price + min_sl_distance);
atr_new_sl = (atr_new_sl < sl && atr_new_sl > current_price) ? atr_new_sl : 0;
}
double new_sl = 0;
if(ms_sl_valid && atr_new_sl > 0)
{
if(pos_type == POSITION_TYPE_BUY)
new_sl = MathMax(ms_new_sl, atr_new_sl);
else
new_sl = MathMin(ms_new_sl, atr_new_sl);
}
else if(ms_sl_valid)
new_sl = ms_new_sl;
else if(atr_new_sl > 0)
new_sl = atr_new_sl;
else
return; // no valid new SL
bool update_sl = false;
if(pos_type == POSITION_TYPE_BUY)
update_sl = (new_sl > sl && new_sl < current_price);
else
update_sl = (new_sl < sl && new_sl > current_price);
if(update_sl)
{
MqlTradeRequest req={};
MqlTradeResult res={};
req.action = TRADE_ACTION_SLTP;
req.position = position_ticket;
req.symbol = symbol;
req.sl = new_sl;
req.tp = tp;
if(!OrderSend(req,res))
PrintFormat("Trailing SL update failed for position %I64d, err=%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 updated for position %I64d: old SL=%.5f new SL=%.5f", position_ticket, sl, new_sl);
}
}
//+------------------------------------------------------------------+
void OnTick()
{
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);
else if(trailingStopMode == TrailingHybrid)
UpdateTrailingStopHybrid(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; // Initial SL distance for entry orders (use original multiplier)
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);
}
}
}
```
---
### How to use:
- Compile and run on MT5.
- Select trailing stop mode and trailing parameters in the inputs.
- The hybrid trailing combines the best of Market Structure and ATR trailing.
- Trailing updates only happen once every 15 seconds per position to reduce broker load.
- Trailing minimum SL distance and activation profit thresholds help stabilize trailing behavior.
---
If you want me to help further customize, add logging, or implement other ideas, just ask!