375 行
11 KiB
MQL5
375 行
11 KiB
MQL5
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| ADWyckoffSignificantBarInversion.mq5 |
|
||
|
|
//| Standalone significant-bar quality and movement inversion |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
#property copyright "AD Institutional Indicators"
|
||
|
|
#property link ""
|
||
|
|
#property version "1.00"
|
||
|
|
#property description "Wyckoff significant bar + control flip"
|
||
|
|
#property indicator_separate_window
|
||
|
|
#property indicator_buffers 5
|
||
|
|
#property indicator_plots 5
|
||
|
|
|
||
|
|
#property indicator_label1 "SignificantBarQuality"
|
||
|
|
#property indicator_type1 DRAW_LINE
|
||
|
|
#property indicator_color1 clrGold
|
||
|
|
#property indicator_width1 1
|
||
|
|
|
||
|
|
#property indicator_label2 "BullishSignificantBar"
|
||
|
|
#property indicator_type2 DRAW_LINE
|
||
|
|
#property indicator_color2 clrLime
|
||
|
|
#property indicator_width2 1
|
||
|
|
|
||
|
|
#property indicator_label3 "BearishSignificantBar"
|
||
|
|
#property indicator_type3 DRAW_LINE
|
||
|
|
#property indicator_color3 clrRed
|
||
|
|
#property indicator_width3 1
|
||
|
|
|
||
|
|
#property indicator_label4 "BullishControlFlip"
|
||
|
|
#property indicator_type4 DRAW_LINE
|
||
|
|
#property indicator_color4 clrAqua
|
||
|
|
#property indicator_width4 1
|
||
|
|
|
||
|
|
#property indicator_label5 "BearishControlFlip"
|
||
|
|
#property indicator_type5 DRAW_LINE
|
||
|
|
#property indicator_color5 clrOrange
|
||
|
|
#property indicator_width5 1
|
||
|
|
|
||
|
|
input int InpLookback = 50;
|
||
|
|
input double InpRangeSignificant = 1.2;
|
||
|
|
input double InpVolumeHigh = 1.5;
|
||
|
|
input double InpATR = 0.5;
|
||
|
|
input int InpContextMode = 0; // 0=FixedBars, 1=Session
|
||
|
|
input int InpSessionType = 5; // 1..10
|
||
|
|
input int InpSessionCount = 1; // 1..20
|
||
|
|
|
||
|
|
double BufQuality[];
|
||
|
|
double BufBullSig[];
|
||
|
|
double BufBearSig[];
|
||
|
|
double BufBullFlip[];
|
||
|
|
double BufBearFlip[];
|
||
|
|
|
||
|
|
int ExtLookback;
|
||
|
|
double ExtRangeSignificant;
|
||
|
|
double ExtVolumeHigh;
|
||
|
|
double ExtATR;
|
||
|
|
int ExtContextMode;
|
||
|
|
int ExtSessionType;
|
||
|
|
int ExtSessionCount;
|
||
|
|
|
||
|
|
bool ExtHasBull = false;
|
||
|
|
bool ExtHasBear = false;
|
||
|
|
double ExtLastBullHigh = 0.0;
|
||
|
|
double ExtLastBullLow = 0.0;
|
||
|
|
double ExtLastBearHigh = 0.0;
|
||
|
|
double ExtLastBearLow = 0.0;
|
||
|
|
|
||
|
|
int IsPreviousSessionTypeMT5(const int sessionType)
|
||
|
|
{
|
||
|
|
return (sessionType >= 1 && sessionType <= 4);
|
||
|
|
}
|
||
|
|
|
||
|
|
int IsZigZagSessionTypeMT5(const int sessionType)
|
||
|
|
{
|
||
|
|
return (sessionType == 9 || sessionType == 10);
|
||
|
|
}
|
||
|
|
|
||
|
|
int MapBaseCalendarTypeMT5(const int sessionType)
|
||
|
|
{
|
||
|
|
if(sessionType == 1 || sessionType == 5) return 1;
|
||
|
|
if(sessionType == 2 || sessionType == 6) return 2;
|
||
|
|
if(sessionType == 3 || sessionType == 7) return 3;
|
||
|
|
if(sessionType == 4 || sessionType == 8) return 4;
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
datetime DayStartMT5(const datetime t)
|
||
|
|
{
|
||
|
|
MqlDateTime dt;
|
||
|
|
TimeToStruct(t, dt);
|
||
|
|
dt.hour = 0;
|
||
|
|
dt.min = 0;
|
||
|
|
dt.sec = 0;
|
||
|
|
return StructToTime(dt);
|
||
|
|
}
|
||
|
|
|
||
|
|
datetime AddMonthsSafeMT5(const datetime t, const int months)
|
||
|
|
{
|
||
|
|
MqlDateTime dt;
|
||
|
|
TimeToStruct(t, dt);
|
||
|
|
|
||
|
|
int m = dt.mon + months;
|
||
|
|
while(m > 12)
|
||
|
|
{
|
||
|
|
m -= 12;
|
||
|
|
dt.year++;
|
||
|
|
}
|
||
|
|
while(m < 1)
|
||
|
|
{
|
||
|
|
m += 12;
|
||
|
|
dt.year--;
|
||
|
|
}
|
||
|
|
|
||
|
|
dt.mon = m;
|
||
|
|
dt.day = 1;
|
||
|
|
dt.hour = 0;
|
||
|
|
dt.min = 0;
|
||
|
|
dt.sec = 0;
|
||
|
|
return StructToTime(dt);
|
||
|
|
}
|
||
|
|
|
||
|
|
datetime ResolveCalendarContextStartMT5(const datetime currentTime)
|
||
|
|
{
|
||
|
|
int st = ExtSessionType;
|
||
|
|
int baseType = MapBaseCalendarTypeMT5(st);
|
||
|
|
datetime currentStart = DayStartMT5(currentTime);
|
||
|
|
|
||
|
|
if(baseType == 2)
|
||
|
|
{
|
||
|
|
MqlDateTime w;
|
||
|
|
TimeToStruct(currentStart, w);
|
||
|
|
int dow = w.day_of_week;
|
||
|
|
int back = (dow == 0) ? 6 : (dow - 1);
|
||
|
|
currentStart -= (datetime)(back * 86400);
|
||
|
|
}
|
||
|
|
else if(baseType == 3)
|
||
|
|
{
|
||
|
|
MqlDateTime m;
|
||
|
|
TimeToStruct(currentStart, m);
|
||
|
|
m.day = 1;
|
||
|
|
m.hour = 0;
|
||
|
|
m.min = 0;
|
||
|
|
m.sec = 0;
|
||
|
|
currentStart = StructToTime(m);
|
||
|
|
}
|
||
|
|
else if(baseType == 4)
|
||
|
|
{
|
||
|
|
MqlDateTime y;
|
||
|
|
TimeToStruct(currentStart, y);
|
||
|
|
y.mon = 1;
|
||
|
|
y.day = 1;
|
||
|
|
y.hour = 0;
|
||
|
|
y.min = 0;
|
||
|
|
y.sec = 0;
|
||
|
|
currentStart = StructToTime(y);
|
||
|
|
}
|
||
|
|
|
||
|
|
int shift = IsPreviousSessionTypeMT5(st) ? -ExtSessionCount : -(ExtSessionCount - 1);
|
||
|
|
|
||
|
|
if(baseType == 1)
|
||
|
|
return currentStart + (datetime)(shift * 86400);
|
||
|
|
if(baseType == 2)
|
||
|
|
return currentStart + (datetime)(shift * 7 * 86400);
|
||
|
|
if(baseType == 3)
|
||
|
|
return AddMonthsSafeMT5(currentStart, shift);
|
||
|
|
|
||
|
|
return AddMonthsSafeMT5(currentStart, shift * 12);
|
||
|
|
}
|
||
|
|
|
||
|
|
void ResetState()
|
||
|
|
{
|
||
|
|
ExtHasBull = false;
|
||
|
|
ExtHasBear = false;
|
||
|
|
ExtLastBullHigh = 0.0;
|
||
|
|
ExtLastBullLow = 0.0;
|
||
|
|
ExtLastBearHigh = 0.0;
|
||
|
|
ExtLastBearLow = 0.0;
|
||
|
|
}
|
||
|
|
|
||
|
|
void OnInit()
|
||
|
|
{
|
||
|
|
ExtLookback = (int)MathMax(5, MathMin(200, InpLookback));
|
||
|
|
ExtRangeSignificant = MathMax(0.5, MathMin(3.0, InpRangeSignificant));
|
||
|
|
ExtVolumeHigh = MathMax(0.5, MathMin(3.0, InpVolumeHigh));
|
||
|
|
ExtATR = MathMax(0.1, MathMin(3.0, InpATR));
|
||
|
|
ExtContextMode = (InpContextMode == 1) ? 1 : 0;
|
||
|
|
ExtSessionType = (int)MathMax(1, MathMin(10, InpSessionType));
|
||
|
|
ExtSessionCount = (int)MathMax(1, MathMin(20, InpSessionCount));
|
||
|
|
|
||
|
|
SetIndexBuffer(0, BufQuality, INDICATOR_DATA);
|
||
|
|
SetIndexBuffer(1, BufBullSig, INDICATOR_DATA);
|
||
|
|
SetIndexBuffer(2, BufBearSig, INDICATOR_DATA);
|
||
|
|
SetIndexBuffer(3, BufBullFlip, INDICATOR_DATA);
|
||
|
|
SetIndexBuffer(4, BufBearFlip, INDICATOR_DATA);
|
||
|
|
|
||
|
|
ArraySetAsSeries(BufQuality, true);
|
||
|
|
ArraySetAsSeries(BufBullSig, true);
|
||
|
|
ArraySetAsSeries(BufBearSig, true);
|
||
|
|
ArraySetAsSeries(BufBullFlip, true);
|
||
|
|
ArraySetAsSeries(BufBearFlip, true);
|
||
|
|
|
||
|
|
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, ExtLookback);
|
||
|
|
PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, ExtLookback);
|
||
|
|
PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, ExtLookback);
|
||
|
|
PlotIndexSetInteger(3, PLOT_DRAW_BEGIN, ExtLookback);
|
||
|
|
PlotIndexSetInteger(4, PLOT_DRAW_BEGIN, ExtLookback);
|
||
|
|
|
||
|
|
IndicatorSetInteger(INDICATOR_DIGITS, 3);
|
||
|
|
IndicatorSetString(INDICATOR_SHORTNAME, "WYSB(" + IntegerToString(ExtLookback) + ")");
|
||
|
|
|
||
|
|
ResetState();
|
||
|
|
}
|
||
|
|
|
||
|
|
void OnDeinit(const int reason)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
int ResolveMaxShift(const int i, const int rates_total, const datetime &time[])
|
||
|
|
{
|
||
|
|
int available = rates_total - 1 - i;
|
||
|
|
if(available <= 0)
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
if(ExtContextMode != 1)
|
||
|
|
return MathMin(available, ExtLookback - 1);
|
||
|
|
|
||
|
|
if(IsZigZagSessionTypeMT5(ExtSessionType))
|
||
|
|
return MathMin(available, ExtLookback * ExtSessionCount - 1);
|
||
|
|
|
||
|
|
datetime start = ResolveCalendarContextStartMT5(time[i]);
|
||
|
|
int maxShift = 0;
|
||
|
|
for(int s = 0; s <= available; s++)
|
||
|
|
{
|
||
|
|
int idx = i + s;
|
||
|
|
if(time[idx] < start)
|
||
|
|
break;
|
||
|
|
maxShift = s;
|
||
|
|
}
|
||
|
|
return MathMax(0, maxShift);
|
||
|
|
}
|
||
|
|
|
||
|
|
int OnCalculate(const int rates_total,
|
||
|
|
const int prev_calculated,
|
||
|
|
const datetime &time[],
|
||
|
|
const double &open[],
|
||
|
|
const double &high[],
|
||
|
|
const double &low[],
|
||
|
|
const double &close[],
|
||
|
|
const long &tick_volume[],
|
||
|
|
const long &volume[],
|
||
|
|
const int &spread[])
|
||
|
|
{
|
||
|
|
if(rates_total < 20)
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
ArraySetAsSeries(time, true);
|
||
|
|
ArraySetAsSeries(open, true);
|
||
|
|
ArraySetAsSeries(high, true);
|
||
|
|
ArraySetAsSeries(low, true);
|
||
|
|
ArraySetAsSeries(close, true);
|
||
|
|
ArraySetAsSeries(tick_volume, true);
|
||
|
|
|
||
|
|
ResetState();
|
||
|
|
ArrayInitialize(BufQuality, 0.0);
|
||
|
|
ArrayInitialize(BufBullSig, 0.0);
|
||
|
|
ArrayInitialize(BufBearSig, 0.0);
|
||
|
|
ArrayInitialize(BufBullFlip, 0.0);
|
||
|
|
ArrayInitialize(BufBearFlip, 0.0);
|
||
|
|
|
||
|
|
int startBar = rates_total - 1;
|
||
|
|
|
||
|
|
for(int i = startBar; i >= 0 && !IsStopped(); i--)
|
||
|
|
{
|
||
|
|
int maxShift = ResolveMaxShift(i, rates_total, time);
|
||
|
|
if(maxShift < 1)
|
||
|
|
{
|
||
|
|
BufQuality[i] = 0.0;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
double sumRange = 0.0;
|
||
|
|
double sumVol = 0.0;
|
||
|
|
int n = 0;
|
||
|
|
for(int s = 0; s <= maxShift; s++)
|
||
|
|
{
|
||
|
|
int idx = i + s;
|
||
|
|
double r = high[idx] - low[idx];
|
||
|
|
double v = MathMax((double)tick_volume[idx], 1.0);
|
||
|
|
sumRange += MathMax(r, 0.000001);
|
||
|
|
sumVol += v;
|
||
|
|
n++;
|
||
|
|
}
|
||
|
|
if(n <= 0)
|
||
|
|
continue;
|
||
|
|
|
||
|
|
double avgRange = sumRange / n;
|
||
|
|
double avgVol = sumVol / n;
|
||
|
|
if(avgRange <= 0.0 || avgVol <= 0.0)
|
||
|
|
continue;
|
||
|
|
|
||
|
|
double barRange = MathMax(high[i] - low[i], 0.000001);
|
||
|
|
double barVol = MathMax((double)tick_volume[i], 1.0);
|
||
|
|
double closePos = (close[i] - low[i]) / barRange;
|
||
|
|
if(closePos < 0.0) closePos = 0.0;
|
||
|
|
if(closePos > 1.0) closePos = 1.0;
|
||
|
|
|
||
|
|
double rangeRatio = barRange / avgRange;
|
||
|
|
double volRatio = barVol / avgVol;
|
||
|
|
|
||
|
|
int available = rates_total - 1 - i;
|
||
|
|
int atrN = MathMin(14, available);
|
||
|
|
if(atrN <= 0)
|
||
|
|
atrN = 1;
|
||
|
|
double sumTR = 0.0;
|
||
|
|
for(int t = 0; t < atrN; t++)
|
||
|
|
{
|
||
|
|
int idx = i + t;
|
||
|
|
double h = high[idx];
|
||
|
|
double l = low[idx];
|
||
|
|
double pc = (idx + 1 < rates_total) ? close[idx + 1] : close[idx];
|
||
|
|
double tr = MathMax(h - l, MathMax(MathAbs(h - pc), MathAbs(l - pc)));
|
||
|
|
sumTR += tr;
|
||
|
|
}
|
||
|
|
double atr = sumTR / atrN;
|
||
|
|
double atrRatio = barRange / MathMax(atr * ExtATR, 0.000001);
|
||
|
|
|
||
|
|
double fallbackResistance = (i + 1 < rates_total) ? high[i + 1] : high[i];
|
||
|
|
double fallbackSupport = (i + 1 < rates_total) ? low[i + 1] : low[i];
|
||
|
|
double priorResistance = ExtHasBear ? ExtLastBearHigh : fallbackResistance;
|
||
|
|
double priorSupport = ExtHasBull ? ExtLastBullLow : fallbackSupport;
|
||
|
|
bool bullStructuralBreak = (close[i] > priorResistance && high[i] > priorResistance);
|
||
|
|
bool bearStructuralBreak = (close[i] < priorSupport && low[i] < priorSupport);
|
||
|
|
|
||
|
|
bool bullSig = (rangeRatio >= ExtRangeSignificant && volRatio >= ExtVolumeHigh && closePos >= 0.70 && close[i] > open[i] && atrRatio >= 1.0 && bullStructuralBreak);
|
||
|
|
bool bearSig = (rangeRatio >= ExtRangeSignificant && volRatio >= ExtVolumeHigh && closePos <= 0.30 && close[i] < open[i] && atrRatio >= 1.0 && bearStructuralBreak);
|
||
|
|
|
||
|
|
double bullScore = ((rangeRatio / ExtRangeSignificant) + (volRatio / ExtVolumeHigh) + closePos) / 3.0;
|
||
|
|
double bearScore = ((rangeRatio / ExtRangeSignificant) + (volRatio / ExtVolumeHigh) + (1.0 - closePos)) / 3.0;
|
||
|
|
if(bullScore < 0.0) bullScore = 0.0;
|
||
|
|
if(bearScore < 0.0) bearScore = 0.0;
|
||
|
|
if(bullScore > 2.0) bullScore = 2.0;
|
||
|
|
if(bearScore > 2.0) bearScore = 2.0;
|
||
|
|
|
||
|
|
double quality = 0.0;
|
||
|
|
if(bullSig) quality = bullScore;
|
||
|
|
else if(bearSig) quality = -bearScore;
|
||
|
|
|
||
|
|
if(bullSig)
|
||
|
|
{
|
||
|
|
ExtHasBull = true;
|
||
|
|
ExtLastBullHigh = high[i];
|
||
|
|
ExtLastBullLow = low[i];
|
||
|
|
}
|
||
|
|
if(bearSig)
|
||
|
|
{
|
||
|
|
ExtHasBear = true;
|
||
|
|
ExtLastBearHigh = high[i];
|
||
|
|
ExtLastBearLow = low[i];
|
||
|
|
}
|
||
|
|
|
||
|
|
double prevClose = (i + 1 < rates_total) ? close[i + 1] : close[i];
|
||
|
|
bool bullFlip = (ExtHasBear && prevClose <= ExtLastBearHigh && close[i] > ExtLastBearHigh);
|
||
|
|
bool bearFlip = (ExtHasBull && prevClose >= ExtLastBullLow && close[i] < ExtLastBullLow);
|
||
|
|
|
||
|
|
// Normalize: guard against NaN/Inf from edge-case inputs
|
||
|
|
if(!MathIsValidNumber(quality)) quality = 0.0;
|
||
|
|
|
||
|
|
BufQuality[i] = quality;
|
||
|
|
BufBullSig[i] = bullSig ? 1.0 : 0.0;
|
||
|
|
BufBearSig[i] = bearSig ? 1.0 : 0.0;
|
||
|
|
BufBullFlip[i] = bullFlip ? 1.0 : 0.0;
|
||
|
|
BufBearFlip[i] = bearFlip ? 1.0 : 0.0;
|
||
|
|
}
|
||
|
|
|
||
|
|
return rates_total;
|
||
|
|
}
|