Warrior_EA/Signals/Wyckoff/SignalWyckoffStructure.mqh

157 lines
7.2 KiB
MQL5

//+------------------------------------------------------------------+
//| SignalWyckoffStructure.mqh |
//| AnimateDread |
//| |
//| ADWyckoffFailedStructure + ADWyckoffSignificantBarInversion as |
//| one classic vote module. |
//| |
//| Both indicators publish a SIGNED QUALITY on buffer 0, and this |
//| module reads that rather than re-deriving a direction from the |
//| flag buffers. That is a deliberate choice, because the flag names |
//| are a trap: |
//| |
//| BullishStructuralFailure -> signedValue += quality -> BULLISH |
//| "higher low, no weakness" - it is the DOWN leg that failed. |
//| Read as "a bullish structure failed" it inverts the signal. |
//| FailedDistribution -> signedValue += quality -> BULLISH |
//| "breakdown failed" |
//| FailedAccumulation -> signedValue -= quality -> BEARISH |
//| "breakout failed" |
//| |
//| (ADWyckoffFailedStructure.mq5:332-338.) The indicator also makes |
//| its two structural flags mutually exclusive precisely so the |
//| signed value cannot net to zero - a defect it fixed in v2.0. Using |
//| buffer 0 inherits that arbitration instead of re-implementing it |
//| here and getting a different answer. |
//| |
//| The magnitudes are the indicators' own confidence and are carried |
//| through: FailedStructure clamps to +-2.5, SignificantBarInversion |
//| to +-2.0. Both are scaled to the pattern weight rather than |
//| flattened to a flag. |
//+------------------------------------------------------------------+
#ifndef WARRIOR_SIGNALWYCKOFFSTRUCTURE_MQH
#define WARRIOR_SIGNALWYCKOFFSTRUCTURE_MQH
#include "..\..\Expert\WarriorSignal.mqh"
#include "WyckoffFeed.mqh"
//--- The clamps the two indicators apply to their signed quality. Named because they are the
//--- denominators that turn a quality into a fraction of a pattern weight; a wrong one here silently
//--- rescales the vote rather than breaking it.
#define WYK_FAIL_QUALITY_MAX 2.5
#define WYK_SBI_QUALITY_MAX 2.0
class CSignalWyckoffStructure : public CWarriorSignal
{
protected:
int m_lookback;
int m_zigzag;
//--- pattern weights (0-100)
int m_pattern_0; // structural failure / failed breakout, scaled by its quality
int m_pattern_1; // significant bar with a control flip - the strongest SBI case
int m_pattern_2; // significant bar alone, scaled by its quality
public:
CSignalWyckoffStructure(void);
~CSignalWyckoffStructure(void) {}
void Lookback(const int v) { m_lookback = v; }
void ZigZag(const int v) { m_zigzag = 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; }
virtual void ApplyPatternWeight(int patternNumber, int weight);
virtual bool InitIndicators(CIndicators *indicators);
virtual int LongCondition(void) { return Condition(+1); }
virtual int ShortCondition(void) { return Condition(-1); }
protected:
int Condition(const int dir);
//--- Names the pattern for the journal; see the note in SignalWyckoffEvent.mqh.
void Note(const int pattern, const int dir)
{
m_active_pattern = "Pattern_" + IntegerToString(pattern);
m_active_direction = (dir > 0) ? "Buy" : "Sell";
}
//--- quality -> 0..weight, saturating at the indicator's own clamp
int Scaled(const double quality, const double clamp, const int weight)
{
const double f = MathMin(MathAbs(quality) / clamp, 1.0);
return (int)MathRound(weight * f);
}
};
//+------------------------------------------------------------------+
CSignalWyckoffStructure::CSignalWyckoffStructure(void) :
m_lookback(50), m_zigzag(3),
m_pattern_0(55), m_pattern_1(40), m_pattern_2(25)
{
m_id = "WYKST";
m_pattern_count = 3;
m_used_series = USE_SERIES_OPEN + USE_SERIES_HIGH + USE_SERIES_LOW + USE_SERIES_CLOSE
+ USE_SERIES_TICK_VOLUME;
}
//+------------------------------------------------------------------+
void CSignalWyckoffStructure::ApplyPatternWeight(int patternNumber, int weight)
{
switch(patternNumber)
{
case 0: Pattern_0(weight); break;
case 1: Pattern_1(weight); break;
case 2: Pattern_2(weight); break;
default: break;
}
}
//+------------------------------------------------------------------+
bool CSignalWyckoffStructure::InitIndicators(CIndicators *indicators)
{
if(indicators == NULL)
return(false);
if(!CWarriorSignal::InitIndicators(indicators))
return(false);
if(!WyckoffFeedEnsure(indicators, m_symbol.Name(), m_period, m_lookback, m_zigzag))
{
PrintFormat("%s: WYCKOFF FEED FAILED - %s. Refusing init rather than voting zeros, which are"
" indistinguishable from 'no structure here'.", __FUNCTION__, g_wyckoffFeed.Why());
return(false);
}
return(true);
}
//+------------------------------------------------------------------+
int CSignalWyckoffStructure::Condition(const int dir)
{
if(!g_wyckoffFeed.Ok())
return 0;
//--- 0: FAILED STRUCTURE. One signed number covering all four of the indicator's cases, already
//--- arbitrated by it. Sign is the direction the failure hands control to.
const double fail = g_wyckoffFeed.FailValue();
if(fail != 0.0 && ((dir > 0) ? (fail > 0.0) : (fail < 0.0)))
{
Note(0, dir);
return Scaled(fail, WYK_FAIL_QUALITY_MAX, m_pattern_0);
}
//--- 1 and 2: THE SIGNIFICANT BAR. A control FLIP is the book's stronger statement - the bar did
//--- not merely show intent, it took control - so it is tested first and weighted above the bar
//--- on its own. Quality is signed the same way (+ bull / - bear).
const double q = g_wyckoffFeed.BarQuality();
if(q != 0.0 && ((dir > 0) ? (q > 0.0) : (q < 0.0)))
{
const bool flip = (dir > 0) ? (g_wyckoffFeed.BullFlip() != 0.0)
: (g_wyckoffFeed.BearFlip() != 0.0);
if(flip)
{
Note(1, dir);
return Scaled(q, WYK_SBI_QUALITY_MAX, m_pattern_1);
}
const bool sig = (dir > 0) ? (g_wyckoffFeed.BullBar() != 0.0)
: (g_wyckoffFeed.BearBar() != 0.0);
if(sig)
{
Note(2, dir);
return Scaled(q, WYK_SBI_QUALITY_MAX, m_pattern_2);
}
}
return 0;
}
#endif // WARRIOR_SIGNALWYCKOFFSTRUCTURE_MQH