Init SimpleFVG stradegy
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| Config.mqh – Step 0: Inputs, Enums, Constants |
|
||||
//| Cho phép cấu hình symbol, timeframe, EMA, FVG, drawing |
|
||||
//+------------------------------------------------------------------+
|
||||
#ifndef __SIMPLE_FVG_CONFIG_MQH__
|
||||
#define __SIMPLE_FVG_CONFIG_MQH__
|
||||
|
||||
//--- Step 0: Asset & Timeframe ---
|
||||
input string InpSymbol = ""; // Symbol (empty = current chart)
|
||||
input ENUM_TIMEFRAMES InpTimeframe = PERIOD_H1; // Analysis Timeframe
|
||||
|
||||
//--- Step 1: Trend (EMA) ---
|
||||
input int InpEMAFastPeriod = 34; // EMA Fast Period
|
||||
input int InpEMASlowPeriod = 89; // EMA Slow Period
|
||||
|
||||
//--- Step 2: FVG Detection ---
|
||||
input int InpFVGLookbackBars = 100; // FVG: Lookback range (bars)
|
||||
input int InpFVGMaxAgeBars = 50; // FVG: Max age before expiry (bars)
|
||||
input double InpFVGMinBodyPct = 50.0; // FVG: Min body % of impulse candle
|
||||
input double InpFVGMinSizePoints = 0; // FVG: Min gap size (points, 0=any)
|
||||
|
||||
//--- Step 4: Drawing ---
|
||||
input color InpColorBullFVG = C'30,80,140'; // Color: Bullish FVG zone
|
||||
input color InpColorBearFVG = C'140,30,20'; // Color: Bearish FVG zone
|
||||
input color InpColorMitigatedFVG = C'60,60,60'; // Color: Mitigated FVG zone
|
||||
input color InpColorEMAFast = clrDodgerBlue; // Color: EMA Fast line
|
||||
input color InpColorEMASlow = clrOrangeRed; // Color: EMA Slow line
|
||||
input bool InpShowPanel = true; // Draw: Show info panel
|
||||
input bool InpShowFVGLabels = true; // Draw: Show FVG labels
|
||||
|
||||
//--- Debug ---
|
||||
input bool InpDebugLog = true; // Debug: Enable logging
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Enums |
|
||||
//+------------------------------------------------------------------+
|
||||
enum ENUM_TREND_DIRECTION
|
||||
{
|
||||
TREND_BULLISH = 1,
|
||||
TREND_BEARISH = -1,
|
||||
TREND_NEUTRAL = 0
|
||||
};
|
||||
|
||||
enum ENUM_FVG_TYPE
|
||||
{
|
||||
FVG_BULLISH = 1,
|
||||
FVG_BEARISH = -1
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Constants |
|
||||
//+------------------------------------------------------------------+
|
||||
const string EA_PREFIX = "SFVG_";
|
||||
const int MAX_FVG_SLOTS = 50;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Resolved symbol (cached) |
|
||||
//+------------------------------------------------------------------+
|
||||
string g_ResolvedSymbol = "";
|
||||
|
||||
string GetTradeSymbol()
|
||||
{
|
||||
if(g_ResolvedSymbol != "")
|
||||
return g_ResolvedSymbol;
|
||||
|
||||
g_ResolvedSymbol = (InpSymbol == "" || InpSymbol == "current")
|
||||
? _Symbol
|
||||
: InpSymbol;
|
||||
return g_ResolvedSymbol;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| New-bar detector |
|
||||
//+------------------------------------------------------------------+
|
||||
datetime g_PreviousBarTime = 0;
|
||||
|
||||
bool IsNewBar()
|
||||
{
|
||||
datetime currentBarTime = iTime(GetTradeSymbol(), InpTimeframe, 0);
|
||||
if(currentBarTime == g_PreviousBarTime)
|
||||
return false;
|
||||
g_PreviousBarTime = currentBarTime;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,205 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| Drawing.mqh – Step 4: Visualize FVG zones, EMA info, trend panel |
|
||||
//+------------------------------------------------------------------+
|
||||
#ifndef __SIMPLE_FVG_DRAWING_MQH__
|
||||
#define __SIMPLE_FVG_DRAWING_MQH__
|
||||
|
||||
#include "Config.mqh"
|
||||
#include "Trend.mqh"
|
||||
#include "FVG.mqh"
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Object-creation helpers |
|
||||
//+------------------------------------------------------------------+
|
||||
void DrawRectangle(string name,
|
||||
datetime timeStart, double priceTop,
|
||||
datetime timeEnd, double priceBot,
|
||||
color clr, bool filled = true)
|
||||
{
|
||||
if(ObjectFind(0, name) < 0)
|
||||
ObjectCreate(0, name, OBJ_RECTANGLE, 0, timeStart, priceTop, timeEnd, priceBot);
|
||||
|
||||
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
|
||||
ObjectSetInteger(0, name, OBJPROP_FILL, filled);
|
||||
ObjectSetInteger(0, name, OBJPROP_BACK, true);
|
||||
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
|
||||
ObjectMove(0, name, 0, timeStart, priceTop);
|
||||
ObjectMove(0, name, 1, timeEnd, priceBot);
|
||||
}
|
||||
|
||||
void DrawTrendLine(string name,
|
||||
datetime t1, double p1,
|
||||
datetime t2, double p2,
|
||||
color clr, int style, int width)
|
||||
{
|
||||
if(ObjectFind(0, name) < 0)
|
||||
ObjectCreate(0, name, OBJ_TREND, 0, t1, p1, t2, p2);
|
||||
|
||||
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
|
||||
ObjectSetInteger(0, name, OBJPROP_STYLE, style);
|
||||
ObjectSetInteger(0, name, OBJPROP_WIDTH, width);
|
||||
ObjectSetInteger(0, name, OBJPROP_RAY_RIGHT, false);
|
||||
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
|
||||
ObjectMove(0, name, 0, t1, p1);
|
||||
ObjectMove(0, name, 1, t2, p2);
|
||||
}
|
||||
|
||||
void DrawLabel(string name, int xDist, int yDist,
|
||||
string text, color clr, int fontSize)
|
||||
{
|
||||
if(ObjectFind(0, name) < 0)
|
||||
ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
|
||||
|
||||
ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
|
||||
ObjectSetInteger(0, name, OBJPROP_XDISTANCE, xDist);
|
||||
ObjectSetInteger(0, name, OBJPROP_YDISTANCE, yDist);
|
||||
ObjectSetInteger(0, name, OBJPROP_FONTSIZE, fontSize);
|
||||
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
|
||||
ObjectSetString (0, name, OBJPROP_FONT, "Consolas");
|
||||
ObjectSetString (0, name, OBJPROP_TEXT, text);
|
||||
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
|
||||
}
|
||||
|
||||
void DrawTextOnChart(string name, datetime t, double price,
|
||||
string text, color clr, int fontSize, int anchor)
|
||||
{
|
||||
if(ObjectFind(0, name) < 0)
|
||||
ObjectCreate(0, name, OBJ_TEXT, 0, t, price);
|
||||
|
||||
ObjectMove(0, name, 0, t, price);
|
||||
ObjectSetString (0, name, OBJPROP_TEXT, text);
|
||||
ObjectSetString (0, name, OBJPROP_FONT, "Arial");
|
||||
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
|
||||
ObjectSetInteger(0, name, OBJPROP_FONTSIZE, fontSize);
|
||||
ObjectSetInteger(0, name, OBJPROP_ANCHOR, anchor);
|
||||
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Draw all active FVG zones as rectangles on the chart |
|
||||
//+------------------------------------------------------------------+
|
||||
void DrawFVGZones()
|
||||
{
|
||||
ObjectsDeleteAll(0, EA_PREFIX + "FVG_");
|
||||
|
||||
string symbol = GetTradeSymbol();
|
||||
datetime currentBar = iTime(symbol, InpTimeframe, 0);
|
||||
|
||||
for(int i = 0; i < g_FVGCount; i++)
|
||||
{
|
||||
if(!g_FVGZones[i].isActive)
|
||||
continue;
|
||||
|
||||
string zoneId = IntegerToString(i);
|
||||
|
||||
//--- Zone color depends on type and mitigation status ---
|
||||
color zoneColor;
|
||||
if(g_FVGZones[i].isMitigated)
|
||||
zoneColor = InpColorMitigatedFVG;
|
||||
else if(g_FVGZones[i].type == FVG_BULLISH)
|
||||
zoneColor = InpColorBullFVG;
|
||||
else
|
||||
zoneColor = InpColorBearFVG;
|
||||
|
||||
//--- Draw filled rectangle for the FVG zone ---
|
||||
DrawRectangle(
|
||||
EA_PREFIX + "FVG_ZONE_" + zoneId,
|
||||
g_FVGZones[i].createdTime, g_FVGZones[i].upperEdge,
|
||||
currentBar, g_FVGZones[i].lowerEdge,
|
||||
zoneColor, true
|
||||
);
|
||||
|
||||
//--- Draw mid-line (50% of FVG) ---
|
||||
double midPrice = (g_FVGZones[i].upperEdge + g_FVGZones[i].lowerEdge) / 2.0;
|
||||
color midColor = g_FVGZones[i].isMitigated ? clrDimGray : clrGold;
|
||||
|
||||
DrawTrendLine(
|
||||
EA_PREFIX + "FVG_MID_" + zoneId,
|
||||
g_FVGZones[i].createdTime, midPrice,
|
||||
currentBar, midPrice,
|
||||
midColor, STYLE_DOT, 1
|
||||
);
|
||||
|
||||
//--- Draw text label ---
|
||||
if(InpShowFVGLabels)
|
||||
{
|
||||
string typeStr = (g_FVGZones[i].type == FVG_BULLISH) ? "BULL" : "BEAR";
|
||||
string arrow = (g_FVGZones[i].type == FVG_BULLISH) ? "▲" : "▼";
|
||||
string mitigStr = g_FVGZones[i].isMitigated ? " [MITIGATED]" : "";
|
||||
|
||||
color labelColor;
|
||||
if(g_FVGZones[i].isMitigated)
|
||||
labelColor = clrDimGray;
|
||||
else if(g_FVGZones[i].type == FVG_BULLISH)
|
||||
labelColor = clrDeepSkyBlue;
|
||||
else
|
||||
labelColor = clrOrangeRed;
|
||||
|
||||
DrawTextOnChart(
|
||||
EA_PREFIX + "FVG_LBL_" + zoneId,
|
||||
g_FVGZones[i].createdTime,
|
||||
g_FVGZones[i].upperEdge,
|
||||
StringFormat("%s %s [%.2f–%.2f]%s",
|
||||
arrow, typeStr,
|
||||
g_FVGZones[i].lowerEdge, g_FVGZones[i].upperEdge,
|
||||
mitigStr),
|
||||
labelColor, 7, ANCHOR_LEFT_LOWER
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Draw info panel (top-left corner) |
|
||||
//+------------------------------------------------------------------+
|
||||
void DrawInfoPanel()
|
||||
{
|
||||
if(!InpShowPanel) return;
|
||||
|
||||
int bullCount = CountActiveFVGs(FVG_BULLISH);
|
||||
int bearCount = CountActiveFVGs(FVG_BEARISH);
|
||||
int mitigCount = CountMitigatedFVGs();
|
||||
string trendText = TrendToString(g_CurrentTrend);
|
||||
color trendColor = TrendToColor(g_CurrentTrend);
|
||||
|
||||
//--- Row 1: EA header ---
|
||||
DrawLabel(EA_PREFIX + "PNL_HDR", 12, 22,
|
||||
StringFormat("SimpleFVG | %s %s",
|
||||
GetTradeSymbol(), EnumToString(InpTimeframe)),
|
||||
clrSilver, 10);
|
||||
|
||||
//--- Row 2: Trend status ---
|
||||
DrawLabel(EA_PREFIX + "PNL_TREND", 12, 44,
|
||||
StringFormat("Trend: %s | EMA%d = %.2f | EMA%d = %.2f",
|
||||
trendText,
|
||||
InpEMAFastPeriod, g_EMAFastValue,
|
||||
InpEMASlowPeriod, g_EMASlowValue),
|
||||
trendColor, 9);
|
||||
|
||||
//--- Row 3: FVG counts ---
|
||||
DrawLabel(EA_PREFIX + "PNL_FVG", 12, 66,
|
||||
StringFormat("FVG Active: %d Bull + %d Bear | Mitigated: %d",
|
||||
bullCount, bearCount, mitigCount),
|
||||
clrWheat, 9);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Master draw: call all draw functions |
|
||||
//+------------------------------------------------------------------+
|
||||
void DrawAll()
|
||||
{
|
||||
DrawFVGZones();
|
||||
DrawInfoPanel();
|
||||
ChartRedraw(0);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Clean up all drawn objects |
|
||||
//+------------------------------------------------------------------+
|
||||
void DrawCleanup()
|
||||
{
|
||||
ObjectsDeleteAll(0, EA_PREFIX);
|
||||
ChartRedraw(0);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,277 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| FVG.mqh – Step 2: Fair Value Gap detection & management |
|
||||
//| |
|
||||
//| 3-candle pattern: |
|
||||
//| CandleA (oldest) ── CandleB (impulse) ── CandleC (newest) |
|
||||
//| |
|
||||
//| Bullish FVG: CandleA.High < CandleC.Low → gap UP |
|
||||
//| zone = [CandleA.High .. CandleC.Low] |
|
||||
//| CandleB must be bullish with strong body |
|
||||
//| |
|
||||
//| Bearish FVG: CandleA.Low > CandleC.High → gap DOWN |
|
||||
//| zone = [CandleC.High .. CandleA.Low] |
|
||||
//| CandleB must be bearish with strong body |
|
||||
//+------------------------------------------------------------------+
|
||||
#ifndef __SIMPLE_FVG_FVG_MQH__
|
||||
#define __SIMPLE_FVG_FVG_MQH__
|
||||
|
||||
#include "Config.mqh"
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| FVG data structure |
|
||||
//+------------------------------------------------------------------+
|
||||
struct FVGZone
|
||||
{
|
||||
bool isActive;
|
||||
ENUM_FVG_TYPE type;
|
||||
double upperEdge;
|
||||
double lowerEdge;
|
||||
datetime createdTime;
|
||||
int ageInBars;
|
||||
bool isMitigated;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Module state |
|
||||
//+------------------------------------------------------------------+
|
||||
FVGZone g_FVGZones[];
|
||||
int g_FVGCount = 0;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Init: allocate array |
|
||||
//+------------------------------------------------------------------+
|
||||
void FVGInit()
|
||||
{
|
||||
ArrayResize(g_FVGZones, MAX_FVG_SLOTS);
|
||||
for(int i = 0; i < MAX_FVG_SLOTS; i++)
|
||||
ZeroMemory(g_FVGZones[i]);
|
||||
g_FVGCount = 0;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if impulse candle has strong body (body/range >= threshold) |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsImpulseCandleStrong(string symbol, ENUM_TIMEFRAMES tf, int shift)
|
||||
{
|
||||
double high = iHigh(symbol, tf, shift);
|
||||
double low = iLow(symbol, tf, shift);
|
||||
double open = iOpen(symbol, tf, shift);
|
||||
double close = iClose(symbol, tf, shift);
|
||||
|
||||
double totalRange = high - low;
|
||||
if(totalRange < _Point)
|
||||
return false;
|
||||
|
||||
double bodySize = MathAbs(close - open);
|
||||
double bodyRatio = bodySize / totalRange * 100.0;
|
||||
|
||||
return (bodyRatio >= InpFVGMinBodyPct);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if this FVG timestamp already exists in our array |
|
||||
//+------------------------------------------------------------------+
|
||||
bool FVGAlreadyTracked(datetime fvgTime, ENUM_FVG_TYPE fvgType)
|
||||
{
|
||||
for(int i = 0; i < g_FVGCount; i++)
|
||||
{
|
||||
if(g_FVGZones[i].isActive
|
||||
&& g_FVGZones[i].createdTime == fvgTime
|
||||
&& g_FVGZones[i].type == fvgType)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Add a new FVG zone to the array |
|
||||
//+------------------------------------------------------------------+
|
||||
bool AddFVGZone(ENUM_FVG_TYPE type, double upper, double lower, datetime time)
|
||||
{
|
||||
if(g_FVGCount >= MAX_FVG_SLOTS)
|
||||
return false;
|
||||
|
||||
if(InpFVGMinSizePoints > 0)
|
||||
{
|
||||
double gapSizePoints = (upper - lower) / _Point;
|
||||
if(gapSizePoints < InpFVGMinSizePoints)
|
||||
return false;
|
||||
}
|
||||
|
||||
ZeroMemory(g_FVGZones[g_FVGCount]);
|
||||
g_FVGZones[g_FVGCount].isActive = true;
|
||||
g_FVGZones[g_FVGCount].type = type;
|
||||
g_FVGZones[g_FVGCount].upperEdge = upper;
|
||||
g_FVGZones[g_FVGCount].lowerEdge = lower;
|
||||
g_FVGZones[g_FVGCount].createdTime = time;
|
||||
g_FVGZones[g_FVGCount].ageInBars = 0;
|
||||
g_FVGZones[g_FVGCount].isMitigated = false;
|
||||
g_FVGCount++;
|
||||
|
||||
if(InpDebugLog)
|
||||
PrintFormat("[FVG] NEW %s [%.5f – %.5f] at %s",
|
||||
(type == FVG_BULLISH) ? "BULL" : "BEAR",
|
||||
lower, upper, TimeToString(time));
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Scan for new FVG patterns across the lookback range |
|
||||
//| Finds ALL FVGs regardless of current trend |
|
||||
//+------------------------------------------------------------------+
|
||||
void ScanForNewFVGs()
|
||||
{
|
||||
string symbol = GetTradeSymbol();
|
||||
int totalBars = Bars(symbol, InpTimeframe);
|
||||
int maxShift = MathMin(InpFVGLookbackBars, totalBars - 3);
|
||||
|
||||
for(int shift = 1; shift <= maxShift; shift++)
|
||||
{
|
||||
int shiftA = shift + 2; // oldest candle
|
||||
int shiftB = shift + 1; // impulse (middle) candle
|
||||
int shiftC = shift; // newest candle
|
||||
|
||||
double candleA_High = iHigh(symbol, InpTimeframe, shiftA);
|
||||
double candleA_Low = iLow (symbol, InpTimeframe, shiftA);
|
||||
|
||||
double candleB_Open = iOpen (symbol, InpTimeframe, shiftB);
|
||||
double candleB_Close = iClose(symbol, InpTimeframe, shiftB);
|
||||
|
||||
double candleC_High = iHigh(symbol, InpTimeframe, shiftC);
|
||||
double candleC_Low = iLow (symbol, InpTimeframe, shiftC);
|
||||
|
||||
datetime fvgTime = iTime(symbol, InpTimeframe, shiftC);
|
||||
|
||||
//--- Bullish FVG: gap between A.High and C.Low ---
|
||||
if(candleA_High < candleC_Low
|
||||
&& candleB_Close > candleB_Open
|
||||
&& IsImpulseCandleStrong(symbol, InpTimeframe, shiftB))
|
||||
{
|
||||
if(!FVGAlreadyTracked(fvgTime, FVG_BULLISH))
|
||||
AddFVGZone(FVG_BULLISH, candleC_Low, candleA_High, fvgTime);
|
||||
}
|
||||
|
||||
//--- Bearish FVG: gap between C.High and A.Low ---
|
||||
if(candleA_Low > candleC_High
|
||||
&& candleB_Close < candleB_Open
|
||||
&& IsImpulseCandleStrong(symbol, InpTimeframe, shiftB))
|
||||
{
|
||||
if(!FVGAlreadyTracked(fvgTime, FVG_BEARISH))
|
||||
AddFVGZone(FVG_BEARISH, candleA_Low, candleC_High, fvgTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if price has mitigated (filled through) any active FVG |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckMitigationStatus()
|
||||
{
|
||||
string symbol = GetTradeSymbol();
|
||||
|
||||
for(int i = 0; i < g_FVGCount; i++)
|
||||
{
|
||||
if(!g_FVGZones[i].isActive || g_FVGZones[i].isMitigated)
|
||||
continue;
|
||||
|
||||
double lastLow = iLow (symbol, InpTimeframe, 1);
|
||||
double lastHigh = iHigh(symbol, InpTimeframe, 1);
|
||||
|
||||
// Bullish FVG mitigated: price drops through lower edge
|
||||
if(g_FVGZones[i].type == FVG_BULLISH && lastLow <= g_FVGZones[i].lowerEdge)
|
||||
{
|
||||
g_FVGZones[i].isMitigated = true;
|
||||
if(InpDebugLog)
|
||||
PrintFormat("[FVG] MITIGATED BULL [%.5f – %.5f]",
|
||||
g_FVGZones[i].lowerEdge, g_FVGZones[i].upperEdge);
|
||||
}
|
||||
|
||||
// Bearish FVG mitigated: price rises through upper edge
|
||||
if(g_FVGZones[i].type == FVG_BEARISH && lastHigh >= g_FVGZones[i].upperEdge)
|
||||
{
|
||||
g_FVGZones[i].isMitigated = true;
|
||||
if(InpDebugLog)
|
||||
PrintFormat("[FVG] MITIGATED BEAR [%.5f – %.5f]",
|
||||
g_FVGZones[i].lowerEdge, g_FVGZones[i].upperEdge);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Age out old FVGs beyond max age |
|
||||
//+------------------------------------------------------------------+
|
||||
void ExpireOldFVGs()
|
||||
{
|
||||
for(int i = 0; i < g_FVGCount; i++)
|
||||
{
|
||||
if(!g_FVGZones[i].isActive) continue;
|
||||
|
||||
g_FVGZones[i].ageInBars++;
|
||||
|
||||
if(g_FVGZones[i].ageInBars > InpFVGMaxAgeBars)
|
||||
{
|
||||
g_FVGZones[i].isActive = false;
|
||||
if(InpDebugLog)
|
||||
PrintFormat("[FVG] EXPIRED %s [%.5f – %.5f] age=%d bars",
|
||||
(g_FVGZones[i].type == FVG_BULLISH) ? "BULL" : "BEAR",
|
||||
g_FVGZones[i].lowerEdge, g_FVGZones[i].upperEdge,
|
||||
g_FVGZones[i].ageInBars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Remove inactive FVGs from array to free slots |
|
||||
//+------------------------------------------------------------------+
|
||||
void CompactFVGArray()
|
||||
{
|
||||
int writeIndex = 0;
|
||||
for(int i = 0; i < g_FVGCount; i++)
|
||||
{
|
||||
if(g_FVGZones[i].isActive)
|
||||
{
|
||||
if(writeIndex != i)
|
||||
g_FVGZones[writeIndex] = g_FVGZones[i];
|
||||
writeIndex++;
|
||||
}
|
||||
}
|
||||
g_FVGCount = writeIndex;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Main update: mitigation → expiry → scan new → compact |
|
||||
//+------------------------------------------------------------------+
|
||||
void FVGUpdate()
|
||||
{
|
||||
CheckMitigationStatus();
|
||||
ExpireOldFVGs();
|
||||
ScanForNewFVGs();
|
||||
CompactFVGArray();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Count helpers for panel display |
|
||||
//+------------------------------------------------------------------+
|
||||
int CountActiveFVGs(ENUM_FVG_TYPE type)
|
||||
{
|
||||
int count = 0;
|
||||
for(int i = 0; i < g_FVGCount; i++)
|
||||
{
|
||||
if(g_FVGZones[i].isActive && g_FVGZones[i].type == type)
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int CountMitigatedFVGs()
|
||||
{
|
||||
int count = 0;
|
||||
for(int i = 0; i < g_FVGCount; i++)
|
||||
{
|
||||
if(g_FVGZones[i].isActive && g_FVGZones[i].isMitigated)
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,147 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| Trend.mqh – Step 1: Trend detection via EMA34 & EMA89 |
|
||||
//| |
|
||||
//| Logic: |
|
||||
//| BULLISH: EMA_fast > EMA_slow AND close > EMA_fast |
|
||||
//| BEARISH: EMA_fast < EMA_slow AND close < EMA_fast |
|
||||
//| NEUTRAL: otherwise (EMA crossing or price between EMAs) |
|
||||
//+------------------------------------------------------------------+
|
||||
#ifndef __SIMPLE_FVG_TREND_MQH__
|
||||
#define __SIMPLE_FVG_TREND_MQH__
|
||||
|
||||
#include "Config.mqh"
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Module state |
|
||||
//+------------------------------------------------------------------+
|
||||
int g_HandleEMAFast = INVALID_HANDLE;
|
||||
int g_HandleEMASlow = INVALID_HANDLE;
|
||||
string g_NameEMAFast = "";
|
||||
string g_NameEMASlow = "";
|
||||
|
||||
double g_EMAFastValue = 0.0;
|
||||
double g_EMASlowValue = 0.0;
|
||||
ENUM_TREND_DIRECTION g_CurrentTrend = TREND_NEUTRAL;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Init: create EMA indicator handles and add to chart |
|
||||
//+------------------------------------------------------------------+
|
||||
bool TrendInit()
|
||||
{
|
||||
string symbol = GetTradeSymbol();
|
||||
|
||||
g_HandleEMAFast = iMA(symbol, InpTimeframe, InpEMAFastPeriod, 0, MODE_EMA, PRICE_CLOSE);
|
||||
if(g_HandleEMAFast == INVALID_HANDLE)
|
||||
{
|
||||
PrintFormat("[Trend] FAILED to create EMA%d handle", InpEMAFastPeriod);
|
||||
return false;
|
||||
}
|
||||
|
||||
g_HandleEMASlow = iMA(symbol, InpTimeframe, InpEMASlowPeriod, 0, MODE_EMA, PRICE_CLOSE);
|
||||
if(g_HandleEMASlow == INVALID_HANDLE)
|
||||
{
|
||||
PrintFormat("[Trend] FAILED to create EMA%d handle", InpEMASlowPeriod);
|
||||
return false;
|
||||
}
|
||||
|
||||
ChartIndicatorAdd(0, 0, g_HandleEMAFast);
|
||||
int countAfterFast = ChartIndicatorsTotal(0, 0);
|
||||
if(countAfterFast > 0)
|
||||
g_NameEMAFast = ChartIndicatorName(0, 0, countAfterFast - 1);
|
||||
|
||||
ChartIndicatorAdd(0, 0, g_HandleEMASlow);
|
||||
int countAfterSlow = ChartIndicatorsTotal(0, 0);
|
||||
if(countAfterSlow > 0)
|
||||
g_NameEMASlow = ChartIndicatorName(0, 0, countAfterSlow - 1);
|
||||
|
||||
if(InpDebugLog)
|
||||
PrintFormat("[Trend] OK – EMA%d + EMA%d on %s %s",
|
||||
InpEMAFastPeriod, InpEMASlowPeriod,
|
||||
symbol, EnumToString(InpTimeframe));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Deinit: release handles and remove from chart |
|
||||
//+------------------------------------------------------------------+
|
||||
void TrendDeinit()
|
||||
{
|
||||
if(g_NameEMAFast != "")
|
||||
ChartIndicatorDelete(0, 0, g_NameEMAFast);
|
||||
if(g_NameEMASlow != "")
|
||||
ChartIndicatorDelete(0, 0, g_NameEMASlow);
|
||||
|
||||
if(g_HandleEMAFast != INVALID_HANDLE)
|
||||
{
|
||||
IndicatorRelease(g_HandleEMAFast);
|
||||
g_HandleEMAFast = INVALID_HANDLE;
|
||||
}
|
||||
if(g_HandleEMASlow != INVALID_HANDLE)
|
||||
{
|
||||
IndicatorRelease(g_HandleEMASlow);
|
||||
g_HandleEMASlow = INVALID_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Read one EMA value at a given bar shift |
|
||||
//+------------------------------------------------------------------+
|
||||
double ReadEMA(int handle, int shift)
|
||||
{
|
||||
double buffer[1];
|
||||
if(handle == INVALID_HANDLE) return 0.0;
|
||||
if(CopyBuffer(handle, 0, shift, 1, buffer) < 1) return 0.0;
|
||||
return buffer[0];
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Update trend direction (call once per new bar) |
|
||||
//+------------------------------------------------------------------+
|
||||
ENUM_TREND_DIRECTION TrendUpdate()
|
||||
{
|
||||
g_EMAFastValue = ReadEMA(g_HandleEMAFast, 1);
|
||||
g_EMASlowValue = ReadEMA(g_HandleEMASlow, 1);
|
||||
|
||||
if(g_EMAFastValue == 0.0 || g_EMASlowValue == 0.0)
|
||||
{
|
||||
g_CurrentTrend = TREND_NEUTRAL;
|
||||
return g_CurrentTrend;
|
||||
}
|
||||
|
||||
double closePrice = iClose(GetTradeSymbol(), InpTimeframe, 1);
|
||||
|
||||
if(g_EMAFastValue > g_EMASlowValue && closePrice > g_EMAFastValue)
|
||||
g_CurrentTrend = TREND_BULLISH;
|
||||
else if(g_EMAFastValue < g_EMASlowValue && closePrice < g_EMAFastValue)
|
||||
g_CurrentTrend = TREND_BEARISH;
|
||||
else
|
||||
g_CurrentTrend = TREND_NEUTRAL;
|
||||
|
||||
return g_CurrentTrend;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Utility: convert trend enum to readable string |
|
||||
//+------------------------------------------------------------------+
|
||||
string TrendToString(ENUM_TREND_DIRECTION trend)
|
||||
{
|
||||
switch(trend)
|
||||
{
|
||||
case TREND_BULLISH: return "BULLISH";
|
||||
case TREND_BEARISH: return "BEARISH";
|
||||
default: return "NEUTRAL";
|
||||
}
|
||||
}
|
||||
|
||||
color TrendToColor(ENUM_TREND_DIRECTION trend)
|
||||
{
|
||||
switch(trend)
|
||||
{
|
||||
case TREND_BULLISH: return clrLime;
|
||||
case TREND_BEARISH: return clrTomato;
|
||||
default: return clrGray;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user