//+------------------------------------------------------------------+ //| WyckoffFeed.mqh | //| AnimateDread | //| | //| THE ONE PLACE THE ADWyckoff INDICATOR API LIVES. | //| | //| Three custom indicators, three separate handles, one accessor. | //| Every iCustom parameter list, every buffer index and every | //| sentinel is stated here and nowhere else, because the indicators | //| are under active revision and their parameter counts have already | //| changed once (v4.x -> v5.5) while the callers went on passing the | //| old list. A wrong parameter ORDER is silent: MQL5 fills the | //| missing ones with defaults and the indicator computes something | //| plausible from the wrong numbers. | //| | //| VERSIONED BY BUFFER COUNT, checked at Create(). If an indicator | //| comes back with a different number of buffers than this file was | //| written against, the feed refuses to load and says so - it does | //| NOT read whatever is there. A shifted buffer map reads a real | //| number from the wrong column, which is the failure that cannot be | //| seen in a result. Refusing is recoverable; guessing is not. | //| | //| CAUSALITY. The indicators zero the forming bar and only advance | //| their state machine on closed bars (ADWyckoffEventStream.mq5:22, | //| and the write loop stamps bar si with the event detected AT si - | //| nothing is back-filled to the pivot that started it). So shift 1 | //| is the newest safely readable bar and every accessor here | //| defaults to it. Shift 0 is always zero by construction. | //+------------------------------------------------------------------+ #ifndef WARRIOR_WYCKOFFFEED_MQH #define WARRIOR_WYCKOFFFEED_MQH #include //--- Indicator file names, relative to MQL5\Indicators #define WYK_IND_EVENTS "ADWyckoffEventStream" #define WYK_IND_FAILURE "ADWyckoffFailedStructure" #define WYK_IND_INVERSION "ADWyckoffSignificantBarInversion" //--- The buffer counts this file was written against. See the version note above. #define WYK_EVENTS_BUFFERS 26 // ADWyckoffEventStream v5.5 #define WYK_FAILURE_BUFFERS 5 // ADWyckoffFailedStructure v2.00 #define WYK_INVERSION_BUFFERS 5 // ADWyckoffSignificantBarInversion v2.00 //--- EventStream buffer map (SetIndexBuffer, ADWyckoffEventStream.mq5:1734-1746) #define WYK_B_EVENT 0 // sparse, -9..+9, 0 = no event #define WYK_B_PHASE 1 // persistent, phase * sign(direction), +-1..+-5 = A..E #define WYK_B_ZONE_TOP 2 // continuous while a range is open, 0 once it closes #define WYK_B_ZONE_BOTTOM 3 #define WYK_B_EVENT_PRICE 4 // close on an event bar, else 0 #define WYK_B_STRUCT_PHASE 5 // sparse, event-derived phase #define WYK_B_CHOCH_TR 6 // trend -> range change of character #define WYK_B_CHOCH_RT 7 // range -> trend #define WYK_B_SLOPE_A_BULL 8 // context flags, 0/1, mutually exclusive #define WYK_B_SLOPE_A_BEAR 9 #define WYK_B_SLOPE_D_BULL 10 #define WYK_B_SLOPE_D_BEAR 11 #define WYK_B_REACC 12 // 0/1, only once resolved with the trend #define WYK_B_REDIS 13 #define WYK_B_SPRING_GRADE 14 // +-1..+-3 on the shake bar, else 0 #define WYK_B_CHARACTER 15 // +1 accumulation / -1 distribution / 0 not anchored #define WYK_B_TEST_PLACE 16 // +1 upper half / -1 below climax / 0 neutral #define WYK_B_AR_STRENGTH 17 // 1 when the AR reached 2x recent legs #define WYK_B_CREEK 18 // HELD levels - these do NOT reset to 0 when the range closes #define WYK_B_ICE 19 #define WYK_B_RANGE_MID 20 #define WYK_B_STOP_LONG 21 // Ice - tolerance #define WYK_B_STOP_SHORT 22 // Creek + tolerance #define WYK_B_TARGET_LONG 23 // Creek + range height #define WYK_B_TARGET_SHORT 24 // Ice - range height #define WYK_B_EVENT_LEVEL 25 // held close of the last event bar //--- FailedStructure buffer map #define WYK_F_VALUE 0 // signed quality, clamped +-2.5 #define WYK_F_BULL_FAILURE 1 // 0/1 #define WYK_F_BEAR_FAILURE 2 #define WYK_F_FAILED_ACCUM 3 #define WYK_F_FAILED_DISTRIB 4 //--- SignificantBarInversion buffer map #define WYK_I_QUALITY 0 // signed, +bull / -bear, +-0..2, 0 when not significant #define WYK_I_BULL_SIG 1 // 0/1 #define WYK_I_BEAR_SIG 2 #define WYK_I_BULL_FLIP 3 #define WYK_I_BEAR_FLIP 4 //--- EVENT VOCABULARY. Defined in the indicator only as switch cases in EvName() //--- (ADWyckoffEventStream.mq5:435-445) - there is no enum to include, so it is mirrored here. //--- Sign is the structure's direction: + bullish, - bearish. #define WYK_EV_NONE 0 #define WYK_EV_PS 1 // preliminary support / PSY preliminary supply #define WYK_EV_SC 2 // selling climax / BC buying climax #define WYK_EV_AR 3 // automatic rally / automatic reaction #define WYK_EV_ST 4 // secondary test #define WYK_EV_SPRING 5 // spring / UTAD upthrust after distribution #define WYK_EV_LPS 6 // last point of support / LPSY last point of supply #define WYK_EV_SOS 7 // sign of strength / SOW sign of weakness #define WYK_EV_TEST 8 // test #define WYK_EV_BU 9 // back-up / BD back-down //--- Phases carried on WYK_B_PHASE, signed by direction: 1=A 2=B 3=C 4=D 5=E #define WYK_PHASE_A 1 #define WYK_PHASE_B 2 #define WYK_PHASE_C 3 #define WYK_PHASE_D 4 #define WYK_PHASE_E 5 //+------------------------------------------------------------------+ //| CWyckoffFeed | //+------------------------------------------------------------------+ class CWyckoffFeed { protected: CiCustom m_events; CiCustom m_failure; CiCustom m_inversion; bool m_ok; string m_why; string m_symbolName; ENUM_TIMEFRAMES m_periodValue; string m_createdFor; // "symbol/period" this feed was built for, "" if never built bool Build(CiCustom &ind, CIndicators *owner, const string name, const int wantBuffers, MqlParam ¶ms[]); public: CWyckoffFeed(void) : m_ok(false), m_why("not created"), m_createdFor("") {} ~CWyckoffFeed(void) {} //--- Creates all three handles against `symbol`/`period` and registers them with `owner`. //--- Returns false and leaves Why() set if any one of them fails or reports the wrong width. bool Create(CIndicators *owner, const string symbol, const ENUM_TIMEFRAMES period, const int lookback = 50, const int zigzag = 3, const int arLegs = 3, const double boundaryTol = 0.50, const int maxRangeBars = 200); bool Ok(void) const { return m_ok; } string Why(void) const { return m_why; } string CreatedFor(void) const { return m_createdFor; } //--- Grow all three handles past the stdlib's 1024-bar buffer, for a caller that walks deep //--- history (the neural module's training sweep). Same contract as CWarriorSignal:: //--- DeepenPrices(): grows only, and `size` must be bars that already exist or CSeries prints //--- a failure line. A feed left at its default still ANSWERS past shift 1023 - with 0.0 - and //--- zero is a legitimate "no structure here" for every one of these reads, so the shortfall //--- is indistinguishable from data unless the buffer is grown to match the caller's reach. bool Deepen(const int size); //--- Raw reads. Shift 1 = the newest CLOSED bar and the newest that carries anything. double Event(const int shift = 1) { return m_events.GetData(WYK_B_EVENT, shift); } double Phase(const int shift = 1) { return m_events.GetData(WYK_B_PHASE, shift); } double SpringGrade(const int shift = 1){ return m_events.GetData(WYK_B_SPRING_GRADE, shift);} double Character(const int shift = 1) { return m_events.GetData(WYK_B_CHARACTER, shift); } double TestPlace(const int shift = 1) { return m_events.GetData(WYK_B_TEST_PLACE, shift); } double ARStrength(const int shift = 1) { return m_events.GetData(WYK_B_AR_STRENGTH, shift);} double Reaccumulation(const int shift = 1) { return m_events.GetData(WYK_B_REACC, shift); } double Redistribution(const int shift = 1) { return m_events.GetData(WYK_B_REDIS, shift); } double ChochTrendToRange(const int shift = 1) { return m_events.GetData(WYK_B_CHOCH_TR, shift); } double ChochRangeToTrend(const int shift = 1) { return m_events.GetData(WYK_B_CHOCH_RT, shift); } //--- The four slope-context flags. Mutually exclusive by construction in the indicator //--- (ADWyckoffEventStream.mq5:1860-1863 picks exactly one branch), so at most one is ever 1. double SlopeAccumBull(const int shift = 1) { return m_events.GetData(WYK_B_SLOPE_A_BULL, shift); } double SlopeAccumBear(const int shift = 1) { return m_events.GetData(WYK_B_SLOPE_A_BEAR, shift); } double SlopeDistribBull(const int shift = 1){ return m_events.GetData(WYK_B_SLOPE_D_BULL, shift); } double SlopeDistribBear(const int shift = 1){ return m_events.GetData(WYK_B_SLOPE_D_BEAR, shift); } double ZoneTop(const int shift = 1) { return m_events.GetData(WYK_B_ZONE_TOP, shift); } double ZoneBottom(const int shift = 1) { return m_events.GetData(WYK_B_ZONE_BOTTOM, shift);} //--- HELD price levels. These persist after the range closes, so a non-zero value is NOT proof //--- that a range is currently open - test RangeOpen() when that matters. double Creek(const int shift = 1) { return m_events.GetData(WYK_B_CREEK, shift); } double Ice(const int shift = 1) { return m_events.GetData(WYK_B_ICE, shift); } double RangeMid(const int shift = 1) { return m_events.GetData(WYK_B_RANGE_MID, shift); } double StopLong(const int shift = 1) { return m_events.GetData(WYK_B_STOP_LONG, shift); } double StopShort(const int shift = 1) { return m_events.GetData(WYK_B_STOP_SHORT, shift); } double TargetLong(const int shift = 1) { return m_events.GetData(WYK_B_TARGET_LONG, shift);} double TargetShort(const int shift = 1){ return m_events.GetData(WYK_B_TARGET_SHORT, shift);} double FailValue(const int shift = 1) { return m_failure.GetData(WYK_F_VALUE, shift); } double BullFailure(const int shift = 1) { return m_failure.GetData(WYK_F_BULL_FAILURE, shift); } double BearFailure(const int shift = 1) { return m_failure.GetData(WYK_F_BEAR_FAILURE, shift); } double FailedAccum(const int shift = 1) { return m_failure.GetData(WYK_F_FAILED_ACCUM, shift); } double FailedDistrib(const int shift = 1){ return m_failure.GetData(WYK_F_FAILED_DISTRIB, shift); } double BarQuality(const int shift = 1) { return m_inversion.GetData(WYK_I_QUALITY, shift); } double BullBar(const int shift = 1) { return m_inversion.GetData(WYK_I_BULL_SIG, shift); } double BearBar(const int shift = 1) { return m_inversion.GetData(WYK_I_BEAR_SIG, shift); } double BullFlip(const int shift = 1) { return m_inversion.GetData(WYK_I_BULL_FLIP, shift);} double BearFlip(const int shift = 1) { return m_inversion.GetData(WYK_I_BEAR_FLIP, shift);} //--- Derived, so that no caller has to re-derive them differently. //--- The event CODE without its direction, and the direction on its own. int EventCode(const int shift = 1) { return (int)MathAbs(Event(shift)); } int EventDir(const int shift = 1) { const double e = Event(shift); return (e > 0.0) ? 1 : ((e < 0.0) ? -1 : 0); } //--- A range is OPEN only while both boundaries are being published; they zero when it closes. bool RangeOpen(const int shift = 1) { const double t = ZoneTop(shift), b = ZoneBottom(shift); return (t > 0.0 && b > 0.0 && t > b); } //--- Phase letter 1..5 and its direction, split out of the signed buffer. int PhaseLetter(const int shift = 1) { return (int)MathAbs(Phase(shift)); } int PhaseDir(const int shift = 1) { const double p = Phase(shift); return (p > 0.0) ? 1 : ((p < 0.0) ? -1 : 0); } }; //+------------------------------------------------------------------+ bool CWyckoffFeed::Build(CiCustom &ind, CIndicators *owner, const string name, const int wantBuffers, MqlParam ¶ms[]) { if(owner == NULL) { m_why = "no indicator collection"; return false; } if(!ind.NumBuffers(wantBuffers)) { m_why = name + ": NumBuffers(" + IntegerToString(wantBuffers) + ") refused"; return false; } if(!owner.Add(GetPointer(ind))) { m_why = name + ": could not be added to the indicator collection"; return false; } if(!ind.Create(m_symbolName, m_periodValue, IND_CUSTOM, ArraySize(params), params)) { m_why = name + ": Create() failed - is the indicator compiled and present in MQL5\\Indicators?"; return false; } return true; } //+------------------------------------------------------------------+ //| Create all three handles. | //| | //| The parameter lists are the indicators' OWN input order, checked | //| against source on 2026-09-10: | //| EventStream (9): Lookback, ZigZag, ARLegs, BoundaryTol, | //| MaxRangeBars, ShowLabels, ShowZones, | //| ShowPhases, ShowPanel | //| Failure (3): LookbackPeriod, ZigZagStrength, ShowMarkers | //| Inversion (2): Lookback, ShowMarkers | //| | //| Every display flag is passed FALSE. The EA's handle exists to be | //| read, not drawn; an indicator that also renders labels, zones, | //| phase bands and a panel on every tester bar costs real time and | //| draws objects nobody looks at. The operator's own chart copy is | //| a separate handle with its own inputs and is unaffected. | //+------------------------------------------------------------------+ bool CWyckoffFeed::Create(CIndicators *owner, const string symbol, const ENUM_TIMEFRAMES period, const int lookback, const int zigzag, const int arLegs, const double boundaryTol, const int maxRangeBars) { m_ok = false; m_symbolName = symbol; m_periodValue = period; MqlParam ev[10]; ev[0].type = TYPE_STRING; ev[0].string_value = WYK_IND_EVENTS; ev[1].type = TYPE_INT; ev[1].integer_value = lookback; ev[2].type = TYPE_INT; ev[2].integer_value = zigzag; ev[3].type = TYPE_INT; ev[3].integer_value = arLegs; ev[4].type = TYPE_DOUBLE; ev[4].double_value = boundaryTol; ev[5].type = TYPE_INT; ev[5].integer_value = maxRangeBars; ev[6].type = TYPE_BOOL; ev[6].integer_value = 0; // ShowLabels ev[7].type = TYPE_BOOL; ev[7].integer_value = 0; // ShowZones ev[8].type = TYPE_BOOL; ev[8].integer_value = 0; // ShowPhases ev[9].type = TYPE_BOOL; ev[9].integer_value = 0; // ShowPanel if(!Build(m_events, owner, WYK_IND_EVENTS, WYK_EVENTS_BUFFERS, ev)) return false; MqlParam fs[4]; fs[0].type = TYPE_STRING; fs[0].string_value = WYK_IND_FAILURE; fs[1].type = TYPE_INT; fs[1].integer_value = lookback; fs[2].type = TYPE_INT; fs[2].integer_value = zigzag; fs[3].type = TYPE_BOOL; fs[3].integer_value = 0; // ShowMarkers if(!Build(m_failure, owner, WYK_IND_FAILURE, WYK_FAILURE_BUFFERS, fs)) return false; MqlParam sb[3]; sb[0].type = TYPE_STRING; sb[0].string_value = WYK_IND_INVERSION; sb[1].type = TYPE_INT; sb[1].integer_value = lookback; sb[2].type = TYPE_BOOL; sb[2].integer_value = 0; // ShowMarkers if(!Build(m_inversion, owner, WYK_IND_INVERSION, WYK_INVERSION_BUFFERS, sb)) return false; m_ok = true; m_why = "ok"; m_createdFor = symbol + "/" + IntegerToString((int)period); PrintFormat("CWyckoffFeed: %s %s - EventStream(%d buf) FailedStructure(%d buf)" " SignificantBarInversion(%d buf) | lookback %d zigzag %d arLegs %d tol %.2f" " maxRange %d | reads shift 1 (newest closed bar)", symbol, EnumToString(period), WYK_EVENTS_BUFFERS, WYK_FAILURE_BUFFERS, WYK_INVERSION_BUFFERS, lookback, zigzag, arLegs, boundaryTol, maxRangeBars); return true; } //+------------------------------------------------------------------+ bool CWyckoffFeed::Deepen(const int size) { if(!m_ok || size <= 0) return false; bool ok = true; if(size > m_events.BufferSize()) ok = m_events.BufferResize(size) && ok; if(size > m_failure.BufferSize()) ok = m_failure.BufferResize(size) && ok; if(size > m_inversion.BufferSize()) ok = m_inversion.BufferResize(size) && ok; return ok; } //+------------------------------------------------------------------+ //| ONE FEED PER CHART, SHARED BY EVERY WYCKOFF MODULE. | //| | //| Three modules read these indicators. Each building its own feed | //| would issue three identical iCustom requests - MT5 de-duplicates | //| handles with identical parameters, so it would work, but it also | //| prints the load line three times and makes "how many handles am I | //| holding" unanswerable from the log. More importantly it invites | //| the modules to drift apart: two feeds created with different | //| lookbacks are two different opinions wearing one name. | //| | //| Global, like g_metaLabel, and guarded by the symbol/period it was | //| built for so a chart change rebuilds it rather than silently | //| serving another instrument's numbers. | //+------------------------------------------------------------------+ CWyckoffFeed g_wyckoffFeed; bool WyckoffFeedEnsure(CIndicators *owner, const string symbol, const ENUM_TIMEFRAMES period, const int lookback = 50, const int zigzag = 3, const int arLegs = 3, const double boundaryTol = 0.50, const int maxRangeBars = 200) { const string want = symbol + "/" + IntegerToString((int)period); if(g_wyckoffFeed.Ok() && g_wyckoffFeed.CreatedFor() == want) return true; return g_wyckoffFeed.Create(owner, symbol, period, lookback, zigzag, arLegs, boundaryTol, maxRangeBars); } #endif // WARRIOR_WYCKOFFFEED_MQH