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