HiddenGap_AD

Author: Scriptong
Miscellaneous
It issuies visual alerts to the screenUses files from the file system
0 Views
0 Downloads
0 Favorites
HiddenGap_AD
#property copyright "Scriptong"
#property link      "http://advancetools.net"
#property description "English: Finding hidden gaps - jumps of prices inside the candle.\nDescription are available at AdvanceTools.net in the \"Tick volume\" section.\n\nRussian: Íàõîæäåíèå ñêðûòûõ ãýïîâ - ñêà÷êîâ öåíû âíóòðè ñâå÷è.\nÎïèñàíèå äîñòóïíî íà ñàéòå AdvanceTools.net â ðàçäåëå \"Òèêîâûå îáúåìû.\""
#property version "2.0"


#property strict

#property indicator_chart_window
#property indicator_buffers 1 
#property indicator_color1 clrTurquoise

struct TickStruct
{
   datetime time;
   double   bid;
   double   ask;   
};

enum ENUM_YESNO
{
   NO,                                                                                             // No / Íåò
   YES                                                                                             // Yes / Äà
};

enum ENUM_GAP_TYPE
{
   GAP_TYPE_NONE,
   GAP_TYPE_UPWARD,
   GAP_TYPE_DOWNWARD
};

struct GapStruct
{
   ENUM_GAP_TYPE gapType;

   datetime    time;
   double      high;
   double      low;   
};

// Indicator parameters
input uint                 i_gapPoints             = 100;                                          // Points in the gap / Ïóíêòîâ â ãýïå
input color                i_upwardGapColor        = clrBlue;                                      // Color pointers gap up / Öâåò óêàçàòåëåé ãýïà ââåðõ
input color                i_downwardGapColor      = clrRed;                                       // Color pointers gap down / Öâåò óêàçàòåëåé ãýïà âíèç
input color                i_upwardRectColor       = clrTurquoise;                                 // Color rectangles gap up / Öâåò ïðÿìîóãîëüíèêîâ ãýïà ââåðõ
input color                i_downwardRectColor     = clrMaroon;                                    // Color rectangles gap down / Öâåò ïðÿìîóãîëüíèêîâ ãýïà âíèç
input ENUM_YESNO           i_isAlert               = YES;                                          // Alert on gap registration? / Ñèãíàë ïðè îáðàçîâàíèè ãýïà?
input ENUM_YESNO           i_isPush                = YES;                                          // Notification on gap registration? / Óâåäîìëÿòü ïðè îáðàçîâàíèè ãýïà?
input int                  i_indBarsCount          = 10000;                                        // Number of bars to display / Êîë-âî áàðîâ äëÿ îòîáðàæåíèÿ

bool g_activate;

uint g_periodSeconds;                                                                              // Amount of seconds in the current timeframe

datetime g_detailedBarTime;                                                                        // The opening time of the bar, for which the currently displayed detailed information.
                                                                                                   // Zero means that detailed information do not show.

double g_spread,                                                                                   // Spread in the price category for testing mode
       g_gapValue,                                                                                 // The value of the minimum gap in the price category
       g_point,                                                                                    // The one point value
       g_delta;                                                                                    // If the difference between two numbers (double type) is greater than g_delta, 
                                                                                                   // ..it means the comparable numbers are not equal

TickStruct        g_ticks[];                                                                       // Array to store ticks received after the start of program
GapStruct         g_gaps[];                                                                        // Array for storing data about gaps

#define PREFIX                                  "HDNGP_"                                           // Prefix of names of the graphics objects
#define SIGN_ARROW_UP                           "ARROW_UP_"                                        // Attribute of the graphics objects "arrow", owned to upward gaps
#define SIGN_ARROW_DN                           "ARROW_DN_"                                        // Attribute of the graphics objects "arrow", owned to downward gaps
#define SIGN_RECT                               "RECT_"                                            // Attribute of the graphics objects "rectangle"

