- 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.
175 lines
8.2 KiB
MQL5
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
|