//+------------------------------------------------------------------+ //| 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