Files
MyProEA/Include/TrailingStop.mqh
T

255 lines
8.1 KiB
Plaintext

//+------------------------------------------------------------------+
//| TrailingStop.mqh - Trailing stop and break-even management |
//| Modifies SL based on profit targets |
//+------------------------------------------------------------------+
#ifndef __TRAILINGSTOP_MQH__
#define __TRAILINGSTOP_MQH__
#include "Config.mqh"
#include "Logger.mqh"
#include "MarketData.mqh"
#include "Utilities.mqh"
#include "TradeManager.mqh"
class CTrailingStop
{
private:
CMarketData *mp_market_data;
CLogger *mp_logger;
CTradeManager *mp_trade_manager;
public:
// Constructor
CTrailingStop(CMarketData *market_data, CLogger *logger, CTradeManager *trade_manager)
{
mp_market_data = market_data;
mp_logger = logger;
mp_trade_manager = trade_manager;
}
// Update trailing stop for all positions
void UpdateAllPositions()
{
if(!g_use_trailing_stop && !g_use_break_even && !g_use_trailing_stop_atr && !g_use_break_even_atr)
return;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(PositionSelectByTicket(PositionGetTicket(i)))
{
if(PositionGetString(POSITION_SYMBOL) == mp_market_data.GetSymbol() &&
PositionGetInteger(POSITION_MAGIC) == g_magic_number)
{
UpdatePosition(PositionGetTicket(i));
}
}
}
}
// Update trailing stop for a single position
void UpdatePosition(ulong ticket)
{
if(ticket == 0)
return;
if(!PositionSelectByTicket(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 open_price = PositionGetDouble(POSITION_PRICE_OPEN);
double profit = PositionGetDouble(POSITION_PROFIT);
double new_sl = current_sl;
double new_tp = current_tp;
bool need_modify = false;
// Apply break-even logic first
if(g_use_break_even)
{
if(ApplyBreakEven(pos_type, open_price, current_sl, profit, new_sl))
{
need_modify = true;
}
}
// Apply trailing stop logic
if(g_use_trailing_stop)
{
if(ApplyTrailingStop(pos_type, current_sl, new_sl))
{
need_modify = true;
}
}
// Only modify if SL changed
if(need_modify && new_sl != current_sl)
{
mp_trade_manager.ModifyPosition(ticket, new_sl, new_tp);
}
}
private:
// Apply break-even logic
bool ApplyBreakEven(ENUM_POSITION_TYPE pos_type, double open_price,
double current_sl, double profit, double &new_sl)
{
string symbol = mp_market_data.GetSymbol();
double current_price = (pos_type == POSITION_TYPE_BUY) ? mp_market_data.GetBid() : mp_market_data.GetAsk();
double price_move = 0.0;
if(pos_type == POSITION_TYPE_BUY)
price_move = current_price - open_price;
else if(pos_type == POSITION_TYPE_SELL)
price_move = open_price - current_price;
else
return false;
if(current_sl <= 0.0)
return false;
double initial_risk = (pos_type == POSITION_TYPE_BUY)
? (open_price - current_sl)
: (current_sl - open_price);
if(initial_risk <= 0.0)
return false;
double trigger_distance = g_break_even_trigger_r * initial_risk;
if(price_move < trigger_distance)
return false;
double buffer = CUtilities::PointsToPrice(symbol, g_break_even_buffer_points);
double point = CUtilities::GetPoint(symbol);
long min_stop_points = (long)SymbolInfoInteger(symbol, SYMBOL_TRADE_STOPS_LEVEL);
double min_stop_distance = min_stop_points * point;
if(pos_type == POSITION_TYPE_BUY)
{
double be_sl = CUtilities::NormalizePrice(symbol, open_price + buffer);
if((mp_market_data.GetBid() - be_sl) < min_stop_distance)
{
if(mp_logger && g_debug_mode)
mp_logger.Info("Break-even skipped: would violate broker minimal stop distance");
return false;
}
if(be_sl > current_sl)
{
new_sl = be_sl;
if(mp_logger && g_debug_mode)
mp_logger.Info(StringFormat("Break-even applied: BUY SL moved to %.5f", be_sl));
return true;
}
}
else if(pos_type == POSITION_TYPE_SELL)
{
double be_sl = CUtilities::NormalizePrice(symbol, open_price - buffer);
if((be_sl - mp_market_data.GetAsk()) < min_stop_distance)
{
if(mp_logger && g_debug_mode)
mp_logger.Info("Break-even skipped: would violate broker minimal stop distance");
return false;
}
if(be_sl < current_sl)
{
new_sl = be_sl;
if(mp_logger && g_debug_mode)
mp_logger.Info(StringFormat("Break-even applied: SELL SL moved to %.5f", be_sl));
return true;
}
}
return false;
}
// Apply trailing stop logic
bool ApplyTrailingStop(ENUM_POSITION_TYPE pos_type, double current_sl, double &new_sl)
{
string symbol = mp_market_data.GetSymbol();
double bid = mp_market_data.GetBid();
double ask = mp_market_data.GetAsk();
if(current_sl <= 0.0)
return false;
double open_price = PositionGetDouble(POSITION_PRICE_OPEN);
double initial_risk = (pos_type == POSITION_TYPE_BUY)
? (open_price - current_sl)
: (current_sl - open_price);
if(initial_risk <= 0.0)
return false;
double price = (pos_type == POSITION_TYPE_BUY) ? bid : ask;
double price_move = (pos_type == POSITION_TYPE_BUY) ? (price - open_price) : (open_price - price);
double trail_start_distance = g_trail_start_r * initial_risk;
if(price_move < trail_start_distance)
return false;
int atr_handle = iATR(symbol, g_strategy_entry_timeframe, g_atr_period);
if(atr_handle == INVALID_HANDLE)
return false;
double atr_value[];
ArraySetAsSeries(atr_value, true);
ArrayResize(atr_value, 1);
if(CopyBuffer(atr_handle, 0, 1, 1, atr_value) <= 0)
{
IndicatorRelease(atr_handle);
return false;
}
IndicatorRelease(atr_handle);
double trail_distance = atr_value[0] * g_trail_distance_atr_multiplier;
if(trail_distance <= 0.0)
return false;
double point = CUtilities::GetPoint(symbol);
long min_stop_points = (long)SymbolInfoInteger(symbol, SYMBOL_TRADE_STOPS_LEVEL);
double min_stop_distance = min_stop_points * point;
if(pos_type == POSITION_TYPE_BUY)
{
double candidate_sl = CUtilities::NormalizePrice(symbol, bid - trail_distance);
if((bid - candidate_sl) < min_stop_distance)
{
if(mp_logger && g_debug_mode)
mp_logger.Info("Trailing stop skipped: candidate SL too close to price (broker stop level)");
return false;
}
if(candidate_sl > current_sl)
{
new_sl = candidate_sl;
if(mp_logger && g_debug_mode)
mp_logger.Info(StringFormat("Trailing stop applied: BUY SL moved to %.5f", candidate_sl));
return true;
}
}
else if(pos_type == POSITION_TYPE_SELL)
{
double candidate_sl = CUtilities::NormalizePrice(symbol, ask + trail_distance);
if((candidate_sl - ask) < min_stop_distance)
{
if(mp_logger && g_debug_mode)
mp_logger.Info("Trailing stop skipped: candidate SL too close to price (broker stop level)");
return false;
}
if(candidate_sl < current_sl)
{
new_sl = candidate_sl;
if(mp_logger && g_debug_mode)
mp_logger.Info(StringFormat("Trailing stop applied: SELL SL moved to %.5f", candidate_sl));
return true;
}
}
return false;
}
};
#endif //__TRAILINGSTOP_MQH__