Warrior_EA/Signals/SignalRegime.mqh
AnimateDread 47a5ef338b Refactor Warrior EA: Integrate custom signal modules, enhance voting mechanism, and improve management features
- Replaced standard library signal modules with custom implementations to allow for named patterns and improved voting.
- Added new input parameters for module weights, allowing for optimization of individual signal contributions.
- Enhanced the management of trades with new options for breakeven and management cut.
- Introduced a mechanism for dynamic ranking of signal weights based on historical performance.
- Improved initialization logic to ensure proper registration of filters and handling of trading conditions.
- Added detailed logging for trading permissions and account status during initialization.
2026-09-13 14:32:40 -04:00

175 lines
8.2 KiB
MQL5

//+------------------------------------------------------------------+
//| SignalRegime.mqh |
//| AnimateDread |
//| |
//| WHICH GAME IS BEING PLAYED: trending, consolidating, or mean- |
//| reverting. A classic module in the ordinary shape - it votes |
//| 0..100, names its pattern, and the journal ranks it like any |
//| other. |
//| |
//| WHY THIS AND NOT MORE WYCKOFF. Wyckoff answers "where are we in |
//| a campaign" - events, phases, springs. That is structure. This |
//| answers a blunter and more useful question first: does price |
//| here travel, or does it come back? Those demand opposite trades |
//| from the same indicator reading, which is why an ensemble of |
//| oscillators and trend modules voting together can be right about |
//| everything and still lose: in a trend the oscillators are wrong, |
//| in a range the trend modules are. |
//| |
//| TWO MEASURES, BOTH FROM CLOSES, BOTH SCALE-FREE. |
//| |
//| EFFICIENCY RATIO (Kaufman): net distance divided by the path |
//| length walked to get there. |
//| ER = |C[t] - C[t-n]| / SUM |C[i] - C[i-1]| |
//| 1.0 is a straight line, 0.0 is thrashing that ends where it |
//| began. It needs no volatility estimate and no threshold that |
//| means different things on different symbols - a ratio of two |
//| distances in the same units cancels the instrument entirely. |
//| |
//| VARIANCE RATIO: the variance of q-bar returns against q times the |
//| variance of 1-bar returns. |
//| VR = Var(q-bar) / (q * Var(1-bar)) |
//| A random walk gives 1.0 because variance scales with time. Above |
//| 1 the moves compound - trending. Below 1 they cancel - mean |
//| reverting. This is the sharper of the two: ER says "is there a |
//| trend", VR says "does this market CONTINUE or REVERSE", which is |
//| the question an entry actually rests on. |
//| |
//| ⚠ IT DOES NOT VOTE IN CONSOLIDATION, deliberately. Neither the |
//| trend patterns nor the reversion patterns have an edge when the |
//| market is doing neither, and a module that always finds something |
//| to say is a module whose vote means nothing. Returning 0/0 is a |
//| real answer here, and the standard library counts it as one. |
//+------------------------------------------------------------------+
#ifndef WARRIOR_SIGNALREGIME_MQH
#define WARRIOR_SIGNALREGIME_MQH
#include "..\Expert\WarriorSignal.mqh"
class CSignalRegime : public CWarriorSignal
{
protected:
int m_erPeriod; // bars for the efficiency ratio
int m_vrPeriod; // bars for the variance ratio
int m_vrQ; // the q in VR(q)
int m_maPeriod; // the reference the trend is measured against
double m_erTrend; // ER at or above this is a trend
double m_erChop; // ER at or below this is consolidation
double m_vrRevert; // VR at or below this is mean reverting
int m_pattern_0; // trend, long
int m_pattern_1; // trend, short
int m_pattern_2; // mean reversion, long
int m_pattern_3; // mean reversion, short
double Sma(const int shift, const int period) const;
public:
CSignalRegime(void);
~CSignalRegime(void) {}
void ErPeriod(const int v) { m_erPeriod = v; }
void VrPeriod(const int v) { m_vrPeriod = v; }
void MaPeriod(const int v) { m_maPeriod = v; }
void Pattern_0(const int v) { m_pattern_0 = v; }
void Pattern_1(const int v) { m_pattern_1 = v; }
void Pattern_2(const int v) { m_pattern_2 = v; }
void Pattern_3(const int v) { m_pattern_3 = v; }
virtual void ApplyPatternWeight(int pattern, int weight)
{
if(pattern == 0) m_pattern_0 = weight;
if(pattern == 1) m_pattern_1 = weight;
if(pattern == 2) m_pattern_2 = weight;
if(pattern == 3) m_pattern_3 = weight;
}
//--- Published so the neural module can read the regime as context rather than recompute it.
double ER(const int shift = 1) const { return EfficiencyRatio(shift, m_erPeriod); }
double VR(const int shift = 1) const { return VarianceRatio(shift, m_vrPeriod, m_vrQ); }
virtual bool ValidationSettings(void) override;
virtual int LongCondition(void) override;
virtual int ShortCondition(void) override;
};
//+------------------------------------------------------------------+
CSignalRegime::CSignalRegime(void) : m_erPeriod(20), m_vrPeriod(60), m_vrQ(5), m_maPeriod(50),
m_erTrend(0.35), m_erChop(0.15), m_vrRevert(0.85),
m_pattern_0(60), m_pattern_1(60),
m_pattern_2(50), m_pattern_3(50)
{
m_id = "REGIME";
m_pattern_count = 4;
m_used_series = USE_SERIES_CLOSE;
}
//+------------------------------------------------------------------+
bool CSignalRegime::ValidationSettings(void)
{
if(!CWarriorSignal::ValidationSettings())
return false;
if(m_erPeriod < 5 || m_vrPeriod < 20 || m_vrQ < 2 || m_vrPeriod < m_vrQ * 4)
{
//--- VR(q) needs several non-overlapping q-blocks to estimate a variance at all; with fewer
//--- than four the ratio is noise with a decimal point.
Print("CSignalRegime: periods are too short for a stable variance ratio");
return false;
}
return true;
}
//+------------------------------------------------------------------+
double CSignalRegime::Sma(const int shift, const int period) const
{
double s = 0.0;
for(int i = 0; i < period; i++)
s += Close(shift + i);
return s / period;
}
//+------------------------------------------------------------------+
int CSignalRegime::LongCondition(void)
{
const int idx = StartIndex();
const double er = EfficiencyRatio(idx, m_erPeriod);
const double vr = VarianceRatio(idx, m_vrPeriod, m_vrQ);
const double c = Close(idx);
const double ma = Sma(idx, m_maPeriod);
//--- TRENDING AND UP. ER says the path is efficient, the reference says which way.
if(er >= m_erTrend && c > ma)
{
m_active_pattern = "Pattern_0";
m_active_direction = "Buy";
return m_pattern_0;
}
//--- MEAN REVERTING AND BELOW THE REFERENCE. VR below 1 says moves cancel; being under the mean
//--- is then a reason to buy rather than a reason to worry. This is the exact setup where the
//--- trend modules are wrong, which is the whole point of separating the two regimes.
if(vr <= m_vrRevert && er <= m_erChop && c < ma)
{
m_active_pattern = "Pattern_2";
m_active_direction = "Buy";
return m_pattern_2;
}
return 0;
}
//+------------------------------------------------------------------+
int CSignalRegime::ShortCondition(void)
{
const int idx = StartIndex();
const double er = EfficiencyRatio(idx, m_erPeriod);
const double vr = VarianceRatio(idx, m_vrPeriod, m_vrQ);
const double c = Close(idx);
const double ma = Sma(idx, m_maPeriod);
if(er >= m_erTrend && c < ma)
{
m_active_pattern = "Pattern_1";
m_active_direction = "Sell";
return m_pattern_1;
}
if(vr <= m_vrRevert && er <= m_erChop && c > ma)
{
m_active_pattern = "Pattern_3";
m_active_direction = "Sell";
return m_pattern_3;
}
return 0;
}
#endif // WARRIOR_SIGNALREGIME_MQH