Warrior_EA/Signals/SignalDTDB.mqh
super.admin 0a527b0cf9 convert
2025-05-30 16:35:54 +02:00

190 lines
7.5 KiB
MQL5

//+------------------------------------------------------------------+
//| SignalDTDB.mqh |
//+------------------------------------------------------------------+
#property copyright "AnimateDread"
#property version "1.0"
#property description "Double Tops and Bottoms Signal"
//+------------------------------------------------------------------+
//| Include files |
//+------------------------------------------------------------------+
#include "..\Expert\ExpertSignalCustom.mqh"
//+------------------------------------------------------------------+
//| CSignalDTDB class. |
//| Purpose: Class for detecting double tops and bottoms. |
//| Inherits from CExpertSignalCustom. |
//+------------------------------------------------------------------+
class CSignalDTDB : public CExpertSignalCustom
{
protected:
// Pattern weights
int m_pattern_0;
// Variables for dynamic filtering
int m_lookback_period; // Lookback period to calculate average range
public:
CSignalDTDB();
~CSignalDTDB();
// Methods for adjusting pattern weights
void Pattern_0(int value) { m_pattern_0 = value; }
void LookBackPeriod(int value) { m_lookback_period = value; }
virtual void ApplyPatternWeight(int patternNumber, int weight);
// Method to validate parameters
virtual bool ValidationSettings();
// Methods to generate signals
virtual int LongCondition(void);
virtual int ShortCondition(void);
protected:
// Method to detect double tops
bool DetectDoubleTop(int startIdx);
// Method to detect double bottoms
bool DetectDoubleBottom(int startIdx);
// Helper method to detect peaks and troughs
bool IsExtreme(int idx, double prevExtreme1, double prevExtreme2, bool isPeak)
{
double currentValue = isPeak ? High(idx) : Low(idx);
return isPeak ? (currentValue > prevExtreme1 && currentValue > prevExtreme2)
: (currentValue < prevExtreme1 && currentValue < prevExtreme2);
}
// Common method to detect Double Top or Bottom
bool DetectDoubleExtreme(int startIdx, bool isTop, double threshold = 0.02, int symmetryThreshold = 2)
{
int extremesFound = 0;
double extreme1 = 0, extreme2 = 0;
int extreme1Idx = 0, extreme2Idx = 0;
double prevExtreme1 = isTop ? -DBL_MAX : DBL_MAX, prevExtreme2 = prevExtreme1;
int trendAnalysisStartIdx = MathMax(0, startIdx - m_lookback_period);
for(int i = startIdx; i >= trendAnalysisStartIdx; i--)
{
double value = isTop ? High(i) : Low(i);
if(extremesFound == 0 && IsExtreme(i, prevExtreme1, prevExtreme2, isTop))
{
extreme1 = value;
extreme1Idx = i;
extremesFound++;
}
else
if(extremesFound == 1 && IsExtreme(i, prevExtreme1, prevExtreme2, isTop))
{
extreme2 = value;
extreme2Idx = i;
extremesFound++;
if(fabs(extreme1 - extreme2) / fabs(extreme1) < threshold)
{
double extremeDistance = fabs(extreme2Idx - extreme1Idx);
double oppositeDuration = fabs(extreme1Idx - startIdx);
if(fabs(extremeDistance - oppositeDuration) < symmetryThreshold)
{
double neckline = (extreme1 + extreme2) / 2;
if((isTop && Low(startIdx) < neckline) || (!isTop && High(startIdx) > neckline))
{
return true;
}
}
}
// Reset extremesFound to 1 to check for another potential double top/bottom
extremesFound = 1;
extreme1 = extreme2;
extreme1Idx = extreme2Idx;
}
prevExtreme2 = prevExtreme1;
prevExtreme1 = value;
}
return false;
}
};
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CSignalDTDB::CSignalDTDB() :
m_pattern_0(30),
m_lookback_period(10)
{
m_id = "DTDB";
m_pattern_count = 1;
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CSignalDTDB::~CSignalDTDB(void)
{
}
//+------------------------------------------------------------------+
//| Validation settings |
//+------------------------------------------------------------------+
bool CSignalDTDB::ValidationSettings()
{
if(!CExpertSignalCustom::ValidationSettings())
return false;
if(m_lookback_period <= 0 || m_lookback_period > 200)
{
Print("Lookback Period must be greater than 0 and smaller than 200");
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| Detect Double Top |
//+------------------------------------------------------------------+
bool CSignalDTDB::DetectDoubleTop(int startIdx)
{
return DetectDoubleExtreme(startIdx, true);
}
//+------------------------------------------------------------------+
//| Detect Double Bottom |
//+------------------------------------------------------------------+
bool CSignalDTDB::DetectDoubleBottom(int startIdx)
{
return DetectDoubleExtreme(startIdx, false);
}
//+------------------------------------------------------------------+
//| Long Condition |
//+------------------------------------------------------------------+
int CSignalDTDB::LongCondition(void)
{
int result = 0;
int idx = StartIndex();
if(IS_PATTERN_USAGE(0) && DetectDoubleBottom(idx))
{
result = m_pattern_0;
m_active_pattern = "Pattern_0";
}
if(result != 0)
{
m_active_direction = "Buy";
}
return result;
}
//+------------------------------------------------------------------+
//| Short Condition |
//+------------------------------------------------------------------+
int CSignalDTDB::ShortCondition(void)
{
int result = 0;
int idx = StartIndex();
if(IS_PATTERN_USAGE(0) && DetectDoubleTop(idx))
{
result = m_pattern_0;
m_active_pattern = "Pattern_0";
}
if(result != 0)
{
m_active_direction = "Sell";
}
return result;
}
//+------------------------------------------------------------------+
//| Apply Pattern Weight |
//+------------------------------------------------------------------+
void CSignalDTDB::ApplyPatternWeight(int patternNumber, int weight)
{
switch(patternNumber)
{
default:
break;
case 0:
Pattern_0(weight);
break;
}
}
//+------------------------------------------------------------------+