derivado de mnbvc188199/Warrior_EA
- Introduced `FeatureScale.mqh` with `FeatSquash` function for stateless feature scaling. - Added `RegimeMath.mqh` class for regime arithmetic, including efficiency and variance calculations. - Documented the Mind trading logic in `MIND.md`, detailing the trading process and modes. - Created `VOLNORM_PLAN.md` and `VOLNORM_RESULTS.md` for tick-volume normalization testing. - Implemented `read_book.py` for analyzing trade book data and correlations. - Developed `volnorm.py` for testing tick-volume normalization with new and old methods.
345 linhas
20 KiB
MQL5
345 linhas
20 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 29 // ADWyckoffEventStream v5.7 (26 + PF count and two PF targets)
| |
#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
| |
#define WYK_B_PF_COUNT 26 // v5.7: not read by any module yet
| |
#define WYK_B_TARGET_LONG_PF 27 // v5.7: not read by any module yet
| |
#define WYK_B_TARGET_SHORT_PF 28 // v5.7: not read by any module yet
| |
| |
//--- 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
|