forked from 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.
150 lines
7.1 KiB
MQL5
150 lines
7.1 KiB
MQL5
//+------------------------------------------------------------------+
| |
//| ModalityWyckoff.mqh |
| |
//| AnimateDread |
| |
//| |
| |
//| THE STRUCTURE READ - what phase the campaign is in, what event |
| |
//| spoke last, whether a spring or upthrust has just shaken the |
| |
//| range, whether the structure has failed. |
| |
//| |
| |
//| Two classes, two jobs: |
| |
//| CWyckoffReader the RULES a Wyckoff trader applies to that |
| |
//| reading, as a pure function of the context. |
| |
//| Changing a rule touches only this class. |
| |
//| CModalityWyckoff fetches the reading from the shared feed |
| |
//| (Signals\Wyckoff\WyckoffFeed.mqh) and stamps |
| |
//| the reader's verdict onto the context. |
| |
//| |
| |
//| WHAT THE VERDICT IS AND IS NOT. The weights below are AUTHORED |
| |
//| priors taken from the two Wyckoff books, not measurements. They |
| |
//| are never used to veto a trade on their own: the verdict is one |
| |
//| bucket dimension in the journal review, so the trades decide |
| |
//| whether "against the structure" is a worse place to buy a dip. |
| |
//| The seven-style scan found no bar-geometry edge beyond the dip |
| |
//| itself, so this is expected to be weak until the journal says |
| |
//| otherwise. |
| |
//| |
| |
//| THE FEED MAY BE ABSENT. The three indicators must be compiled in |
| |
//| MQL5\Indicators. If Create() fails the modality reports it once |
| |
//| and answers false forever; CTX_WYK_OK stays NA and the reviewer |
| |
//| treats the dimension as neutral. |
| |
//+------------------------------------------------------------------+
| |
#ifndef WARRIOR_MODALITYWYCKOFF_MQH
| |
#define WARRIOR_MODALITYWYCKOFF_MQH
| |
| |
#include "Modality.mqh"
| |
#include "..\Signals\Wyckoff\WyckoffFeed.mqh"
| |
| |
//+------------------------------------------------------------------+
| |
//| The rules. Long-side: +1 = the structure supports buying here, |
| |
//| -1 = it argues the dip is the start of markdown, not a shake-out. |
| |
//+------------------------------------------------------------------+
| |
class CWyckoffReader
| |
{
| |
public:
| |
static double Bias(const SMarketContext &c)
| |
{
| |
if(!c.Has(CTX_WYK_OK) || c.v[CTX_WYK_OK] < 0.5)
| |
return CTX_NA;
| |
double s = 0.0;
| |
//--- What the campaign IS: accumulation supports a buy, distribution argues against it.
| |
if(c.Has(CTX_WYK_CHAR))
| |
s += 0.35 * c.v[CTX_WYK_CHAR];
| |
//--- Where it is. Bull C/D/E (spring done, markup starting) is where dips get bought;
| |
//--- bear D/E is the markdown a "dip" belongs to.
| |
if(c.Has(CTX_WYK_PHASE))
| |
{
| |
const double p = c.v[CTX_WYK_PHASE];
| |
if(p >= 3.0) s += 0.20;
| |
else if(p <= -4.0) s -= 0.40;
| |
else if(p <= -3.0) s -= 0.20;
| |
}
| |
//--- The shake-out itself: a spring under support is the textbook long; an upthrust is not.
| |
if(c.Has(CTX_WYK_SPRING) && c.v[CTX_WYK_SPRING] != 0.0)
| |
s += 0.30 * MathMax(-1.0, MathMin(1.0, c.v[CTX_WYK_SPRING] / 3.0));
| |
//--- The most recent event, if it is fresh: SOS/LPS confirm, SOW/LPSY warn.
| |
if(c.Has(CTX_WYK_EVENT) && c.Has(CTX_WYK_AGE) && c.v[CTX_WYK_AGE] <= 10.0)
| |
{
| |
const int code = (int)MathAbs(c.v[CTX_WYK_EVENT]);
| |
const double dir = (c.v[CTX_WYK_EVENT] >= 0.0) ? 1.0 : -1.0;
| |
if(code == WYK_EV_SOS) s += 0.20 * dir;
| |
else if(code == WYK_EV_LPS) s += 0.10 * dir;
| |
else if(code == WYK_EV_SC || code == WYK_EV_ST)
| |
s += 0.05 * dir;
| |
}
| |
//--- A structure that has failed is the strongest thing a trader reads, either way.
| |
if(c.Has(CTX_WYK_FAIL))
| |
s += 0.15 * MathMax(-2.5, MathMin(2.5, c.v[CTX_WYK_FAIL]));
| |
return MathMax(-1.0, MathMin(1.0, s));
| |
}
| |
};
| |
| |
//+------------------------------------------------------------------+
| |
class CModalityWyckoff : public CModality
| |
{
| |
protected:
| |
CIndicators m_owner; // refreshed here; the shared feed may have another owner too
| |
bool m_reported;
| |
string m_symbol;
| |
ENUM_TIMEFRAMES m_tf;
| |
int m_lookback; // bars scanned for the freshest event / spring
| |
| |
public:
| |
CModalityWyckoff(const string symbol, const ENUM_TIMEFRAMES tf,
| |
const int lookback = 10)
| |
: m_reported(false), m_symbol(symbol), m_tf(tf),
| |
m_lookback(lookback)
| |
{
| |
//--- The handles are members of the global feed, not heap objects: a collection that frees
| |
//--- its elements on destruction would delete them.
| |
m_owner.FreeMode(false);
| |
//--- Created HERE, at init, not at the first read: a handle made mid-run has no calculated
| |
//--- buffers yet, so the first trade would be booked with a Wyckoff reading of zeros.
| |
if(!WyckoffFeedEnsure(GetPointer(m_owner), m_symbol, m_tf))
| |
{
| |
m_reported = true;
| |
PrintFormat("CModalityWyckoff: feed unavailable (%s) - the Wyckoff dimension will read"
| |
" as neutral.", g_wyckoffFeed.Why());
| |
}
| |
}
| |
virtual string Name(void) const override { return "wyckoff"; }
| |
virtual bool Read(const SBarRef &bar, SMarketContext &ctx) override;
| |
};
| |
//+------------------------------------------------------------------+
| |
bool CModalityWyckoff::Read(const SBarRef &bar, SMarketContext &ctx)
| |
{
| |
if(!g_wyckoffFeed.Ok())
| |
return false;
| |
m_owner.Refresh();
| |
const int s = bar.shift;
| |
ctx.v[CTX_WYK_OK] = 1.0;
| |
const double phase = g_wyckoffFeed.Phase(s);
| |
ctx.v[CTX_WYK_PHASE] = phase;
| |
ctx.v[CTX_WYK_CHAR] = g_wyckoffFeed.Character(s);
| |
ctx.v[CTX_WYK_FAIL] = g_wyckoffFeed.FailValue(s);
| |
//--- The freshest event and the strongest spring/upthrust inside the look-back window.
| |
double ev = 0.0, age = CTX_NA, spring = 0.0;
| |
for(int k = 0; k < m_lookback; k++)
| |
{
| |
const double e = g_wyckoffFeed.Event(s + k);
| |
if(ev == 0.0 && e != 0.0)
| |
{
| |
ev = e;
| |
age = k;
| |
}
| |
const double sg = g_wyckoffFeed.SpringGrade(s + k);
| |
if(MathAbs(sg) > MathAbs(spring))
| |
spring = sg;
| |
}
| |
ctx.v[CTX_WYK_EVENT] = ev;
| |
ctx.v[CTX_WYK_AGE] = age;
| |
ctx.v[CTX_WYK_SPRING] = spring;
| |
if(g_wyckoffFeed.RangeOpen(s))
| |
{
| |
const double top = g_wyckoffFeed.ZoneTop(s), bot = g_wyckoffFeed.ZoneBottom(s);
| |
const double c = iClose(m_symbol, m_tf, s);
| |
ctx.v[CTX_WYK_RANGEPOS] = MathMax(0.0, MathMin(1.0, (c - bot) / (top - bot)));
| |
}
| |
ctx.v[CTX_WYK_BIAS] = CWyckoffReader::Bias(ctx);
| |
return true;
| |
}
| |
#endif // WARRIOR_MODALITYWYCKOFF_MQH
|