Warrior_EA/Signals/Wyckoff/SignalWyckoffEvent.mqh

200 lines
9.4 KiB
MQL5

//+------------------------------------------------------------------+
//| SignalWyckoffEvent.mqh |
//| AnimateDread |
//| |
//| The ADWyckoffEventStream as a CLASSIC VOTE MODULE. |
//| |
//| It states an opinion; it does not own a trade. That distinction |
//| is the one IsClassicVoter() now tests (OwnsTrade() == false), and |
//| it is deliberate here: the event stream says "this is a spring in |
//| phase C of an accumulation", which is a reason to lean long, not |
//| a complete strategy with an entry, a stop and a target. The book |
//| setups own trades; this module votes alongside the fifteen other |
//| classic modules and is weighted like them. |
//| |
//| PATTERNS, ordered by the book's own conviction (Wyckoff's phases |
//| are a sequence, and later phases are later BECAUSE they are more |
//| confirmed): |
//| 0 Spring / UTAD, graded 1-3 by the indicator. The shake. The |
//| highest-conviction entry the method has, and the grade is |
//| the indicator's own confidence in it. |
//| 1 SOS / SOW - phase D confirmation, the range resolving. |
//| 2 LPS / LPSY - the pullback that holds after an SOS. |
//| 3 Test, placed well (upper half of an accumulation / below a |
//| climax), with the range character agreeing. |
//| 4 Change of character, range -> trend, with slope context. |
//| |
//| ALL READS GO THROUGH CWyckoffFeed AT SHIFT 1. Nothing here knows |
//| a buffer index; when the indicator is revised again this file |
//| does not change unless the MEANING changed. |
//+------------------------------------------------------------------+
#ifndef WARRIOR_SIGNALWYCKOFFEVENT_MQH
#define WARRIOR_SIGNALWYCKOFFEVENT_MQH
#include "..\..\Expert\WarriorSignal.mqh"
#include "WyckoffFeed.mqh"
class CSignalWyckoffEvent : public CWarriorSignal
{
protected:
//--- indicator inputs, exposed so a sweep can move them without editing this file
int m_lookback;
int m_zigzag;
int m_arLegs;
double m_boundaryTol;
int m_maxRangeBars;
//--- pattern weights (0-100)
int m_pattern_0; // spring / UTAD, scaled by the indicator's grade
int m_pattern_1; // SOS / SOW
int m_pattern_2; // LPS / LPSY
int m_pattern_3; // well-placed test
int m_pattern_4; // change of character, range -> trend
public:
CSignalWyckoffEvent(void);
~CSignalWyckoffEvent(void) {}
void Lookback(const int v) { m_lookback = v; }
void ZigZag(const int v) { m_zigzag = v; }
void ARLegs(const int v) { m_arLegs = v; }
void BoundaryTol(const double v) { m_boundaryTol = v; }
void MaxRangeBars(const int v) { m_maxRangeBars = 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; }
void Pattern_4(const int v) { m_pattern_4 = v; }
virtual void ApplyPatternWeight(int patternNumber, int weight);
virtual bool InitIndicators(CIndicators *indicators);
virtual int LongCondition(void);
virtual int ShortCondition(void);
//--- The feed is the shared g_wyckoffFeed - see WyckoffFeedEnsure(). Nothing owns it.
protected:
//--- One implementation, mirrored by direction. `dir` is +1 for the long case, -1 for the short,
//--- and every comparison below is written against it so the two sides cannot drift apart -
//--- which is exactly how m_entry came to be wiped by its own other side (project note 09-08).
int Condition(const int dir);
void Note(const int pattern, const int dir);
};
//+------------------------------------------------------------------+
CSignalWyckoffEvent::CSignalWyckoffEvent(void) :
m_lookback(50), m_zigzag(3), m_arLegs(3), m_boundaryTol(0.50), m_maxRangeBars(200),
m_pattern_0(80), m_pattern_1(60), m_pattern_2(45), m_pattern_3(30), m_pattern_4(20)
{
m_id = "WYKEV";
m_pattern_count = 5;
m_used_series = USE_SERIES_OPEN + USE_SERIES_HIGH + USE_SERIES_LOW + USE_SERIES_CLOSE;
}
//+------------------------------------------------------------------+
void CSignalWyckoffEvent::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;
case 3: Pattern_3(weight); break;
case 4: Pattern_4(weight); break;
default: break;
}
}
//+------------------------------------------------------------------+
bool CSignalWyckoffEvent::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, m_arLegs, m_boundaryTol, m_maxRangeBars))
{
//--- LOUD, AND FATAL. A feed that did not load reads zeros, and zeros are indistinguishable
//--- from "no Wyckoff event here" - the module would abstain forever and look healthy.
PrintFormat("%s: WYCKOFF FEED FAILED - %s. This module cannot vote and init is refused rather"
" than leaving a silent abstainer in the divisor.", __FUNCTION__, g_wyckoffFeed.Why());
return(false);
}
return(true);
}
//+------------------------------------------------------------------+
int CSignalWyckoffEvent::LongCondition(void)
{
return Condition(+1);
}
//+------------------------------------------------------------------+
int CSignalWyckoffEvent::ShortCondition(void)
{
return Condition(-1);
}
//+------------------------------------------------------------------+
//--- Names the pattern that produced this vote. The journal keys its tables on it, so it is part of
//--- the module's contract with the database rather than a log string: change one of these names and
//--- that pattern's history splits into a before and an after that nothing will ever join up.
void CSignalWyckoffEvent::Note(const int pattern, const int dir)
{
m_active_pattern = "Pattern_" + IntegerToString(pattern);
m_active_direction = (dir > 0) ? "Buy" : "Sell";
}
//+------------------------------------------------------------------+
int CSignalWyckoffEvent::Condition(const int dir)
{
if(!g_wyckoffFeed.Ok())
return 0;
const int code = g_wyckoffFeed.EventCode(); // 0..9, direction stripped
const int evDir = g_wyckoffFeed.EventDir(); // +1 bullish structure, -1 bearish, 0 none
const double grade = g_wyckoffFeed.SpringGrade(); // +-1..+-3 on the shake bar
const double charact = g_wyckoffFeed.Character(); // +1 accumulation, -1 distribution
const int phase = g_wyckoffFeed.PhaseLetter(); // 1..5 = A..E
//--- 0: THE SHAKE. Graded by the indicator, and the grade is carried through to the vote rather
//--- than flattened: grade 3 is "direct entry", grade 1 is "wait for the test", and a module that
//--- returned the same weight for both would be discarding the indicator's own confidence.
if(code == WYK_EV_SPRING && evDir == dir)
{
Note(0, dir);
const int g = (int)MathAbs(grade);
if(g >= 1 && g <= 3)
return (int)MathRound(m_pattern_0 * (g / 3.0));
return m_pattern_0 / 2; // a spring the grader could not rank
}
//--- 1: PHASE D CONFIRMATION. The range is resolving in `dir`.
if(code == WYK_EV_SOS && evDir == dir)
{
Note(1, dir);
return m_pattern_1;
}
//--- 2: THE PULLBACK THAT HELD.
if(code == WYK_EV_LPS && evDir == dir)
{
Note(2, dir);
return m_pattern_2;
}
//--- 3: A WELL-PLACED TEST. The test alone is weak; the test plus a range whose character agrees
//--- with the side being voted is the book's actual condition, so both are required.
if(code == WYK_EV_TEST && evDir == dir)
{
const double place = g_wyckoffFeed.TestPlace();
const bool characterAgrees = (dir > 0) ? (charact > 0.0) : (charact < 0.0);
const bool placedWell = (dir > 0) ? (place > 0.0) : (place < 0.0);
if(characterAgrees && placedWell)
{
Note(3, dir);
return m_pattern_3;
}
}
//--- 4: CHANGE OF CHARACTER, range -> trend, only while the phase agrees with the side. Phase is
//--- signed by direction in the buffer, so PhaseDir() is what makes this directional at all.
if(g_wyckoffFeed.ChochRangeToTrend() != 0.0 && g_wyckoffFeed.PhaseDir() == dir && phase >= WYK_PHASE_D)
{
Note(4, dir);
return m_pattern_4;
}
return 0;
}
#endif // WARRIOR_SIGNALWYCKOFFEVENT_MQH