773 lines
No EOL
21 KiB
MQL5
773 lines
No EOL
21 KiB
MQL5
//+------------------------------------------------------------------+
| |
//| C_Chapter4_SupplyDemand.mqh |
| |
//+------------------------------------------------------------------+
| |
#property strict
| |
#property version "1.00"
| |
| |
#include "AbstractChapter.mqh"
| |
| |
//========================
| |
// Inputs
| |
//========================
| |
input int ch4_zoneLookbackBars = 350;
| |
input int ch4_baseLookbackBars = 8;
| |
input int ch4_momentumAvgPeriod = 12;
| |
input double ch4_momentumBodyFactor = 1.20;
| |
input int ch4_protectedTolerancePts = 35;
| |
input int ch4_minZoneSeparationBars = 3;
| |
input int ch4_refineMode = 1; // 0=full, 1=body, 2=half
| |
input int ch4_minQualityScore = 3; // BOS + momentum + liquidity + imbalance
| |
input bool ch4_requireBos = true;
| |
input bool ch4_requireMomentum = true;
| |
input bool ch4_requireLiquidity = false;
| |
input bool ch4_requireImbalance = false;
| |
input bool ch4_hideInvalidZones = false;
| |
| |
//========================
| |
// Enums
| |
//========================
| |
enum EZoneSide { ZONE_DEMAND = 0, ZONE_SUPPLY = 1 };
| |
enum EZoneType { ZONE_OB = 0, ZONE_EXTREME = 1, ZONE_DECISIONAL = 2 };
| |
enum EZoneState { ZONE_FRESH = 0, ZONE_TOUCHED = 1, ZONE_MITIGATED = 2, ZONE_INVALID = 3 };
| |
| |
struct SupplyDemandZone {
| |
int id;
| |
EZoneSide side;
| |
EZoneType type;
| |
EZoneState state;
| |
| |
int leftBar;
| |
int baseBar;
| |
int breakBar;
| |
int rightBar;
| |
| |
double fullTop;
| |
double fullBottom;
| |
| |
double refinedTop;
| |
double refinedBottom;
| |
| |
bool hasBos;
| |
bool hasMomentum;
| |
bool hasLiquidity;
| |
bool hasImbalance;
| |
bool isInducement;
| |
| |
int qualityScore;
| |
int touchBar;
| |
int mitigateBar;
| |
int invalidateBar;
| |
};
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
class C_Chapter4 : public CAbstractChapter {
| |
private:
| |
FairValue fairValue;
| |
| |
SupplyDemandZone zones[];
| |
int nextZoneId;
| |
| |
int protectedHigh;
| |
int protectedLow;
| |
| |
string K_ZONE(int id) { return "CH4_ZONE_" + (string)id; }
| |
string K_ZONE_R(int id) { return "CH4_ZONE_R_" + (string)id; }
| |
| |
private:
| |
//========================
| |
// Core helpers
| |
//========================
| |
void PushZone(SupplyDemandZone &z) {
| |
int n = ArraySize(zones);
| |
ArrayResize(zones, n + 1);
| |
zones[n] = z;
| |
}
| |
| |
bool ZoneExists(EZoneSide side, int baseBar, int breakBar, double top, double bottom) {
| |
for(int i = 0; i < ArraySize(zones); i++) {
| |
if(zones[i].side != side)
| |
continue;
| |
| |
if(zones[i].baseBar != baseBar)
| |
continue;
| |
| |
if(zones[i].breakBar != breakBar)
| |
continue;
| |
| |
if(MathAbs(zones[i].fullTop - top) <= _Point * 2 &&
| |
MathAbs(zones[i].fullBottom - bottom) <= _Point * 2)
| |
return true;
| |
}
| |
| |
return false;
| |
}
| |
| |
bool HasBullImbalanceAround(int breakBar) {
| |
int from = breakBar - 1;
| |
int to = breakBar + 1;
| |
| |
if(from < 1) from = 1;
| |
if(to >= Bars()) to = Bars() - 1;
| |
| |
for(int i = from; i <= to; i++) {
| |
Fvg fvg;
| |
fairValue.getBullFvg(fvg, i);
| |
if(fairValue.IsValidValue(fvg))
| |
return true;
| |
}
| |
| |
return false;
| |
}
| |
| |
bool HasBearImbalanceAround(int breakBar) {
| |
int from = breakBar - 1;
| |
int to = breakBar + 1;
| |
| |
if(from < 1) from = 1;
| |
if(to >= Bars()) to = Bars() - 1;
| |
| |
for(int i = from; i <= to; i++) {
| |
Fvg fvg;
| |
fairValue.getBearFvg(fvg, i);
| |
if(fairValue.IsValidValue(fvg))
| |
return true;
| |
}
| |
| |
return false;
| |
}
| |
| |
double AvgBodySize(int endIndex, int period) {
| |
if(period <= 0)
| |
return 0.0;
| |
| |
int from = endIndex - period + 1;
| |
if(from < 0)
| |
from = 0;
| |
| |
double sum = 0.0;
| |
int count = 0;
| |
| |
for(int i = from; i <= endIndex; i++) {
| |
if(!IsValidIndex(i))
| |
continue;
| |
| |
sum += BodySize(i);
| |
count++;
| |
}
| |
| |
if(count == 0)
| |
return 0.0;
| |
| |
return sum / count;
| |
}
| |
| |
bool IsBullMomentumBar(int i) {
| |
if(!IsBullishCandle(i))
| |
return false;
| |
| |
double avg = AvgBodySize(i - 1, ch4_momentumAvgPeriod);
| |
if(avg <= 0.0)
| |
avg = BodySize(i);
| |
| |
return BodySize(i) >= avg * ch4_momentumBodyFactor;
| |
}
| |
| |
bool IsBearMomentumBar(int i) {
| |
if(!IsBearishCandle(i))
| |
return false;
| |
| |
double avg = AvgBodySize(i - 1, ch4_momentumAvgPeriod);
| |
if(avg <= 0.0)
| |
avg = BodySize(i);
| |
| |
return BodySize(i) >= avg * ch4_momentumBodyFactor;
| |
}
| |
| |
int FindBrokenSwingHigh(int breakBar) {
| |
int start = breakBar - ch4_baseLookbackBars - 10;
| |
if(start < 0) start = 0;
| |
| |
int last = -1;
| |
| |
for(int i = start; i < breakBar; i++) {
| |
if(!IsSwingHigh(i))
| |
continue;
| |
| |
if(BreakAboveHigh(i, breakBar))
| |
last = i;
| |
}
| |
| |
return last;
| |
}
| |
| |
int FindBrokenSwingLow(int breakBar) {
| |
int start = breakBar - ch4_baseLookbackBars - 10;
| |
if(start < 0) start = 0;
| |
| |
int last = -1;
| |
| |
for(int i = start; i < breakBar; i++) {
| |
if(!IsSwingLow(i))
| |
continue;
| |
| |
if(BreakBelowLow(i, breakBar))
| |
last = i;
| |
}
| |
| |
return last;
| |
}
| |
| |
int FindDemandBaseBar(int breakBar) {
| |
int from = breakBar - ch4_baseLookbackBars;
| |
if(from < 0) from = 0;
| |
| |
for(int i = breakBar - 1; i >= from; i--) {
| |
if(IsBearishCandle(i) || IsSellToBuyCandle(i) || IsDojiCandle(i, 0.20))
| |
return i;
| |
}
| |
| |
return breakBar - 1;
| |
}
| |
| |
int FindSupplyBaseBar(int breakBar) {
| |
int from = breakBar - ch4_baseLookbackBars;
| |
if(from < 0) from = 0;
| |
| |
for(int i = breakBar - 1; i >= from; i--) {
| |
if(IsBullishCandle(i) || IsBuyToSellCandle(i) || IsDojiCandle(i, 0.20))
| |
return i;
| |
}
| |
| |
return breakBar - 1;
| |
}
| |
| |
int FindBaseStartBar(int baseBar) {
| |
if(!IsValidIndex(baseBar))
| |
return -1;
| |
| |
int from = baseBar - ch4_baseLookbackBars;
| |
if(from < 0) from = 0;
| |
| |
int left = baseBar;
| |
| |
for(int i = baseBar; i >= from; i--) {
| |
if(RangeAt(i) <= 0.0)
| |
continue;
| |
| |
left = i;
| |
| |
if(BodySize(i) > AvgBodySize(i, 3) * 1.4)
| |
break;
| |
}
| |
| |
return left;
| |
}
| |
| |
void BuildDemandZone(int leftBar,int baseBar,int breakBar,double &fullTop,double &fullBottom,double &refinedTop,double &refinedBottom) {
| |
fullTop = HighAt(baseBar);
| |
fullBottom = LowAt(baseBar);
| |
| |
for(int i = leftBar; i <= baseBar; i++) {
| |
if(HighAt(i) > fullTop) fullTop = HighAt(i);
| |
if(LowAt(i) < fullBottom) fullBottom = LowAt(i);
| |
}
| |
| |
if(ch4_refineMode == 0) {
| |
refinedTop = fullTop;
| |
refinedBottom = fullBottom;
| |
} else if(ch4_refineMode == 1) {
| |
refinedTop = BodyTop(baseBar);
| |
refinedBottom = BodyBottom(baseBar);
| |
} else {
| |
refinedTop = fullTop;
| |
refinedBottom = ZoneMid(fullTop, fullBottom);
| |
}
| |
}
| |
| |
void BuildSupplyZone(int leftBar,int baseBar,int breakBar,double &fullTop,double &fullBottom,double &refinedTop,double &refinedBottom) {
| |
fullTop = HighAt(baseBar);
| |
fullBottom = LowAt(baseBar);
| |
| |
for(int i = leftBar; i <= baseBar; i++) {
| |
if(HighAt(i) > fullTop) fullTop = HighAt(i);
| |
if(LowAt(i) < fullBottom) fullBottom = LowAt(i);
| |
}
| |
| |
if(ch4_refineMode == 0) {
| |
refinedTop = fullTop;
| |
refinedBottom = fullBottom;
| |
} else if(ch4_refineMode == 1) {
| |
refinedTop = BodyTop(baseBar);
| |
refinedBottom = BodyBottom(baseBar);
| |
} else {
| |
refinedTop = ZoneMid(fullTop, fullBottom);
| |
refinedBottom = fullBottom;
| |
}
| |
}
| |
| |
bool NearProtectedLow(int baseBar) {
| |
if(!IsValidIndex(protectedLow))
| |
return false;
| |
| |
return NearPrice(LowAt(baseBar), LowAt(protectedLow), ch4_protectedTolerancePts);
| |
}
| |
| |
bool NearProtectedHigh(int baseBar) {
| |
if(!IsValidIndex(protectedHigh))
| |
return false;
| |
| |
return NearPrice(HighAt(baseBar), HighAt(protectedHigh), ch4_protectedTolerancePts);
| |
}
| |
| |
bool HasBullLiquidityContext(int baseBar, int breakBar) {
| |
if(IsValidIndex(protectedLow)) {
| |
int from = baseBar - 3;
| |
if(from < 0) from = 0;
| |
| |
for(int i = from; i <= breakBar; i++) {
| |
if(WickBelowLow(protectedLow, i) || BreakBelowLow(protectedLow, i))
| |
return true;
| |
}
| |
}
| |
| |
int lowA = PrevSwingLowIndex(baseBar, 1);
| |
int lowB = PrevSwingLowIndex(baseBar, 2);
| |
if(IsValidIndex(lowA) && IsValidIndex(lowB)) {
| |
if(NearPrice(LowAt(lowA), LowAt(lowB), ch4_protectedTolerancePts))
| |
return true;
| |
}
| |
| |
return false;
| |
}
| |
| |
bool HasBearLiquidityContext(int baseBar, int breakBar) {
| |
if(IsValidIndex(protectedHigh)) {
| |
int from = baseBar - 3;
| |
if(from < 0) from = 0;
| |
| |
for(int i = from; i <= breakBar; i++) {
| |
if(WickAboveHigh(protectedHigh, i) || BreakAboveHigh(protectedHigh, i))
| |
return true;
| |
}
| |
}
| |
| |
int highA = PrevSwingHighIndex(baseBar, 1);
| |
int highB = PrevSwingHighIndex(baseBar, 2);
| |
if(IsValidIndex(highA) && IsValidIndex(highB)) {
| |
if(NearPrice(HighAt(highA), HighAt(highB), ch4_protectedTolerancePts))
| |
return true;
| |
}
| |
| |
return false;
| |
}
| |
| |
EZoneType ClassifyDemandType(int baseBar, int breakBar) {
| |
if(NearProtectedLow(baseBar))
| |
return ZONE_EXTREME;
| |
| |
return ZONE_DECISIONAL;
| |
}
| |
| |
EZoneType ClassifySupplyType(int baseBar, int breakBar) {
| |
if(NearProtectedHigh(baseBar))
| |
return ZONE_EXTREME;
| |
| |
return ZONE_DECISIONAL;
| |
}
| |
| |
int ScoreZone(bool hasBos, bool hasMomentum, bool hasLiquidity, bool hasImbalance) {
| |
int score = 0;
| |
if(hasBos) score++;
| |
if(hasMomentum) score++;
| |
if(hasLiquidity) score++;
| |
if(hasImbalance) score++;
| |
return score;
| |
}
| |
| |
bool PassesFilters(bool hasBos, bool hasMomentum, bool hasLiquidity, bool hasImbalance, int score) {
| |
if(ch4_requireBos && !hasBos) return false;
| |
if(ch4_requireMomentum && !hasMomentum) return false;
| |
if(ch4_requireLiquidity && !hasLiquidity) return false;
| |
if(ch4_requireImbalance && !hasImbalance) return false;
| |
| |
return score >= ch4_minQualityScore;
| |
}
| |
| |
//========================
| |
// Detection
| |
//========================
| |
void DetectDemandZones() {
| |
int end = Bars() - 1;
| |
if(end < 10)
| |
return;
| |
| |
int start = end - ch4_zoneLookbackBars;
| |
if(start < 5) start = 5;
| |
| |
for(int i = start; i <= end; i++) {
| |
if(!IsBullMomentumBar(i))
| |
continue;
| |
| |
int brokenHigh = FindBrokenSwingHigh(i);
| |
bool hasBos = IsValidIndex(brokenHigh);
| |
| |
if(ch4_requireBos && !hasBos)
| |
continue;
| |
| |
int baseBar = FindDemandBaseBar(i);
| |
if(!IsValidIndex(baseBar))
| |
continue;
| |
| |
int leftBar = FindBaseStartBar(baseBar);
| |
if(!IsValidIndex(leftBar))
| |
leftBar = baseBar;
| |
| |
double fullTop, fullBottom, refinedTop, refinedBottom;
| |
BuildDemandZone(leftBar, baseBar, i, fullTop, fullBottom, refinedTop, refinedBottom);
| |
| |
bool hasMomentum = true;
| |
bool hasLiquidity = HasBullLiquidityContext(baseBar, i);
| |
bool hasImbalance = HasBullImbalanceAround(i);
| |
| |
int score = ScoreZone(hasBos, hasMomentum, hasLiquidity, hasImbalance);
| |
if(!PassesFilters(hasBos, hasMomentum, hasLiquidity, hasImbalance, score))
| |
continue;
| |
| |
if(ZoneExists(ZONE_DEMAND, baseBar, i, fullTop, fullBottom))
| |
continue;
| |
| |
SupplyDemandZone z;
| |
z.id = nextZoneId++;
| |
z.side = ZONE_DEMAND;
| |
z.type = ClassifyDemandType(baseBar, i);
| |
z.state = ZONE_FRESH;
| |
| |
z.leftBar = leftBar;
| |
z.baseBar = baseBar;
| |
z.breakBar = i;
| |
z.rightBar = i;
| |
| |
z.fullTop = fullTop;
| |
z.fullBottom = fullBottom;
| |
z.refinedTop = refinedTop;
| |
z.refinedBottom = refinedBottom;
| |
| |
z.hasBos = hasBos;
| |
z.hasMomentum = hasMomentum;
| |
z.hasLiquidity = hasLiquidity;
| |
z.hasImbalance = hasImbalance;
| |
z.isInducement = false;
| |
| |
z.qualityScore = score;
| |
z.touchBar = -1;
| |
z.mitigateBar = -1;
| |
z.invalidateBar = -1;
| |
| |
PushZone(z);
| |
}
| |
}
| |
| |
void DetectSupplyZones() {
| |
int end = Bars() - 1;
| |
if(end < 10)
| |
return;
| |
| |
int start = end - ch4_zoneLookbackBars;
| |
if(start < 5) start = 5;
| |
| |
for(int i = start; i <= end; i++) {
| |
if(!IsBearMomentumBar(i))
| |
continue;
| |
| |
int brokenLow = FindBrokenSwingLow(i);
| |
bool hasBos = IsValidIndex(brokenLow);
| |
| |
if(ch4_requireBos && !hasBos)
| |
continue;
| |
| |
int baseBar = FindSupplyBaseBar(i);
| |
if(!IsValidIndex(baseBar))
| |
continue;
| |
| |
int leftBar = FindBaseStartBar(baseBar);
| |
if(!IsValidIndex(leftBar))
| |
leftBar = baseBar;
| |
| |
double fullTop, fullBottom, refinedTop, refinedBottom;
| |
BuildSupplyZone(leftBar, baseBar, i, fullTop, fullBottom, refinedTop, refinedBottom);
| |
| |
bool hasMomentum = true;
| |
bool hasLiquidity = HasBearLiquidityContext(baseBar, i);
| |
bool hasImbalance = HasBearImbalanceAround(i);
| |
| |
int score = ScoreZone(hasBos, hasMomentum, hasLiquidity, hasImbalance);
| |
if(!PassesFilters(hasBos, hasMomentum, hasLiquidity, hasImbalance, score))
| |
continue;
| |
| |
if(ZoneExists(ZONE_SUPPLY, baseBar, i, fullTop, fullBottom))
| |
continue;
| |
| |
SupplyDemandZone z;
| |
z.id = nextZoneId++;
| |
z.side = ZONE_SUPPLY;
| |
z.type = ClassifySupplyType(baseBar, i);
| |
z.state = ZONE_FRESH;
| |
| |
z.leftBar = leftBar;
| |
z.baseBar = baseBar;
| |
z.breakBar = i;
| |
z.rightBar = i;
| |
| |
z.fullTop = fullTop;
| |
z.fullBottom = fullBottom;
| |
z.refinedTop = refinedTop;
| |
z.refinedBottom = refinedBottom;
| |
| |
z.hasBos = hasBos;
| |
z.hasMomentum = hasMomentum;
| |
z.hasLiquidity = hasLiquidity;
| |
z.hasImbalance = hasImbalance;
| |
z.isInducement = false;
| |
| |
z.qualityScore = score;
| |
z.touchBar = -1;
| |
z.mitigateBar = -1;
| |
z.invalidateBar = -1;
| |
| |
PushZone(z);
| |
}
| |
}
| |
| |
//========================
| |
// Inducement tagging
| |
//========================
| |
void MarkInducementZones() {
| |
for(int i = 0; i < ArraySize(zones); i++) {
| |
zones[i].isInducement = false;
| |
}
| |
| |
for(int i = 0; i < ArraySize(zones); i++) {
| |
for(int j = 0; j < ArraySize(zones); j++) {
| |
if(i == j)
| |
continue;
| |
| |
if(zones[i].side != zones[j].side)
| |
continue;
| |
| |
if(zones[i].state == ZONE_INVALID || zones[j].state == ZONE_INVALID)
| |
continue;
| |
| |
if(zones[i].side == ZONE_DEMAND) {
| |
if(zones[i].baseBar < zones[j].baseBar &&
| |
zones[i].fullBottom > zones[j].fullBottom &&
| |
zones[i].qualityScore < zones[j].qualityScore) {
| |
zones[i].isInducement = true;
| |
}
| |
} else {
| |
if(zones[i].baseBar < zones[j].baseBar &&
| |
zones[i].fullTop < zones[j].fullTop &&
| |
zones[i].qualityScore < zones[j].qualityScore) {
| |
zones[i].isInducement = true;
| |
}
| |
}
| |
}
| |
}
| |
}
| |
| |
//========================
| |
// Lifecycle
| |
//========================
| |
void UpdateZoneState() {
| |
int end = Bars() - 1;
| |
| |
for(int i = 0; i < ArraySize(zones); i++) {
| |
if(zones[i].state == ZONE_INVALID)
| |
continue;
| |
| |
for(int b = zones[i].breakBar + 1; b <= end; b++) {
| |
bool touched = PriceTouchesZone(b, zones[i].fullTop, zones[i].fullBottom);
| |
bool reached50 = false;
| |
bool invalid = false;
| |
| |
if(zones[i].side == ZONE_DEMAND) {
| |
reached50 = LowAt(b) <= ZoneMid(zones[i].fullTop, zones[i].fullBottom);
| |
invalid = PriceClosesBeyondDemand(b, zones[i].fullBottom);
| |
} else {
| |
reached50 = HighAt(b) >= ZoneMid(zones[i].fullTop, zones[i].fullBottom);
| |
invalid = PriceClosesBeyondSupply(b, zones[i].fullTop);
| |
}
| |
| |
if(touched && zones[i].touchBar < 0) {
| |
zones[i].touchBar = b;
| |
if(zones[i].state == ZONE_FRESH)
| |
zones[i].state = ZONE_TOUCHED;
| |
}
| |
| |
if(reached50 && zones[i].mitigateBar < 0) {
| |
zones[i].mitigateBar = b;
| |
if(zones[i].state != ZONE_INVALID)
| |
zones[i].state = ZONE_MITIGATED;
| |
}
| |
| |
if(invalid) {
| |
zones[i].invalidateBar = b;
| |
zones[i].state = ZONE_INVALID;
| |
break;
| |
}
| |
}
| |
}
| |
}
| |
| |
//========================
| |
// Draw
| |
//========================
| |
string TypeText(EZoneType type) {
| |
if(type == ZONE_EXTREME) return "EXT";
| |
if(type == ZONE_DECISIONAL) return "DEC";
| |
return "OB";
| |
}
| |
| |
string StateText(EZoneState state) {
| |
if(state == ZONE_FRESH) return "FRESH";
| |
if(state == ZONE_TOUCHED) return "TOUCHED";
| |
if(state == ZONE_MITIGATED) return "MITIGATED";
| |
return "INVALID";
| |
}
| |
| |
color ZoneColor(const SupplyDemandZone &z) {
| |
if(z.state == ZONE_INVALID)
| |
return clrSilver;
| |
| |
if(z.state == ZONE_MITIGATED)
| |
return clrDarkGray;
| |
| |
if(z.side == ZONE_DEMAND)
| |
return clrPaleGreen;
| |
| |
return clrTomato;
| |
}
| |
| |
string ZoneText(const SupplyDemandZone &z) {
| |
string txt = (z.side == ZONE_DEMAND ? "DEMAND " : "SUPPLY ");
| |
txt += TypeText(z.type);
| |
txt += " ";
| |
txt += StateText(z.state);
| |
| |
if(z.isInducement)
| |
txt += " IDM";
| |
| |
if(z.hasBos) txt += " BOS";
| |
if(z.hasMomentum) txt += " MOM";
| |
if(z.hasLiquidity) txt += " LIQ";
| |
if(z.hasImbalance) txt += " IMB";
| |
| |
return txt;
| |
}
| |
| |
void DrawZones() {
| |
int end = Bars() - 1;
| |
| |
for(int i = 0; i < ArraySize(zones); i++) {
| |
SupplyDemandZone z = zones[i];
| |
| |
if(ch4_hideInvalidZones && z.state == ZONE_INVALID)
| |
continue;
| |
| |
int toIndex = end;
| |
if(z.invalidateBar >= 0) toIndex = z.invalidateBar;
| |
| |
color fullColor = ZoneColor(z);
| |
string txt = ZoneText(z);
| |
| |
DrawZone(
| |
K_ZONE(z.id),
| |
z.leftBar,
| |
toIndex,
| |
z.fullTop,
| |
z.fullBottom,
| |
txt,
| |
fullColor,
| |
0.06,
| |
6,
| |
false
| |
);
| |
| |
DrawZone(
| |
K_ZONE_R(z.id),
| |
z.leftBar,
| |
toIndex,
| |
z.refinedTop,
| |
z.refinedBottom,
| |
"",
| |
(z.side == ZONE_DEMAND ? clrLime : clrRed),
| |
0.16,
| |
5,
| |
true
| |
);
| |
}
| |
}
| |
| |
public:
| |
C_Chapter4() {
| |
nextZoneId = 1;
| |
protectedHigh = -1;
| |
protectedLow = -1;
| |
ArrayResize(zones, 0);
| |
}
| |
| |
~C_Chapter4() {}
| |
| |
void SetExternalRange(int protectedHighIndex, int protectedLowIndex) {
| |
protectedHigh = protectedHighIndex;
| |
protectedLow = protectedLowIndex;
| |
}
| |
| |
int GetZonesCount() const {
| |
return ArraySize(zones);
| |
}
| |
| |
bool GetZone(int index, SupplyDemandZone &outZone) {
| |
if(index < 0 || index >= ArraySize(zones))
| |
return false;
| |
| |
outZone = zones[index];
| |
return true;
| |
}
| |
| |
int OnInit(string s, ENUM_TIMEFRAMES tf_, int lr = 3, int look = 1) {
| |
timeframe = tf_;
| |
symbol = s;
| |
debug.Init(0, "Chapter4", true);
| |
| |
if(swing.Init("Fractal", s, tf_, lr, look) == -1) {
| |
Print("Error creating Swing Indicator: ", GetLastError());
| |
return INIT_FAILED;
| |
}
| |
| |
if(fairValue.Init("Fair Value Gaps", s, tf_, lr, look) == -1) {
| |
Print("Error creating Fair Value indicator: ", GetLastError());
| |
return INIT_FAILED;
| |
}
| |
| |
return INIT_SUCCEEDED;
| |
}
| |
| |
void OnCalculate() {
| |
if(!OnNewBar())
| |
return;
| |
| |
UpdateBars(3);
| |
| |
ArrayResize(zones, 0);
| |
nextZoneId = 1;
| |
| |
DetectDemandZones();
| |
DetectSupplyZones();
| |
MarkInducementZones();
| |
UpdateZoneState();
| |
DrawZones();
| |
}
| |
};
| |
//+------------------------------------------------------------------+ |