| //+------------------------------------------------------------------+
|
| //| Fvg.mq5 |
|
| //| Copyright 2024, rpanchyk |
|
| //| https://github.com/rpanchyk |
|
| //+------------------------------------------------------------------+
|
| #property copyright "Copyright 2024, rpanchyk"
|
| #property link "https://github.com/rpanchyk"
|
| #property version "1.03"
|
| #property description "Expert Advisor shows fair value gaps"
|
|
|
|
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| #include "..\\Utils\\MetricSaver.mqh"
|
|
|
|
|
| // https://github.com/rpanchyk/mt5-fvg-ind/blob/main/MQL5/Indicators/Fvg.mq5 (convertido a EA)
|
|
|
| // types
|
| enum ENUM_BORDER_STYLE
|
| {
|
| BORDER_STYLE_SOLID = STYLE_SOLID, // Solid
|
| BORDER_STYLE_DASH = STYLE_DASH // Dash
|
| };
|
|
|
| // config
|
| input group "Section :: Main";
|
| input bool InpContinueToMitigation = true; // Continue to mitigation
|
|
|
| input group "Section :: Style";
|
| input color InpDownTrendColor = clrLightPink; // Down trend color
|
| input color InpUpTrendColor = clrLightGreen; // Up trend color
|
| input bool InpFill = true; // Fill solid (true) or transparent (false)
|
| input ENUM_BORDER_STYLE InpBoderStyle = BORDER_STYLE_SOLID; // Border line style
|
| input int InpBorderWidth = 2; // Border line width
|
|
|
| input group "Section :: Dev";
|
| input bool InpDebugEnabled = false; // Enable debug (verbose logging)
|
|
|
| // constants
|
| const string OBJECT_PREFIX = "FVG";
|
| const string OBJECT_PREFIX_CONTINUATED = OBJECT_PREFIX + "CNT";
|
| const string OBJECT_SEP = "#";
|
|
|
| // global variables
|
| datetime lastBarTime = 0;
|
|
|
| //+------------------------------------------------------------------+
|
| //| Expert initialization function |
|
| //+------------------------------------------------------------------+
|
| int OnInit()
|
| {
|
| CMetricsSave::Start(FILE_CODE1);
|
|
|
|
|
| if(InpDebugEnabled)
|
| {
|
| Print("Fvg expert advisor initialization started");
|
| }
|
|
|
| lastBarTime = 0;
|
|
|
| if(InpDebugEnabled)
|
| {
|
| Print("Fvg expert advisor initialization finished");
|
| }
|
| return(INIT_SUCCEEDED);
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| Expert deinitialization function |
|
| //+------------------------------------------------------------------+
|
| void OnDeinit(const int reason)
|
| {
|
| if(InpDebugEnabled)
|
| {
|
| Print("Fvg expert advisor deinitialization started");
|
| }
|
|
|
| if(!MQLInfoInteger(MQL_TESTER))
|
| {
|
| ObjectsDeleteAll(0, OBJECT_PREFIX);
|
| }
|
|
|
| if(InpDebugEnabled)
|
| {
|
| Print("Fvg expert advisor deinitialization finished");
|
| }
|
| CMetricsSave::Destroy();
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| Expert tick function |
|
| //+------------------------------------------------------------------+
|
| void OnTick()
|
| {
|
| datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);
|
| if(lastBarTime == currentBarTime)
|
| {
|
| return;
|
| }
|
| lastBarTime = currentBarTime;
|
|
|
| MqlRates rates[];
|
| ArraySetAsSeries(rates, true);
|
| int copied = CopyRates(_Symbol, PERIOD_CURRENT, 0, 100, rates);
|
| if(copied < 3)
|
| {
|
| return;
|
| }
|
|
|
| if(InpContinueToMitigation)
|
| {
|
| int total = ObjectsTotal(0, 0, OBJ_RECTANGLE);
|
| for(int i = 0; i < total; i++)
|
| {
|
| string objName = ObjectName(0, i, 0, OBJ_RECTANGLE);
|
| if(StringFind(objName, OBJECT_PREFIX_CONTINUATED) == 0)
|
| {
|
| string result[];
|
| StringSplit(objName, StringGetCharacter(OBJECT_SEP, 0), result);
|
|
|
| datetime leftTime = StringToTime(result[1]);
|
| double leftPrice = StringToDouble(result[2]);
|
| datetime rightTime = rates[0].time;
|
| double rightPrice = StringToDouble(result[4]);
|
|
|
| if(rightPrice < rates[1].high && rightPrice > rates[1].low)
|
| {
|
| rightTime = rates[1].time;
|
| if(ObjectDelete(0, objName))
|
| {
|
| DrawBox(leftTime, leftPrice, rightTime, rightPrice, false);
|
| }
|
| }
|
| else
|
| {
|
| ObjectMove(0, objName, 1, rightTime, rightPrice);
|
| if(InpDebugEnabled)
|
| {
|
| PrintFormat("Expand box %s", objName);
|
| }
|
| }
|
| }
|
| }
|
| }
|
|
|
| int limit = copied - 3;
|
| if(InpDebugEnabled)
|
| {
|
| PrintFormat("Copied: %i, Limit: %i", copied, limit);
|
| }
|
|
|
| for(int i = 1; i < limit; i++)
|
| {
|
| double rightHighPrice = rates[i].high;
|
| double rightLowPrice = rates[i].low;
|
| double midHighPrice = rates[i + 1].high;
|
| double midLowPrice = rates[i + 1].low;
|
| double leftHighPrice = rates[i + 2].high;
|
| double leftLowPrice = rates[i + 2].low;
|
|
|
| datetime rightTime = rates[i].time;
|
| datetime leftTime = rates[i + 2].time;
|
|
|
| // Up trend
|
| bool upLeft = midLowPrice <= leftHighPrice && midLowPrice > leftLowPrice;
|
| bool upRight = midHighPrice >= rightLowPrice && midHighPrice < rightHighPrice;
|
| bool upGap = leftHighPrice < rightLowPrice;
|
| if(upLeft && upRight && upGap)
|
| {
|
| if(InpContinueToMitigation)
|
| {
|
| rightTime = rates[0].time;
|
| for(int j = i - 1; j > 0; j--) // Search mitigation bar
|
| {
|
| if((rightLowPrice < rates[j].high && rightLowPrice >= rates[j].low) || (leftHighPrice > rates[j].low && leftHighPrice <= rates[j].high))
|
| {
|
| rightTime = rates[j].time;
|
| break;
|
| }
|
| }
|
| }
|
|
|
| DrawBox(leftTime, leftHighPrice, rightTime, rightLowPrice, InpContinueToMitigation && rightTime == rates[0].time);
|
|
|
| continue;
|
| }
|
|
|
| // Down trend
|
| bool downLeft = midHighPrice >= leftLowPrice && midHighPrice < leftHighPrice;
|
| bool downRight = midLowPrice <= rightHighPrice && midLowPrice > rightLowPrice;
|
| bool downGap = leftLowPrice > rightHighPrice;
|
| if(downLeft && downRight && downGap)
|
| {
|
| if(InpContinueToMitigation)
|
| {
|
| rightTime = rates[0].time;
|
| for(int j = i - 1; j > 0; j--) // Search mitigation bar
|
| {
|
| if((rightHighPrice <= rates[j].high && rightHighPrice > rates[j].low) || (leftLowPrice >= rates[j].low && leftLowPrice < rates[j].high))
|
| {
|
| rightTime = rates[j].time;
|
| break;
|
| }
|
| }
|
| }
|
|
|
| DrawBox(leftTime, leftLowPrice, rightTime, rightHighPrice, InpContinueToMitigation && rightTime == rates[0].time);
|
|
|
| continue;
|
| }
|
| }
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| Draws FVG box |
|
| //+------------------------------------------------------------------+
|
| void DrawBox(datetime leftDt, double leftPrice, datetime rightDt, double rightPrice, bool continuated)
|
| {
|
| string objName = (continuated ? OBJECT_PREFIX_CONTINUATED : OBJECT_PREFIX)
|
| + OBJECT_SEP
|
| + TimeToString(leftDt)
|
| + OBJECT_SEP
|
| + DoubleToString(leftPrice)
|
| + OBJECT_SEP
|
| + TimeToString(rightDt)
|
| + OBJECT_SEP
|
| + DoubleToString(rightPrice);
|
|
|
| if(ObjectFind(0, objName) < 0)
|
| {
|
| ObjectCreate(0, objName, OBJ_RECTANGLE, 0, leftDt, leftPrice, rightDt, rightPrice);
|
|
|
| ObjectSetInteger(0, objName, OBJPROP_COLOR, leftPrice < rightPrice ? InpUpTrendColor : InpDownTrendColor);
|
| ObjectSetInteger(0, objName, OBJPROP_FILL, InpFill);
|
| ObjectSetInteger(0, objName, OBJPROP_STYLE, InpBoderStyle);
|
| ObjectSetInteger(0, objName, OBJPROP_WIDTH, InpBorderWidth);
|
| ObjectSetInteger(0, objName, OBJPROP_BACK, true);
|
| ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false);
|
| ObjectSetInteger(0, objName, OBJPROP_SELECTED, false);
|
| ObjectSetInteger(0, objName, OBJPROP_HIDDEN, false);
|
| ObjectSetInteger(0, objName, OBJPROP_ZORDER, 0);
|
|
|
| if(InpDebugEnabled)
|
| {
|
| PrintFormat("Draw box: %s", objName);
|
| }
|
| }
|
| }
|
| //+------------------------------------------------------------------+ |