enum ENUM_MESSAGE_CODE
{
   MESSAGE_CODE_FEW_ITEMS,
   MESSAGE_CODE_TERMINAL_FATAL_ERROR1,
   MESSAGE_CODE_TERMINAL_FATAL_ERROR2,
   MESSAGE_CODE_ALLOCATE_ERROR1, 
   MESSAGE_CODE_READ_TEMP_ERROR, 
   MESSAGE_CODE_WRITE_TEMP_ERROR,
   MESSAGE_CODE_UNDEFINE,
   MESSAGE_CODE_GAP_UPWARD,
   MESSAGE_CODE_GAP_DOWNWARD,
   MESSAGE_CODE_POINTS, 
   MESSAGE_CODE_UPWARD_GAP,
   MESSAGE_CODE_DOWNWARD_GAP,
   MESSAGE_CODE_NOT_ENOUGHT_MEMORY1
};

//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Custom indicator initialization function                                                                                                                                                          |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
int OnInit()
{
   g_activate = false;
   
   if (!TuningParameters())                                                                        
      return INIT_FAILED; 
      
   if (!IsLoadTempTicks())                                                                         
      return INIT_FAILED;

   g_activate = true;
   return INIT_SUCCEEDED;
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Checking the correctness of values of tuning parameters                                                                                                                                           |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
bool TuningParameters()
{
   string name = WindowExpertName();

   if (i_gapPoints < 2)
   {
      Alert(name, GetStringByMessageCode(MESSAGE_CODE_FEW_ITEMS));
      return false;
   } 
   
   g_point = _Point;
   g_delta = - _Point / 10;
   g_gapValue = i_gapPoints * _Point;
   g_spread = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) * _Point;
   if (g_point == 0)
   {
      Alert(name, GetStringByMessageCode(MESSAGE_CODE_TERMINAL_FATAL_ERROR1));
      return false;
   } 
   
   g_periodSeconds = PeriodSeconds();
   if (g_periodSeconds == 0)
   {
      Alert(name, GetStringByMessageCode(MESSAGE_CODE_TERMINAL_FATAL_ERROR2));
      return false;
   } 
   
   g_detailedBarTime = 0;
   
   return true;
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Reading data about ticks accumulated during the previous session of the program                                                                                                                   |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
bool IsLoadTempTicks()
{
   if (IsTesting())
      return true;

   // Opening the file of ticks
   int hTicksFile = FileOpen(Symbol() + "temp.tks", FILE_BIN | FILE_READ | FILE_SHARE_READ | FILE_SHARE_WRITE);
   if (hTicksFile < 1)
      return true;
      
   // Allocating the memory for g_ticks array
   int recSize = (int)(FileSize(hTicksFile) / sizeof(TickStruct));   
   if (ArrayResize(g_ticks, recSize, 1000) < 0)
   {
      Alert(WindowExpertName(), GetStringByMessageCode(MESSAGE_CODE_ALLOCATE_ERROR1));
      FileClose(hTicksFile);
      return false;
   }
   
   // Reading the file
   int i = 0;
   while (i < recSize)
   {
      if (FileReadStruct(hTicksFile, g_ticks[i]) == 0)
      {
         Alert(WindowExpertName(), GetStringByMessageCode(MESSAGE_CODE_READ_TEMP_ERROR));
         return false;
      }
      
      i++;
   }

   FileClose(hTicksFile);
   return true;
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Custom indicator deinitialization function                                                                                                                                                        |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   if (!IsSavedFile())                                                                             
      SaveTempTicks();                                                                             
      
   DeleteObjectsAll();
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Checking the availability of the data recorded by another indicator                                                                                                                               |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
bool IsSavedFile()
{
   if (IsTesting())
      return true;

   // Getting the time of receipt of the last recorded teak
   int lastTickIndex = ArraySize(g_ticks) - 1;
   if (lastTickIndex < 0)                                                                          // No one ticks received. Saving data do not required
      return true;

   // Opening the file of ticks
   int hTicksFile = FileOpen(Symbol() + "temp.tks", FILE_BIN | FILE_READ | FILE_SHARE_READ | FILE_SHARE_WRITE);
   if (hTicksFile < 1)
      return false;
   
   // Moving to last record in the file 
   if (!FileSeek(hTicksFile, -sizeof(TickStruct), SEEK_END))
   {
      FileClose(hTicksFile);
      return false;
   }
   
   // Reading the last record and closing the file 
   TickStruct tick;
   uint readBytes = FileReadStruct(hTicksFile, tick);
   FileClose(hTicksFile);
   if (readBytes == 0)
      return false;
      
   // Comparison of ticks date recorded in the file, and the date of the last incoming tick
   return tick.time >= g_ticks[lastTickIndex].time;                                        
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Saving data about ticks accumulated during the current working session program                                                                                                                    |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void SaveTempTicks()
{
   // Creating of ticks file
   int hTicksFile = FileOpen(Symbol() + "temp.tks", FILE_BIN | FILE_READ | FILE_WRITE | FILE_SHARE_READ | FILE_SHARE_WRITE);
   if (hTicksFile < 1)
      return;
   
   // Saving the file
   int total = ArraySize(g_ticks), i = 0;
   while (i < total)
   {
      if (FileWriteStruct(hTicksFile, g_ticks[i]) == 0)
      {
         Print(GetStringByMessageCode(MESSAGE_CODE_WRITE_TEMP_ERROR));
         return;
      }
      
      i++;
   }

   FileClose(hTicksFile);
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Deleting the objects created by program                                                                                                                                                           |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void DeleteObjectsAll()
{
   for (int i = ObjectsTotal() - 1; i >= 0; i--)     
      if (StringSubstr(ObjectName(i), 0, StringLen(PREFIX)) == PREFIX)
         ObjectDelete(ObjectName(i));
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Determination of the index bar for recalculation                                                                                                                                                  |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
int GetRecalcIndex(int& total, const int ratesTotal, const int prevCalculated)
{
   total = ratesTotal - 1;                                                                         
                                                   
   if (i_indBarsCount > 0 && i_indBarsCount < total)
      total = MathMin(i_indBarsCount, total);                      
                                                   
   // The first displaying of indicator or the swapping data
   if (prevCalculated < ratesTotal - 1)                     
   {  
      ArrayResize(g_gaps, 0, 100);     
      DeleteObjectsAll();
      return (total);
   }
   
   // Normal development of the history
   return (MathMin(ratesTotal - prevCalculated, total));                            
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Displaying the graphical object of the type "arrow"                                                                                                                                               |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void ShowArrow(string name, datetime time, double price, uint gapsCnt, color clr, ENUM_ARROW_ANCHOR anchor)
{
   if (gapsCnt == 0)
      return;
      
   uint arrowCode = gapsCnt < 10 ? 139 + gapsCnt : 149;

   if (ObjectFind(0, name) < 0)
   {
      ObjectCreate(0, name, OBJ_ARROW, 0, time, price);
      ObjectSetInteger(0, name, OBJPROP_ANCHOR, anchor);
      ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
      ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
      ObjectSetInteger(0, name, OBJPROP_BACK, false);
      ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
      ObjectSetInteger(0, name, OBJPROP_HIDDEN, false);
   }
   
   ObjectMove(0, name, 0, time, price);
   ObjectSetInteger(0, name, OBJPROP_ARROWCODE, arrowCode);
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Displaying the graphical object of the type "rectangle"                                                                                                                                           |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void ShowRectangle(string name, datetime time1, double price1, datetime time2, double price2, color clr, string toolTip)
{
   if (ObjectFind(0, name) < 0)
   {
      ObjectCreate(0, name, OBJ_RECTANGLE, 0, time1, price1, time2, price2);
      ObjectSetInteger(0, name, OBJPROP_BACK, false);
      ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
      ObjectSetInteger(0, name, OBJPROP_HIDDEN, false);
   }
   
   ObjectMove(0, name, 0, time1, price1);
   ObjectMove(0, name, 1, time2, price2);
   ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
   ObjectSetString(0, name, OBJPROP_TOOLTIP, toolTip);
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Determination of height of the gap in points                                                                                                                                                      |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
int GetPointDiff(double value1, double value2)
{
   return (int)MathRound(MathAbs(value1 - value2) / g_point);
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Filling an array barGaps by information of gaps, beginning from the narrow and finishing by the widest                                                                                            |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void CreateOrderedArray(GapStruct &barsGap[], int firstGapIndex, int lastGapIndex)
{
   // Direct copying of data from g_gaps to barsGap
   int cnt = 0;
   for (int i = firstGapIndex; i <= lastGapIndex; i++)
   {
      barsGap[cnt] = g_gaps[i];
      cnt++;
   }
   
   // Sorting of array barsGap
   GapStruct temp;
   for (int i = 1; i < cnt; i++)
      for (int j = i; j > 0; j--)
         if (GetPointDiff(barsGap[j].high, barsGap[j].low) < GetPointDiff(barsGap[j - 1].high, barsGap[j - 1].low))
         {
            temp = barsGap[j];
            barsGap[j] = barsGap[j - 1];
            barsGap[j - 1] = temp;
         }
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Determination of open time of the bar, which is located on the right with respect to said bar at a distance "increment"                                                                           |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
datetime GetRightTime(int leftBarIndex, int increment)
{
   int diff = leftBarIndex - increment;
   if (diff >= 0)
      return Time[diff];
      
   return Time[0] - diff * g_periodSeconds;
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Cast a type of gap to string                                                                                                                                                                      |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
string GapTypeToString(GapStruct &barGaps)
{
   string gapHeight = DoubleToString(MathRound((barGaps.high - barGaps.low) / g_point), 0);
   switch (barGaps.gapType)
   {
      case GAP_TYPE_NONE: return GetStringByMessageCode(MESSAGE_CODE_UNDEFINE);
      case GAP_TYPE_DOWNWARD: return GetStringByMessageCode(MESSAGE_CODE_GAP_DOWNWARD) + gapHeight + GetStringByMessageCode(MESSAGE_CODE_POINTS);
      case GAP_TYPE_UPWARD: return GetStringByMessageCode(MESSAGE_CODE_GAP_UPWARD) + gapHeight + GetStringByMessageCode(MESSAGE_CODE_POINTS);
   }   
   
   return GetStringByMessageCode(MESSAGE_CODE_UNDEFINE);
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Show the detailed information about gap                                                                                                                                                           |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void ShowDetailedGap(int firstGapIndex, int lastGapIndex, datetime curBarTime, int barIndex)
{
   GapStruct barGaps[];
   int gapsCnt = lastGapIndex - firstGapIndex + 1;
   if (ArrayResize(barGaps, gapsCnt) == 0)
      return;
      
   CreateOrderedArray(barGaps, firstGapIndex, lastGapIndex);
   
   int barsWidth = 5;                                                                              
   color clr = i_upwardRectColor;
   for (int i = 0; i < gapsCnt; i++)
   {
      clr = (barGaps[i].gapType == GAP_TYPE_UPWARD) ? i_upwardGapColor : i_downwardRectColor;
      ShowRectangle(PREFIX + SIGN_RECT + IntegerToString(barsWidth), curBarTime, barGaps[i].high, 
                    GetRightTime(barIndex, barsWidth), barGaps[i].low, clr, 
                    GapTypeToString(barGaps[i]) + "\nHigh: " + DoubleToString(barGaps[i].high, _Digits) + 
                    "\nLow: " + DoubleToString(barGaps[i].low, _Digits));
      
      barsWidth += 3;
   }
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Show the gaps of the one candle                                                                                                                                                                   |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void ShowLastGaps(int lastGapIndex)
{
   if (lastGapIndex >= ArraySize(g_gaps) || lastGapIndex < 0)
      return;
      
   // Determination of the number and types of gaps of the current candle
   uint upwardGapsCnt = 0, downwardGapsCnt = 0;
   datetime curBarTime = (g_gaps[lastGapIndex].time / g_periodSeconds) * g_periodSeconds;
   int firstGapIndex = lastGapIndex;
   for (; firstGapIndex >= 0 && curBarTime <= g_gaps[firstGapIndex].time; firstGapIndex--)
   {
      if (g_gaps[firstGapIndex].gapType == GAP_TYPE_DOWNWARD)
         downwardGapsCnt++;
      if (g_gaps[firstGapIndex].gapType == GAP_TYPE_UPWARD)
         upwardGapsCnt++;
   }
   
   // Show the amount of gaps on the current candle
   int barIndex = iBarShift(NULL, 0, curBarTime);
   ShowArrow(PREFIX + SIGN_ARROW_DN + IntegerToString(curBarTime), curBarTime, High[barIndex], downwardGapsCnt, i_downwardGapColor, ANCHOR_BOTTOM);
   ShowArrow(PREFIX + SIGN_ARROW_UP + IntegerToString(curBarTime), curBarTime, Low[barIndex], upwardGapsCnt, i_upwardGapColor, ANCHOR_TOP);
   
   // View a detailed information of gaps at the current candle
   if (curBarTime == g_detailedBarTime)
      ShowDetailedGap(firstGapIndex + 1, lastGapIndex, curBarTime, barIndex);                        
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Cast the timeframe to string value                                                                                                                                                                |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
string CastTFToString(ENUM_TIMEFRAMES tf)
{
   switch(tf)
   {
      case PERIOD_H1: return "H1";
      case PERIOD_H4: return "H4";
      case PERIOD_D1: return "D1";
      case PERIOD_W1: return "W1";
      case PERIOD_MN1: return "MN1";
   }
   
   return "M" + IntegerToString((int)tf);
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Alert and notification on new gap registration                                                                                                                                                    |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void SignalOnGap(string text)
{
   if (i_isAlert)
      Alert(_Symbol, ", ", CastTFToString((ENUM_TIMEFRAMES)_Period), ": ", text);
      
   if (i_isPush)
      SendNotification(_Symbol + ", " + CastTFToString((ENUM_TIMEFRAMES)_Period) + ": " + text);
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Saving the data of gap and displaying it on the chart                                                                                                                                             |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
bool SaveAndShowGap(TickStruct &prevTick, TickStruct &curTick)
{
   int gapsCnt = ArraySize(g_gaps);
   if (ArrayResize(g_gaps, gapsCnt + 1, 100) < 0)
   {
      Alert(WindowExpertName(), GetStringByMessageCode(MESSAGE_CODE_NOT_ENOUGHT_MEMORY1));
      g_activate = false;
      return false;
   }

   g_gaps[gapsCnt].time = curTick.time;
   
   // Upward gap
   if (prevTick.bid < curTick.bid)
   {
      g_gaps[gapsCnt].gapType = GAP_TYPE_UPWARD;
      g_gaps[gapsCnt].high = curTick.bid;
      g_gaps[gapsCnt].low = prevTick.bid;
      if (curTick.time >= Time[0])
         SignalOnGap(GetStringByMessageCode(MESSAGE_CODE_UPWARD_GAP));
   }
   // Downward gap
   else
   {
      g_gaps[gapsCnt].gapType = GAP_TYPE_DOWNWARD;
      g_gaps[gapsCnt].high = prevTick.bid;
      g_gaps[gapsCnt].low = curTick.bid;
      if (curTick.time >= Time[0])
         SignalOnGap(GetStringByMessageCode(MESSAGE_CODE_DOWNWARD_GAP));
   }
   
   ShowLastGaps(gapsCnt);
   return true;
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Determination of the existence of a gap between two adjacent ticks                                                                                                                                |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
bool IsGap(TickStruct &prevTick, TickStruct &curTick)
{
   // Does the gap exists?
   if (!IsFirstMoreThanSecond(MathAbs(prevTick.bid - curTick.bid), g_gapValue))
      return false;
      
   // Determining bars which include these ticks
   datetime curTickBarTime = (curTick.time / g_periodSeconds) * g_periodSeconds;
   datetime prevTickBarTime = (prevTick.time / g_periodSeconds) * g_periodSeconds;
   if (curTickBarTime != prevTickBarTime)
   {
      int curTickBarIndex = iBarShift(NULL, 0, curTickBarTime);
      int prevTickBarIndex = iBarShift(NULL, 0, prevTickBarTime);
      if (prevTickBarIndex - curTickBarIndex > 1)                                                  // Ticks do not belong to the neighboring bars. It is a "hole" at the history
         return false;                                                                             
   }
      
   return true;
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Reading the data from the base history file                                                                                                                                                       |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
datetime ReadBaseFileHistory(int limit)
{
   if (IsTesting())
      return 0;

   // Opening the file of ticks
   int hTicksFile = FileOpen(Symbol() + ".tks", FILE_BIN | FILE_READ | FILE_SHARE_READ | FILE_SHARE_WRITE);
   if (hTicksFile < 1)
      return Time[limit];
      
   // Search first tick owned to "limit" bar or to any later bar
   TickStruct tick;
   tick.time = Time[limit];
   tick.bid = Open[limit];
   while (!IsStopped())
   {
      if (!IsReadOneTick(hTicksFile, tick))
         return (datetime)MathMax(tick.time, Time[limit]);
         
      if (tick.time >= Time[limit])
         break;
   }
   
   // Finding the gaps at the history
   datetime extremeTime = Time[0] + PeriodSeconds();
   TickStruct prevTick = tick;
   while (tick.time < extremeTime && tick.time != 0)
   {
      if (!IsReadOneTick(hTicksFile, tick))
         return tick.time;
         
      if (IsGap(prevTick, tick))
         if (!SaveAndShowGap(prevTick, tick))
            break;
            
      prevTick = tick;
   }
   
   FileClose(hTicksFile);
   return tick.time;
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Processing data, which readed from additional history file                                                                                                                                        |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void ReadAdditionalHistory(datetime lastTick)
{
   int total = ArraySize(g_ticks);
   for (int i = 0; i < total; i++)
   {
      if (g_ticks[i].time <= lastTick)
         continue;
         
      if (i > 0 && IsGap(g_ticks[i - 1], g_ticks[i]))
         SaveAndShowGap(g_ticks[i - 1], g_ticks[i]);
   }
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Displaying the data about historical bars beginning from specified bar index                                                                                                                      |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void ProcessOldCandles(int limit)
{
   datetime lastTick = ReadBaseFileHistory(limit);
   ReadAdditionalHistory(lastTick);
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Reading of the one tick from file                                                                                                                                                                 |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
bool IsReadOneTick(int hTicksFile, TickStruct &tick)
{
   if (FileIsEnding(hTicksFile))
   {
      FileClose(hTicksFile);
      return false;
   }
   
   uint bytesCnt = FileReadStruct(hTicksFile, tick);
   if (bytesCnt == sizeof(TickStruct))
      return true;
   
   FileClose(hTicksFile);
   return false;
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| First value greater than second value?                                                                                                                                                            |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
bool IsFirstMoreThanSecond(double first, double second)
{
   return (first - second > g_delta);
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Write the ticks data to g_ticks array                                                                                                                                                             |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
bool IsUpdateTicksArray(TickStruct &tick, int ticksCnt)
{
   if (ArrayResize(g_ticks, ticksCnt + 1, 100) < 0)
   {
      Alert(WindowExpertName(), GetStringByMessageCode(MESSAGE_CODE_NOT_ENOUGHT_MEMORY1));
      return false;
   }
   
   g_ticks[ticksCnt] = tick;
   return true;
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Adding of new tick to a forming candle                                                                                                                                                            |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
bool ProcessOneTick(int limit)
{
   TickStruct tick;
   if (IsTesting())
   {
      tick.time = TimeCurrent();
      tick.bid = Close[0];
      tick.ask = tick.bid + g_spread;
   }
   else
   {
      tick.time = TimeCurrent();
      tick.ask = Ask;
      tick.bid = Bid;
   }

   // Adding of new tick to an array g_ticks
   int ticksCnt = ArraySize(g_ticks);
   if (!IsUpdateTicksArray(tick, ticksCnt))
   {
      g_activate = false;
      return false;
   }

   // Is new gap created?
   if (ticksCnt <= 0 || !IsGap(g_ticks[ticksCnt - 1], g_ticks[ticksCnt]))
      return false;

   SaveAndShowGap(g_ticks[ticksCnt - 1], g_ticks[ticksCnt]);
   return true;
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Calculation of the indicator values                                                                                                                                                               |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void CalcIndicatorData(int limit, int total)
{
   if (limit > 1)                                                                                  
   {                                                                                               
      ProcessOldCandles(limit);
      return;
   }
    
   if (!ProcessOneTick(limit))
      ShowLastGaps(ArraySize(g_gaps) - 1);
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Custom indicator iteration function                                                                                                                                                               |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime& time[],
                const double& open[],
                const double& high[],
                const double& low[],
                const double& close[],
                const long& tick_volume[],
                const long& volume[],
                const int& spread[])
{
   if (!g_activate)
      return rates_total;

   int total;   
   int limit = GetRecalcIndex(total, rates_total, prev_calculated);                                

   CalcIndicatorData(limit, total);                                                                

   return rates_total;
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Deleting the all objects displaying the detailed information about gaps at the specified bar                                                                                                      |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void DeleteObjectsAtBar(datetime time)
{
   string name = PREFIX + SIGN_RECT;
   int nameLen = StringLen(name);
   for (int i = ObjectsTotal() - 1; i >= 0; i--)
   {
      string objName = ObjectName(i);
      if (StringSubstr(objName, 0, nameLen) == name)
         ObjectDelete(objName);
   }
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Process the chart events                                                                                                                                                                          |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)
{
   if (id != CHARTEVENT_OBJECT_CLICK)
      return;
   
   string objName = StringSubstr(sparam, 0, StringLen(PREFIX + SIGN_ARROW_UP));   
   if (objName != PREFIX + SIGN_ARROW_UP && objName != PREFIX + SIGN_ARROW_DN)
      return;
   
   // Erase the detailed information if click was committed at another bar
   datetime clickBarTime = (datetime)ObjectGetInteger(0, sparam, OBJPROP_TIME);
   if (g_detailedBarTime != 0)
      DeleteObjectsAtBar(g_detailedBarTime);
      
   // If a click is made on the same bar, the new details are not show
   if (g_detailedBarTime == clickBarTime)
   {
      g_detailedBarTime = 0;
      return;
   }
      
   // Displaying the detailed information
   g_detailedBarTime = clickBarTime;
   int index = ArraySize(g_gaps) - 1;
   for (; index >= 0; index--)
      if ((g_gaps[index].time / g_periodSeconds) * g_periodSeconds == g_detailedBarTime)
         break;
   
   ShowLastGaps(index);
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Getting string by code of message and terminal language                                                                                                                                           |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
string GetStringByMessageCode(ENUM_MESSAGE_CODE messageCode)
{
   string language = TerminalInfoString(TERMINAL_LANGUAGE);
   if (language == "Russian")
      return GetRussianMessage(messageCode);
      
   return GetEnglishMessage(messageCode);
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Getting string by code of message for russian language                                                                                                                                            |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
string GetRussianMessage(ENUM_MESSAGE_CODE messageCode)
{
   switch (messageCode)
   {
      case MESSAGE_CODE_FEW_ITEMS:                    return ": ìèíèìàëüíîå êîëè÷åñòâî ïóíêòîâ â ãýïå - 2. Èíäèêàòîð îòêëþ÷åí.";
      case MESSAGE_CODE_TERMINAL_FATAL_ERROR1:        return ": ôàòàëüíàÿ îøèáêà òåðìèíàëà - ïóíêò ðàâåí íóëþ. Èíäèêàòîð îòêëþ÷åí.";
      case MESSAGE_CODE_TERMINAL_FATAL_ERROR2:        return ": ôàòàëüíàÿ îøèáêà òåðìèíàëà - ïåðèîä ãðàôèêà ðàâåí íóëþ. Èíäèêàòîð îòêëþ÷åí.";
      case MESSAGE_CODE_ALLOCATE_ERROR1:              return ": íå óäàëîñü ðàñïðåäåëèòü ïàìÿòü äëÿ ïîäêà÷êè äàííûõ èç âðåìåííîãî ôàéëà òèêîâ. Èíäèêàòîð îòêëþ÷åí.";
      case MESSAGE_CODE_READ_TEMP_ERROR:              return ": îøèáêà ÷òåíèÿ äàííûõ èç âðåìåííîãî ôàéëà. Èíäèêàòîð îòêëþ÷åí.";
      case MESSAGE_CODE_WRITE_TEMP_ERROR:             return "Îøèáêà ñîõðàíåíèÿ äàííûõ âî âðåìåííûé ôàéë...";
      case MESSAGE_CODE_UNDEFINE:                     return "Í/Î";
      case MESSAGE_CODE_GAP_UPWARD:                   return "Ãýï ââåðõ (";
      case MESSAGE_CODE_GAP_DOWNWARD:                 return "Ãýï âíèç (";
      case MESSAGE_CODE_POINTS:                       return " ïï.)";
      case MESSAGE_CODE_NOT_ENOUGHT_MEMORY1:          return ": íå õâàòàåò ïàìÿòè.";
      case MESSAGE_CODE_UPWARD_GAP:                   return ": ãýï ââåðõ.";
      case MESSAGE_CODE_DOWNWARD_GAP:                 return ": ãýï âíèç.";
   }
   
   return "";
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Getting string by code of message for english language                                                                                                                                            |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
string GetEnglishMessage(ENUM_MESSAGE_CODE messageCode)
{
   switch (messageCode)
   {
      case MESSAGE_CODE_FEW_ITEMS:                    return ": the minimum number of points in the gap - 2. The indicator is turned off.";
      case MESSAGE_CODE_TERMINAL_FATAL_ERROR1:        return ": terminal fatal error - point equals to zero. The indicator is turned off.";
      case MESSAGE_CODE_TERMINAL_FATAL_ERROR2:        return ": terminal fatal error - period of chart equals to zero. The indicator is turned off.";
      case MESSAGE_CODE_ALLOCATE_ERROR1:              return ": allocate the memory for swap data from the temporary file ticks is failed. The indicator is turned off.";
      case MESSAGE_CODE_READ_TEMP_ERROR:              return ": reading the data from the temporary file failed. The indicator is turned off.";
      case MESSAGE_CODE_WRITE_TEMP_ERROR:             return "Saving data to temporary file failed...";
      case MESSAGE_CODE_UNDEFINE:                     return "U/D";
      case MESSAGE_CODE_GAP_UPWARD:                   return "Upward gap (";
      case MESSAGE_CODE_GAP_DOWNWARD:                 return "Downward gap (";
      case MESSAGE_CODE_POINTS:                       return " pts.)";
      case MESSAGE_CODE_NOT_ENOUGHT_MEMORY1:          return ": not enought memory.";
      case MESSAGE_CODE_UPWARD_GAP:                   return ": upward gap.";
      case MESSAGE_CODE_DOWNWARD_GAP:                 return ": downward gap.";
   }

   return "";
}

Comments