200 lines
9.4 KiB
MQL5
200 lines
9.4 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| SignalWyckoffEvent.mqh |
|
|
//| AnimateDread |
|
|
//| |
|
|
//| The ADWyckoffEventStream as a CLASSIC VOTE MODULE. |
|
|
//| |
|
|
//| It states an opinion; it does not own a trade. That distinction |
|
|
//| is the one IsClassicVoter() now tests (OwnsTrade() == false), and |
|
|
//| it is deliberate here: the event stream says "this is a spring in |
|
|
//| phase C of an accumulation", which is a reason to lean long, not |
|
|
//| a complete strategy with an entry, a stop and a target. The book |
|
|
//| setups own trades; this module votes alongside the fifteen other |
|
|
//| classic modules and is weighted like them. |
|
|
//| |
|
|
//| PATTERNS, ordered by the book's own conviction (Wyckoff's phases |
|
|
//| are a sequence, and later phases are later BECAUSE they are more |
|
|
//| confirmed): |
|
|
//| 0 Spring / UTAD, graded 1-3 by the indicator. The shake. The |
|
|
//| highest-conviction entry the method has, and the grade is |
|
|
//| the indicator's own confidence in it. |
|
|
//| 1 SOS / SOW - phase D confirmation, the range resolving. |
|
|
//| 2 LPS / LPSY - the pullback that holds after an SOS. |
|
|
//| 3 Test, placed well (upper half of an accumulation / below a |
|
|
//| climax), with the range character agreeing. |
|
|
//| 4 Change of character, range -> trend, with slope context. |
|
|
//| |
|
|
//| ALL READS GO THROUGH CWyckoffFeed AT SHIFT 1. Nothing here knows |
|
|
//| a buffer index; when the indicator is revised again this file |
|
|
//| does not change unless the MEANING changed. |
|
|
//+------------------------------------------------------------------+
|
|
#ifndef WARRIOR_SIGNALWYCKOFFEVENT_MQH
|
|
#define WARRIOR_SIGNALWYCKOFFEVENT_MQH
|
|
|
|
#include "..\..\Expert\WarriorSignal.mqh"
|
|
#include "WyckoffFeed.mqh"
|
|
|
|
class CSignalWyckoffEvent : public CWarriorSignal
|
|
{
|
|
protected:
|
|
//--- indicator inputs, exposed so a sweep can move them without editing this file
|
|
int m_lookback;
|
|
int m_zigzag;
|
|
int m_arLegs;
|
|
double m_boundaryTol;
|
|
int m_maxRangeBars;
|
|
//--- pattern weights (0-100)
|
|
int m_pattern_0; // spring / UTAD, scaled by the indicator's grade
|
|
int m_pattern_1; // SOS / SOW
|
|
int m_pattern_2; // LPS / LPSY
|
|
int m_pattern_3; // well-placed test
|
|
int m_pattern_4; // change of character, range -> trend
|
|
|
|
public:
|
|
CSignalWyckoffEvent(void);
|
|
~CSignalWyckoffEvent(void) {}
|
|
void Lookback(const int v) { m_lookback = v; }
|
|
void ZigZag(const int v) { m_zigzag = v; }
|
|
void ARLegs(const int v) { m_arLegs = v; }
|
|
void BoundaryTol(const double v) { m_boundaryTol = v; }
|
|
void MaxRangeBars(const int v) { m_maxRangeBars = v; }
|
|
void Pattern_0(const int v) { m_pattern_0 = v; }
|
|
void Pattern_1(const int v) { m_pattern_1 = v; }
|
|
void Pattern_2(const int v) { m_pattern_2 = v; }
|
|
void Pattern_3(const int v) { m_pattern_3 = v; }
|
|
void Pattern_4(const int v) { m_pattern_4 = v; }
|
|
virtual void ApplyPatternWeight(int patternNumber, int weight);
|
|
virtual bool InitIndicators(CIndicators *indicators);
|
|
virtual int LongCondition(void);
|
|
virtual int ShortCondition(void);
|
|
//--- The feed is the shared g_wyckoffFeed - see WyckoffFeedEnsure(). Nothing owns it.
|
|
|
|
protected:
|
|
//--- One implementation, mirrored by direction. `dir` is +1 for the long case, -1 for the short,
|
|
//--- and every comparison below is written against it so the two sides cannot drift apart -
|
|
//--- which is exactly how m_entry came to be wiped by its own other side (project note 09-08).
|
|
int Condition(const int dir);
|
|
void Note(const int pattern, const int dir);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
CSignalWyckoffEvent::CSignalWyckoffEvent(void) :
|
|
m_lookback(50), m_zigzag(3), m_arLegs(3), m_boundaryTol(0.50), m_maxRangeBars(200),
|
|
m_pattern_0(80), m_pattern_1(60), m_pattern_2(45), m_pattern_3(30), m_pattern_4(20)
|
|
{
|
|
m_id = "WYKEV";
|
|
m_pattern_count = 5;
|
|
m_used_series = USE_SERIES_OPEN + USE_SERIES_HIGH + USE_SERIES_LOW + USE_SERIES_CLOSE;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
void CSignalWyckoffEvent::ApplyPatternWeight(int patternNumber, int weight)
|
|
{
|
|
switch(patternNumber)
|
|
{
|
|
case 0: Pattern_0(weight); break;
|
|
case 1: Pattern_1(weight); break;
|
|
case 2: Pattern_2(weight); break;
|
|
case 3: Pattern_3(weight); break;
|
|
case 4: Pattern_4(weight); break;
|
|
default: break;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
bool CSignalWyckoffEvent::InitIndicators(CIndicators *indicators)
|
|
{
|
|
if(indicators == NULL)
|
|
return(false);
|
|
if(!CWarriorSignal::InitIndicators(indicators))
|
|
return(false);
|
|
if(!WyckoffFeedEnsure(indicators, m_symbol.Name(), m_period,
|
|
m_lookback, m_zigzag, m_arLegs, m_boundaryTol, m_maxRangeBars))
|
|
{
|
|
//--- LOUD, AND FATAL. A feed that did not load reads zeros, and zeros are indistinguishable
|
|
//--- from "no Wyckoff event here" - the module would abstain forever and look healthy.
|
|
PrintFormat("%s: WYCKOFF FEED FAILED - %s. This module cannot vote and init is refused rather"
|
|
" than leaving a silent abstainer in the divisor.", __FUNCTION__, g_wyckoffFeed.Why());
|
|
return(false);
|
|
}
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
int CSignalWyckoffEvent::LongCondition(void)
|
|
{
|
|
return Condition(+1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
int CSignalWyckoffEvent::ShortCondition(void)
|
|
{
|
|
return Condition(-1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//--- Names the pattern that produced this vote. The journal keys its tables on it, so it is part of
|
|
//--- the module's contract with the database rather than a log string: change one of these names and
|
|
//--- that pattern's history splits into a before and an after that nothing will ever join up.
|
|
void CSignalWyckoffEvent::Note(const int pattern, const int dir)
|
|
{
|
|
m_active_pattern = "Pattern_" + IntegerToString(pattern);
|
|
m_active_direction = (dir > 0) ? "Buy" : "Sell";
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
int CSignalWyckoffEvent::Condition(const int dir)
|
|
{
|
|
if(!g_wyckoffFeed.Ok())
|
|
return 0;
|
|
|
|
const int code = g_wyckoffFeed.EventCode(); // 0..9, direction stripped
|
|
const int evDir = g_wyckoffFeed.EventDir(); // +1 bullish structure, -1 bearish, 0 none
|
|
const double grade = g_wyckoffFeed.SpringGrade(); // +-1..+-3 on the shake bar
|
|
const double charact = g_wyckoffFeed.Character(); // +1 accumulation, -1 distribution
|
|
const int phase = g_wyckoffFeed.PhaseLetter(); // 1..5 = A..E
|
|
|
|
//--- 0: THE SHAKE. Graded by the indicator, and the grade is carried through to the vote rather
|
|
//--- than flattened: grade 3 is "direct entry", grade 1 is "wait for the test", and a module that
|
|
//--- returned the same weight for both would be discarding the indicator's own confidence.
|
|
if(code == WYK_EV_SPRING && evDir == dir)
|
|
{
|
|
Note(0, dir);
|
|
const int g = (int)MathAbs(grade);
|
|
if(g >= 1 && g <= 3)
|
|
return (int)MathRound(m_pattern_0 * (g / 3.0));
|
|
return m_pattern_0 / 2; // a spring the grader could not rank
|
|
}
|
|
|
|
//--- 1: PHASE D CONFIRMATION. The range is resolving in `dir`.
|
|
if(code == WYK_EV_SOS && evDir == dir)
|
|
{
|
|
Note(1, dir);
|
|
return m_pattern_1;
|
|
}
|
|
|
|
//--- 2: THE PULLBACK THAT HELD.
|
|
if(code == WYK_EV_LPS && evDir == dir)
|
|
{
|
|
Note(2, dir);
|
|
return m_pattern_2;
|
|
}
|
|
|
|
//--- 3: A WELL-PLACED TEST. The test alone is weak; the test plus a range whose character agrees
|
|
//--- with the side being voted is the book's actual condition, so both are required.
|
|
if(code == WYK_EV_TEST && evDir == dir)
|
|
{
|
|
const double place = g_wyckoffFeed.TestPlace();
|
|
const bool characterAgrees = (dir > 0) ? (charact > 0.0) : (charact < 0.0);
|
|
const bool placedWell = (dir > 0) ? (place > 0.0) : (place < 0.0);
|
|
if(characterAgrees && placedWell)
|
|
{
|
|
Note(3, dir);
|
|
return m_pattern_3;
|
|
}
|
|
}
|
|
|
|
//--- 4: CHANGE OF CHARACTER, range -> trend, only while the phase agrees with the side. Phase is
|
|
//--- signed by direction in the buffer, so PhaseDir() is what makes this directional at all.
|
|
if(g_wyckoffFeed.ChochRangeToTrend() != 0.0 && g_wyckoffFeed.PhaseDir() == dir && phase >= WYK_PHASE_D)
|
|
{
|
|
Note(4, dir);
|
|
return m_pattern_4;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif // WARRIOR_SIGNALWYCKOFFEVENT_MQH
|