- Replaced standard library signal modules with custom implementations to allow for named patterns and improved voting. - Added new input parameters for module weights, allowing for optimization of individual signal contributions. - Enhanced the management of trades with new options for breakeven and management cut. - Introduced a mechanism for dynamic ranking of signal weights based on historical performance. - Improved initialization logic to ensure proper registration of filters and handling of trading conditions. - Added detailed logging for trading permissions and account status during initialization.
342 lines
19 KiB
MQL5
342 lines
19 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| 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; }
|
|
|
|
//--- 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
|