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
|