136 lines
No EOL
4 KiB
MQL5
136 lines
No EOL
4 KiB
MQL5
extern MqlRates rates[];
|
|
|
|
|
|
class CFvg{
|
|
private:
|
|
//bool isFvgTapped;
|
|
struct fvg{
|
|
double upperBand;
|
|
double lowerBand;
|
|
double range;
|
|
int flag;
|
|
bool isFvgTapped;
|
|
};
|
|
//ICollection
|
|
|
|
fvg latestFvg;
|
|
|
|
|
|
public:
|
|
//getters
|
|
//bool getLatestFvgFlag();
|
|
double getLatestFvgLowerBand();
|
|
double getLatestFvgUpperBand();
|
|
double getLatestFvgRange();
|
|
bool getIsLatestFvgFresh();
|
|
int isFvg(int index=2); //return the flag of fvg formed or NULL
|
|
bool update();
|
|
bool isFvgTapped();
|
|
};
|
|
|
|
//bool CFvg::getLatestFvgFlag(void){
|
|
//
|
|
// return latestFvg.flag;
|
|
//}
|
|
|
|
double CFvg::getLatestFvgLowerBand(){
|
|
return latestFvg.lowerBand;
|
|
}
|
|
|
|
double CFvg::getLatestFvgUpperBand(void){
|
|
return latestFvg.upperBand;
|
|
}
|
|
|
|
double CFvg::getLatestFvgRange(){
|
|
return latestFvg.range;
|
|
}
|
|
|
|
bool CFvg::getIsLatestFvgFresh(void){
|
|
return latestFvg.isFvgTapped;
|
|
}
|
|
|
|
//int CFvg::isFvg(/*int &flag*/){
|
|
// if(rates[1].high<rates[3].low){ //there's a gap in candle 2
|
|
// //flag=-1; //bearish fvg found
|
|
// return -1;
|
|
// }
|
|
// else if(rates[1].low>rates[3].high){ //tail not meeting head
|
|
// //flag=1; //bullish fvg found
|
|
// return 1;
|
|
// }
|
|
//
|
|
//return NULL;}
|
|
|
|
int CFvg::isFvg(int index=2){
|
|
if(rates[index-1].high<rates[index+1].low){ //there's a gap in candle 2
|
|
//flag=-1; //bearish fvg found
|
|
return -1;
|
|
}
|
|
else if(rates[index-1].low>rates[index+1].high){ //tail not meeting head
|
|
//flag=1; //bullish fvg found
|
|
return 1;
|
|
}
|
|
|
|
return NULL;}
|
|
|
|
bool CFvg::update() {
|
|
if(isFvgTapped()==true){
|
|
latestFvg.isFvgTapped=true;
|
|
}
|
|
|
|
int flag = isFvg();
|
|
if (flag == NULL) return false; // no FVG in current 3-candle window
|
|
|
|
// ── Derive candidate FVG bands from the same candles isFvg() inspects ──
|
|
// Bearish FVG: impulse drove price down; gap sits between
|
|
// lowerBand = rates[1].high (top of right candle)
|
|
// upperBand = rates[3].low (bottom of left candle)
|
|
// Bullish FVG: impulse drove price up; gap sits between
|
|
// lowerBand = rates[3].high (top of left candle)
|
|
// upperBand = rates[1].low (bottom of right candle)
|
|
double upper, lower;
|
|
if (flag == -1) { // bearish
|
|
upper = rates[3].low;
|
|
lower = rates[1].high;
|
|
} else { // bullish
|
|
upper = rates[1].low;
|
|
lower = rates[3].high;
|
|
}
|
|
double newRange = upper - lower;
|
|
if (newRange <= 0) return false; // degenerate gap — skip
|
|
|
|
// ── Consecutive-FVG guard ────────────────────────────────────────────
|
|
// If the candidate has the same direction as the stored latestFvg AND
|
|
// their price bands overlap, this is the same gap that has simply
|
|
// extended beyond the standard 3-candle window — not a new FVG.
|
|
// Registering it again would double-count the same structural feature.
|
|
if (latestFvg.flag == flag && latestFvg.range > 0) {
|
|
if(isFvg(3)==true){
|
|
if(flag==1){
|
|
latestFvg.upperBand=upper;
|
|
latestFvg.range=latestFvg.upperBand-latestFvg.lowerBand;
|
|
}
|
|
else{ //prev bearish fvg
|
|
latestFvg.lowerBand=lower;
|
|
latestFvg.range=latestFvg.upperBand-latestFvg.lowerBand;
|
|
}
|
|
}
|
|
//if (bandsOverlap) return false; // same FVG extending — skip
|
|
}
|
|
|
|
// ── New distinct FVG — populate latestFvg ───────────────────────────
|
|
latestFvg.flag = flag;
|
|
latestFvg.upperBand = upper;
|
|
latestFvg.lowerBand = lower;
|
|
latestFvg.range = newRange;
|
|
latestFvg.isFvgTapped = false; // fresh, untouched gap
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CFvg::isFvgTapped(void){
|
|
bool isTapped=(rates[0].high>latestFvg.upperBand && rates[0].low<latestFvg.lowerBand)
|
|
|| (rates[0].high>latestFvg.lowerBand && rates[0].low<latestFvg.upperBand);
|
|
|
|
return isTapped;
|
|
} |