#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