ictCore/Experts/core/fvg.mqh
2026-05-17 22:39:25 +03:00

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;
}