2025-05-30 16:35:54 +02:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| CTrailingATR.mqh |
|
|
|
|
|
//| AnimateDread |
|
|
|
|
|
//| https://tawarriors.com|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#property copyright "AnimateDread"
|
|
|
|
|
#include <Expert\ExpertTrailing.mqh>
|
2026-07-26 23:08:32 -04:00
|
|
|
#include "..\System\TradeChecks.mqh"
|
2025-05-30 16:35:54 +02:00
|
|
|
// wizard description start
|
|
|
|
|
//+----------------------------------------------------------------------+
|
|
|
|
|
//| Description of the class |
|
|
|
|
|
//| Title=Trailing Stop based on ATR Indicator |
|
|
|
|
|
//| Type=Trailing |
|
|
|
|
|
//| Name=ATR |
|
|
|
|
|
//| Class=CTrailingATR |
|
|
|
|
|
//| Page= |
|
|
|
|
|
//| Parameter=Multiplier,double,2, ATR Multiplier |
|
|
|
|
|
//| Parameter=Periods,int,14, ATR Periods |
|
|
|
|
|
//| Parameter=Shift,int,0, ATR Shift |
|
|
|
|
|
//+----------------------------------------------------------------------+
|
|
|
|
|
// wizard description end
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Class CTrailingATR. |
|
|
|
|
|
//| Purpose: Class of trailing stops based on ATR * Multiplier. |
|
|
|
|
|
//| Derives from class CExpertTrailing. |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
class CTrailingATR : public CExpertTrailing
|
|
|
|
|
{
|
|
|
|
|
protected:
|
|
|
|
|
CiATR m_ATR; // ATR indicator
|
|
|
|
|
//--- input parameters
|
|
|
|
|
double m_multiplier; // Configurable multiple for ATR
|
|
|
|
|
int m_periods; // Configurable periods for ATR
|
|
|
|
|
int m_shift; // Configurable shift for ATR
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
CTrailingATR(void);
|
|
|
|
|
~CTrailingATR(void);
|
|
|
|
|
//--- methods of initialization of protected data
|
|
|
|
|
void Multiplier(double multiplier) { m_multiplier = multiplier; }
|
|
|
|
|
void Periods(int periods) { m_periods = periods; }
|
|
|
|
|
void Shift(int shift) { m_shift = shift; }
|
|
|
|
|
|
|
|
|
|
virtual bool InitIndicators(CIndicators* indicators);
|
|
|
|
|
virtual bool ValidationSettings();
|
|
|
|
|
virtual bool CheckTrailingStopLong(CPositionInfo* position, double& sl, double& tp);
|
|
|
|
|
virtual bool CheckTrailingStopShort(CPositionInfo* position, double& sl, double& tp);
|
|
|
|
|
protected:
|
|
|
|
|
bool AdjustStopLoss(double& sl, double currentPrice, double atrValue, bool isLong);
|
refactor(yagni): drop 13 accessors nothing called; unify the ATR trailing pair
Verified dead by grep across all first-party sources (references/, Scripts/,
research/ excluded): EraCount, HiddenLayersCount, LstmHiddenSize, ConvFilterCount,
HistoryBars and MinTrainYear setters, PendingBatchSamples, getPrevOutIndex,
BaseCurrency, QuoteCurrency, CurrencyCount, IsLoaded, LastFiredDirection,
DBConfidence, SpecIndex, and the conv Step/WindowOut shape accessors. Every
backing member stays - each is still read internally and several are pinned by
the positional .cfg layout - so this removes surface, not behaviour.
Two comments were asserting the opposite of the code and are now true: the
"No setter: the taper's endpoints are derived" note was directly above three
setters, and the conv shape block claimed EnforceTopologyContract reads all
three accessors when CNet::FirstConvWindow only ever calls Window().
CTrailingATR::CheckTrailingStopLong/Short were byte-identical but for Bid vs Ask
and the isLong flag; both now delegate to one CheckTrailingStop body.
Deliberately NOT removed: the fractal-target branch (TrainTargetFractal,
IsFractalTarget and their label machinery). It reads as dead because the
TrainingTarget input was withdrawn, but Warrior_EA.mq5:836 documents it as a
parked option with a three-line restore path - that is a product call, not a
refactor.
Not compiled - MetaEditor compile pending.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 18:55:36 -04:00
|
|
|
bool CheckTrailingStop(CPositionInfo* position, double& sl, double& tp, bool isLong);
|
2025-05-30 16:35:54 +02:00
|
|
|
};
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Constructor |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void CTrailingATR::CTrailingATR(void) :
|
|
|
|
|
m_multiplier(2),
|
|
|
|
|
m_periods(14),
|
|
|
|
|
m_shift(0)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Destructor |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void CTrailingATR::~CTrailingATR(void)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Validation settings protected data. |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CTrailingATR::ValidationSettings()
|
|
|
|
|
{
|
|
|
|
|
if(!CExpertTrailing::ValidationSettings())
|
|
|
|
|
return (false);
|
|
|
|
|
// Check multiplier
|
|
|
|
|
if(m_multiplier <= 0.0 || m_multiplier > 100)
|
|
|
|
|
{
|
|
|
|
|
printf(__FUNCTION__ + ": multiplier must be greater than 0 and lesser than 100");
|
|
|
|
|
return (false);
|
|
|
|
|
}
|
|
|
|
|
// Check ATR Periods
|
|
|
|
|
if(m_periods <= 0 || m_periods > 200)
|
|
|
|
|
{
|
|
|
|
|
printf(__FUNCTION__ + ": ATR Periods must be greater than 0 and lesser than 200");
|
|
|
|
|
return (false);
|
|
|
|
|
}
|
|
|
|
|
// Check ATR shift
|
|
|
|
|
if(m_shift < 0 || m_shift > 200)
|
|
|
|
|
{
|
|
|
|
|
printf(__FUNCTION__ + ": ATR shift must be 0-200");
|
|
|
|
|
return (false);
|
|
|
|
|
}
|
|
|
|
|
//--- ok
|
|
|
|
|
return (true);
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Checking for input parameters and setting protected data. |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CTrailingATR::InitIndicators(CIndicators* indicators)
|
|
|
|
|
{
|
|
|
|
|
if(indicators == NULL)
|
|
|
|
|
return (false);
|
|
|
|
|
// Add ATR indicator to the collection
|
|
|
|
|
if(!indicators.Add(GetPointer(m_ATR)))
|
|
|
|
|
{
|
|
|
|
|
printf(__FUNCTION__ + ": error adding object");
|
|
|
|
|
return (false);
|
|
|
|
|
}
|
|
|
|
|
// Initialize ATR indicator
|
|
|
|
|
if(!m_ATR.Create(m_symbol.Name(), m_period, m_periods))
|
|
|
|
|
{
|
|
|
|
|
return (false);
|
|
|
|
|
}
|
|
|
|
|
//--- ok
|
|
|
|
|
return (true);
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Common logic for adjusting SL considering freeze level |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CTrailingATR::AdjustStopLoss(double& sl, double currentPrice, double atrValue, bool isLong)
|
|
|
|
|
{
|
2026-07-26 12:12:14 -04:00
|
|
|
// Brokers often set SYMBOL_TRADE_STOPS_LEVEL independently of (and sometimes larger than)
|
|
|
|
|
// SYMBOL_TRADE_FREEZE_LEVEL - validating only against the freeze level let a computed new_sl pass
|
|
|
|
|
// here yet still be inside the broker's minimum-stop-distance zone, so the eventual
|
|
|
|
|
// PositionModify() outside this function would get rejected with nothing logged here to explain it.
|
2026-07-26 23:08:32 -04:00
|
|
|
// TCMinStopDistance() is the shared max(stops, freeze) rule from System\TradeChecks.mqh (article
|
|
|
|
|
// 2555 #6/#7); it also floors the stops level at the current spread, which brokers that publish a
|
|
|
|
|
// 0 SYMBOL_TRADE_STOPS_LEVEL and enforce a floating spread-derived limit instead require.
|
|
|
|
|
double minDistance = TCMinStopDistance(m_symbol.Name());
|
2025-05-30 16:35:54 +02:00
|
|
|
int digits = m_symbol.Digits(); // Get the number of digits after the decimal for the instrument
|
|
|
|
|
// Calculate new SL based on position type (Long or Short)
|
|
|
|
|
double new_sl = isLong ? NormalizeDouble(currentPrice - atrValue * m_multiplier, digits)
|
|
|
|
|
: NormalizeDouble(currentPrice + atrValue * m_multiplier, digits);
|
2026-07-26 12:12:14 -04:00
|
|
|
// Calculate the level beyond which SL cannot be set due to freeze/stops level
|
|
|
|
|
double level = isLong ? currentPrice - minDistance : currentPrice + minDistance;
|
2025-05-30 16:35:54 +02:00
|
|
|
// Check if new SL is in the correct direction and respects the freeze level
|
|
|
|
|
bool isSlValid = isLong ? (new_sl > sl && new_sl < level) : (new_sl < sl && new_sl > level);
|
|
|
|
|
if(isSlValid)
|
|
|
|
|
{
|
|
|
|
|
sl = new_sl;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Checking trailing stop and/or profit for long position. |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CTrailingATR::CheckTrailingStopLong(CPositionInfo* position, double& sl, double& tp)
|
|
|
|
|
{
|
refactor(yagni): drop 13 accessors nothing called; unify the ATR trailing pair
Verified dead by grep across all first-party sources (references/, Scripts/,
research/ excluded): EraCount, HiddenLayersCount, LstmHiddenSize, ConvFilterCount,
HistoryBars and MinTrainYear setters, PendingBatchSamples, getPrevOutIndex,
BaseCurrency, QuoteCurrency, CurrencyCount, IsLoaded, LastFiredDirection,
DBConfidence, SpecIndex, and the conv Step/WindowOut shape accessors. Every
backing member stays - each is still read internally and several are pinned by
the positional .cfg layout - so this removes surface, not behaviour.
Two comments were asserting the opposite of the code and are now true: the
"No setter: the taper's endpoints are derived" note was directly above three
setters, and the conv shape block claimed EnforceTopologyContract reads all
three accessors when CNet::FirstConvWindow only ever calls Window().
CTrailingATR::CheckTrailingStopLong/Short were byte-identical but for Bid vs Ask
and the isLong flag; both now delegate to one CheckTrailingStop body.
Deliberately NOT removed: the fractal-target branch (TrainTargetFractal,
IsFractalTarget and their label machinery). It reads as dead because the
TrainingTarget input was withdrawn, but Warrior_EA.mq5:836 documents it as a
parked option with a three-line restore path - that is a product call, not a
refactor.
Not compiled - MetaEditor compile pending.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 18:55:36 -04:00
|
|
|
return CheckTrailingStop(position, sl, tp, true);
|
2025-05-30 16:35:54 +02:00
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Checking trailing stop and/or profit for short position. |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CTrailingATR::CheckTrailingStopShort(CPositionInfo* position, double& sl, double& tp)
|
|
|
|
|
{
|
refactor(yagni): drop 13 accessors nothing called; unify the ATR trailing pair
Verified dead by grep across all first-party sources (references/, Scripts/,
research/ excluded): EraCount, HiddenLayersCount, LstmHiddenSize, ConvFilterCount,
HistoryBars and MinTrainYear setters, PendingBatchSamples, getPrevOutIndex,
BaseCurrency, QuoteCurrency, CurrencyCount, IsLoaded, LastFiredDirection,
DBConfidence, SpecIndex, and the conv Step/WindowOut shape accessors. Every
backing member stays - each is still read internally and several are pinned by
the positional .cfg layout - so this removes surface, not behaviour.
Two comments were asserting the opposite of the code and are now true: the
"No setter: the taper's endpoints are derived" note was directly above three
setters, and the conv shape block claimed EnforceTopologyContract reads all
three accessors when CNet::FirstConvWindow only ever calls Window().
CTrailingATR::CheckTrailingStopLong/Short were byte-identical but for Bid vs Ask
and the isLong flag; both now delegate to one CheckTrailingStop body.
Deliberately NOT removed: the fractal-target branch (TrainTargetFractal,
IsFractalTarget and their label machinery). It reads as dead because the
TrainingTarget input was withdrawn, but Warrior_EA.mq5:836 documents it as a
parked option with a three-line restore path - that is a product call, not a
refactor.
Not compiled - MetaEditor compile pending.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 18:55:36 -04:00
|
|
|
return CheckTrailingStop(position, sl, tp, false);
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Shared body of both directions. A long trails off the Bid, a |
|
|
|
|
|
//| short off the Ask; everything else is identical. |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CTrailingATR::CheckTrailingStop(CPositionInfo* position, double& sl, double& tp, bool isLong)
|
|
|
|
|
{
|
|
|
|
|
sl = EMPTY_VALUE;
|
|
|
|
|
tp = EMPTY_VALUE;
|
2025-05-30 16:35:54 +02:00
|
|
|
if(position == NULL)
|
|
|
|
|
return false;
|
|
|
|
|
double new_sl = position.StopLoss();
|
refactor(yagni): drop 13 accessors nothing called; unify the ATR trailing pair
Verified dead by grep across all first-party sources (references/, Scripts/,
research/ excluded): EraCount, HiddenLayersCount, LstmHiddenSize, ConvFilterCount,
HistoryBars and MinTrainYear setters, PendingBatchSamples, getPrevOutIndex,
BaseCurrency, QuoteCurrency, CurrencyCount, IsLoaded, LastFiredDirection,
DBConfidence, SpecIndex, and the conv Step/WindowOut shape accessors. Every
backing member stays - each is still read internally and several are pinned by
the positional .cfg layout - so this removes surface, not behaviour.
Two comments were asserting the opposite of the code and are now true: the
"No setter: the taper's endpoints are derived" note was directly above three
setters, and the conv shape block claimed EnforceTopologyContract reads all
three accessors when CNet::FirstConvWindow only ever calls Window().
CTrailingATR::CheckTrailingStopLong/Short were byte-identical but for Bid vs Ask
and the isLong flag; both now delegate to one CheckTrailingStop body.
Deliberately NOT removed: the fractal-target branch (TrainTargetFractal,
IsFractalTarget and their label machinery). It reads as dead because the
TrainingTarget input was withdrawn, but Warrior_EA.mq5:836 documents it as a
parked option with a three-line restore path - that is a product call, not a
refactor.
Not compiled - MetaEditor compile pending.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 18:55:36 -04:00
|
|
|
double price = isLong ? m_symbol.Bid() : m_symbol.Ask();
|
|
|
|
|
//--- AdjustStopLoss honours the freeze level; only publish sl when it actually moved
|
|
|
|
|
if(!AdjustStopLoss(new_sl, price, m_ATR.Main(m_shift), isLong))
|
|
|
|
|
return false;
|
|
|
|
|
sl = new_sl;
|
|
|
|
|
return true;
|
2025-05-30 16:35:54 +02:00
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|