2026-09-11 06:16:08 -04:00 | | | //+------------------------------------------------------------------+
|
| | | //| 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 <Indicators\Custom.mqh>
|
| | |
|
| | | //--- 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; }
|
| | |
|
2026-09-13 14:32:40 -04:00 | | | //--- 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);
|
| | |
|
2026-09-11 06:16:08 -04:00 | | | //--- 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;
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
2026-09-13 14:32:40 -04:00 | | | 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;
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
2026-09-11 06:16:08 -04:00 | | | //| 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
|