2026-09-11 06:16:08 -04:00 | | | //+------------------------------------------------------------------+
|
| | | //| 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
|
| | |
|
2026-09-13 14:32:28 -04:00 | | | #include "..\..\Expert\WarriorSignal.mqh"
|
2026-09-11 06:16:08 -04:00 | | | #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
|