forked from animatedread/Warrior_EA
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
|