This script is a specialized visual tool for MetaTrader 4 designed to help traders visualize high-frequency price activity by creating "Cluster Histograms." Unlike standard volume indicators that show total volume over a candle, this tool breaks down price action into specific zones to reveal where the most significant trading activity occurred.
Core Functionality
The primary purpose of this script is to group individual price ticks into "clusters." It identifies specific price levels where trading activity is concentrated and represents this data as a histogram on your chart. By doing this, it allows you to see not just that volume happened, but exactly at what price it occurred.
Key Features and Logic:
1. Dynamic View Range (The Selection Box) One of the most unique aspects of this tool is the interactive rectangle on the chart. This box defines the specific time area you want to analyze. You can move or resize this area, and the script will automatically recalculate the cluster data only for the price action contained within those boundaries.
2. Cluster Grouping and Scaling The script takes every individual "tick" (the smallest move in price) and assigns it to a price cluster based on a user-defined point range. For example, if you set the cluster size to 50 points, all ticks occurring within that 50-point window are grouped together as part of the same horizontal bar in the histogram.
3. Multi-Level Visual Feedback To make the data easy to read at a glance, the tool features a tiered coloring system:
- Volume Thresholds: You can set different minimum volume levels (e.g., Level 1, Level 2, etc.).
- Color Coding: Each level is assigned a specific color. As a price cluster becomes more active (reaching higher tick counts), it changes color, allowing you to instantly identify the "hot zones" of highest liquidity or heavy trading.
4. Delta Tracking (Bull vs. Bear Activity) Beyond just showing total volume, the script can be configured to show the "Delta." This tracks the direction of the price movement within each cluster:
- It counts how many ticks moved upward (Bulls) versus how many moved downward (Bears).
- These are displayed as separate lines/histograms above and below the main cluster price, providing a clearer picture of whether aggressive buying or selling was driving the volume in that specific zone.
5. Data Persistence and Memory Management Because tick data is very dense and can be heavy for a computer to process all at once, the script includes an intelligent data handling system:
- Temporary Storage: It saves and loads tick data from temporary files, ensuring that information isn't lost when the indicator is restarted.
- Buffer Management: It manages memory efficiently by only processing ticks relevant to your active view range, keeping the platform running smoothly even during high volatility.
Summary of User Experience
From a user's perspective, this tool transforms raw, messy tick data into an organized visual map. By looking at the resulting histogram, you can see "walls" or "pools" of activity. You can identify where large orders are likely sitting based on historical cluster density and see whether those clusters were formed by aggressive buyers (bullish delta) or sellers (bearish delta).
//+------------------------------------------------------------------+
//| clusterbox_histogramm_ad.mq4 |
//| Copyright 2015, Scriptong |
//| http://advancetools.net |
//+------------------------------------------------------------------+
#property copyright "Scriptong"
#property link "http://advancetools.net"
#property description "English: Displays the ticks volume of specified interval in the form of cluster histogram.\nRussian: Îòîáðàæåíèå òèêîâûõ îáúåìîâ çàäàííîãî èíòåðâàëà â âèäå ãèñòîãðàììû êëàñòåðîâ."
#property strict
#property indicator_chart_window
#property indicator_buffers 1
#define MAX_VOLUMES_SHOW 5 // Êîëè÷åñòâî óðîâíåé ìàêñèìàëüíîãî îáúåìà, êîòîðûå ñëåäóåò îòîáðàæàòü
#define VIEWRANGE_STARTBAR 10 // Èíäåêñ áàðà, íà êîòîðîì íà÷èíàåòñÿ îáëàñòü îòîáðàæåíèÿ ãèñòîãðàììû
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
struct LevelVolumeColor // Ñòðóêòóðà ñîîòâåòñòâèÿ óðîâíåé îáúåìà, äîñòèæåíèå êîòîðûõ íà öåíîâîì óðîâíå îòîáðàæàåòñÿ..
{ // ..ñîîòâåòñòâóþùèì öâåòîì
color levelColor;
int levelMinVolume;
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
struct TickStruct // Ñòðóêòóðà äëÿ çàïèñè äàííûõ îá îäíîì òèêå
{
datetime time;
double bid;
double ask;
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
struct ViewRange // Ñòðóêòóðà äëÿ çàïèñè ãðàíèö èíòåðâàëà îòîáðàæåíèÿ ãèñòîãðàììû
{
datetime leftTime;
datetime rightTime;
int rightIndex;
int rangeDuration;
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
struct LevelsData // Ñòðóêòóðà äëÿ çàïèñè öåíîâîãî óðîâíÿ, êîëè÷åñòâà åãî ïîâòîðåíèé, ðîñòà è ïàäåíèÿ öåíû íà èíòåðâàëå îòîáðàæåíèÿ ãèñòîãðàììû
{
double price;
int repeatCnt;
int bullsCnt;
int bearsCnt;
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
enum ENUM_YESNO
{
YES, // Yes / Äà
NO // No / Íåò
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
enum ENUM_LINEWIDTH // Òîëùèíà ëèíèé ãèñòîãðàììû
{
LINEWIDTH_NULL, // Most thin / Íàèáîëåå òîíêàÿ
LINEWIDTH_ONE, // Thin / Òîíêàÿ
LINEWIDTH_TWO, // Median / Ñðåäíÿÿ
LINEWIDTH_THREE, // Thick / Òîëñòàÿ
LINEWIDTH_FOUR // Most thick / Íàèáîëåå òîëñòàÿ
};
// Íàñòðîå÷íûå ïàðàìåòðû èíäèêàòîðà
input int i_pointsInBox = 50; // Points in one cluster / Êîëè÷åñòâî ïóíêòîâ â îäíîì êëàñòåðå
input string i_string1 = "Min volumes and colors / Ìèí. îáúåìû è öâåòà"; // ==============================================
input int i_minVolumeLevel1 = 1; // Minimal volume. Level 1 / Ìèíèìàëüíûé îáúåì. Óðîâåíü 1
input color i_colorLevel1 = clrSkyBlue; // Color of level 1 / Öâåò óðîâíÿ 1
input int i_minVolumeLevel2 = 50; // Minimal volume. Level 2 / Ìèíèìàëüíûé îáúåì. Óðîâåíü 2
input color i_colorLevel2 = clrTurquoise; // Color of level 2 / Öâåò óðîâíÿ 2
input int i_minVolumeLevel3 = 75; // Minimal volume. Level 3 / Ìèíèìàëüíûé îáúåì. Óðîâåíü 3
input color i_colorLevel3 = clrRoyalBlue; // Color of level 3 / Öâåò óðîâíÿ 3
input int i_minVolumeLevel4 = 100; // Minimal volume. Level 4 / Ìèíèìàëüíûé îáúåì. Óðîâåíü 4
input color i_colorLevel4 = clrBlue; // Color of level 4 / Öâåò óðîâíÿ 4
input int i_minVolumeLevel5 = 150; // Minimal volume. Level 5 / Ìèíèìàëüíûé îáúåì. Óðîâåíü 5
input color i_colorLevel5 = clrMagenta; // Color of level 5 / Öâåò óðîâíÿ 5
input string i_string2 = "Delta of volumes / Äåëüòû îáúåìîâ"; // ==============================================
input ENUM_YESNO i_isShowDelta = YES; // Show the delta of volumes? / Îòîáðàæàòü äåëüòó îáúåìîâ?
input color i_bullDeltaColor = clrLime; // Color of line price growth / Öâåò ëèíèè ðîñòà öåíû
input color i_bearDeltaColor = clrRed; // Color of line fall in prices / Öâåò ëèíèè ïàäåíèÿ öåíû
input string i_string3 = "Ïàðàìåòðû ãðàôèêà"; // ==============================================
input ENUM_LINEWIDTH i_lineWidth = LINEWIDTH_THREE; // Histogram thickness / Òîëùèíà ëèíèè ãèñòîãðàììû
input color i_viewRangeColor = clrGoldenrod; // Rectangle color / Öâåò ïðÿìîóãîëüíèêà
input ENUM_YESNO i_point5Digits = YES; // Use 5-digits in prices? / Èñïîëüçîâàòü 5-çíà÷íîå ïðåäñòàâëåíèå êîòèðîâîê?
input int i_indBarsCount=10000; // Number of bars to display / Êîë-âî áàðîâ îòîáðàæåíèÿ
// Ïðî÷èå ãëîáàëüíûå ïåðåìåííûå èíäèêàòîðà
bool g_chartForeground, // Ïðèçíàê íàõîæäåíèÿ ñâå÷åé íà ïåðåäíåì ïëàíå
g_activate; // Ïðèçíàê óñïåøíîé èíèöèàëèçàöèè èíäèêàòîðà
int g_pointMultiply; // Ìíîæèòåëü âåëè÷èíû ïóíêòà, èñïîëüçóþùèéñÿ ïðè ðàáîòå íà 5-çíà÷íûõ êîòèðîâêàõ
double g_point,
g_tickSize;
TickStruct g_ticks[]; // Ìàññèâ äëÿ õðàíåíèÿ òèêîâ, ïîñòóïèâøèõ ïîñëå íà÷àëà ðàáîòû èíäèêàòîðà
LevelVolumeColor g_volumeLevelsColor[MAX_VOLUMES_SHOW]; // Ìàññèâ îáúåìîâ è, ñîîòâåòñòâóþùèì èì, öâåòîâ óðîâíåé
ViewRange g_viewRange; // Òåêóùåå ïîëîæåíèå îáëàñòè îòîáðàæåíèÿ ãèñòîãðàììû
LevelsData g_levelsData[]; // Ðàáî÷èé ìàññèâ óðîâíåé, â êîòîðûé çàïèñûâàåòñÿ êîëè÷åñòâî òèêîâ, ïîïàâøèõ íà ñîîòâåòñòâóþùóþ öåíó
#define PREFIX "CLSTRBXH_" // Ïðåôèêñ ãðàôè÷åñêèõ îáúåêòîâ, îòîáðàæàåìûõ èíäèêàòîðîì
#define VIEW_RANGE "VIEWRANGE" // Êîðåíü èìåíè ãðàôè÷åñêîãî îáúåêòà "Âðåìåííûå çîíû", óêàçûâàþùåãî èíòåðâàë îòîáðàæåíèÿ..
// ..ãèñòîãðàììû êëàñòåðíûõ îáúåìîâ
#define FONT_NAME "MS Sans Serif"
#define FONT_SIZE 8
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Custom indicator initialization function |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
int OnInit()
{
g_activate=false; // Èíäèêàòîð íå èíèöèàëèçèðîâàí
if(!IsTuningParametersCorrect()) // Íåâåðíî óêàçàííûå çíà÷åíèÿ íàñòðîå÷íûõ ïàðàìåòðîâ - ïðè÷èíà íåóäà÷íîé èíèöèàëèçàöèè
return INIT_FAILED;
if(!IsLoadTempTicks()) // Çàãðóçêà äàííûõ î òèêàõ, ñîõðàíåííûõ çà ïðåäûäóùèé ïåðèîä ðàáîòû èíäèêàòîðà
return INIT_FAILED;
InitViewRange(); // Èíèöèàëèçàöèÿ äàííûõ èíòåðâàëà îòîáðàæåíèÿ ãèñòîãðàììû
CreateVolumeColorsArray(); // Êîïèðîâàíèå äàííûõ î öâåòå è âåëè÷èíå óðîâíåé â ìàññèâ
g_activate=true; // Èíäèêàòîð óñïåøíî èíèöèàëèçèðîâàí
return INIT_SUCCEEDED;
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Ïðîâåðêà êîððåêòíîñòè íàñòðîå÷íûõ ïàðàìåòðîâ |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
bool IsTuningParametersCorrect()
{
string name=WindowExpertName();
int period= Period();
if(period == 0)
{
Alert(name,": ôàòàëüíàÿ îøèáêà òåðìèíàëà - ïåðèîä 0 ìèíóò. Èíäèêàòîð îòêëþ÷åí.");
return (false);
}
g_point=Point;
if(g_point==0)
{
Alert(name,": ôàòàëüíàÿ îøèáêà òåðìèíàëà - âåëè÷èíà ïóíêòà ðàâíà íóëþ. Èíäèêàòîð îòêëþ÷åí.");
return (false);
}
g_tickSize=MarketInfo(Symbol(),MODE_TICKSIZE);
if(g_tickSize==0)
{
Alert(name,": ôàòàëüíàÿ îøèáêà òåðìèíàëà - âåëè÷èíà øàãà îäíîãî òèêà ðàâíà íóëþ. Èíäèêàòîð îòêëþ÷åí.");
return (false);
}
g_pointMultiply=1;
if(i_point5Digits==YES)
g_pointMultiply=10;
if(i_pointsInBox<3*g_pointMultiply)
{
Alert(name,": êîëè÷åñòâî ïóíêòîâ â êëàñòåðå äîëæíî áûòü íå ìåíåå ",3*g_pointMultiply,". Èíäèêàòîð îòêëþ÷åí.");
return (false);
}
if(VIEWRANGE_STARTBAR>=Bars)
{
Alert(name,": òåêóùåå êîëè÷åñòâî áàðîâ ãðàôèêà ñëèøêîì ìàëî. Èíäèêàòîð îòêëþ÷åí.");
return (false);
}
return (true);
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| ×òåíèå äàííûõ î òèêàõ, íàêîïëåííûõ â òå÷åíèå ïðåäûäóùåé ðàáî÷åé ñåññèè ïðîãðàììû |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
bool IsLoadTempTicks()
{
// Îòêðûòèå ôàéëà òèêîâîé èñòîðèè
int hTicksFile=FileOpen(Symbol()+"temp.tks",FILE_BIN|FILE_READ|FILE_SHARE_READ|FILE_SHARE_WRITE);
if(hTicksFile<1)
return true;
// Ðàñïðåäåëåíèå ïàìÿòè äëÿ ìàññèâà g_ticks
int recSize=(int)(FileSize(hTicksFile)/sizeof(TickStruct));
if(ArrayResize(g_ticks,recSize,1000)<0)
{
Alert(WindowExpertName(),": íå óäàëîñü ðàñïðåäåëèòü ïàìÿòü äëÿ ïîäêà÷êè äàííûõ èç âðåìåííîãî ôàéëà òèêîâ. Èíäèêàòîð îòêëþ÷åí.");
FileClose(hTicksFile);
return false;
}
// ×òåíèå ôàéëà
int i=0;
while(i<recSize)
{
if(FileReadStruct(hTicksFile,g_ticks[i])==0)
{
Alert(WindowExpertName(),": îøèáêà ÷òåíèÿ äàííûõ èç âðåìåííîãî ôàéëà. Èíäèêàòîð îòêëþ÷åí.");
return false;
}
i++;
}
FileClose(hTicksFile);
return true;
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Èíèöèàëèçàöèÿ äàííûõ èíòåðâàëà îòîáðàæåíèÿ ãèñòîãðàììû |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void InitViewRange()
{
g_viewRange.rightTime= Time[0]+PeriodSeconds();
g_viewRange.leftTime = Time[VIEWRANGE_STARTBAR];
g_viewRange.rightIndex=-1;
g_viewRange.rangeDuration=VIEWRANGE_STARTBAR+2;
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Ôîðìèðîâàíèå ìàññèâà çíà÷åíèé îáúåìîâ è ñîîòâåòñòâóþùèõ èì öâåòàì óðîâíåé |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void CreateVolumeColorsArray()
{
g_volumeLevelsColor[0].levelMinVolume = i_minVolumeLevel1;
g_volumeLevelsColor[1].levelMinVolume = i_minVolumeLevel2;
g_volumeLevelsColor[2].levelMinVolume = i_minVolumeLevel3;
g_volumeLevelsColor[3].levelMinVolume = i_minVolumeLevel4;
g_volumeLevelsColor[4].levelMinVolume = i_minVolumeLevel5;
g_volumeLevelsColor[0].levelColor = i_colorLevel1;
g_volumeLevelsColor[1].levelColor = i_colorLevel2;
g_volumeLevelsColor[2].levelColor = i_colorLevel3;
g_volumeLevelsColor[3].levelColor = i_colorLevel4;
g_volumeLevelsColor[4].levelColor = i_colorLevel5;
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(!IsSavedFile()) // Åñëè íè îäèí èç ïîäêëþ÷åííûõ èíäèêàòîðîâ íå ñîõðàíèë äàííûå, òî èõ ñîõðàíèò òåêóùèé èíäèêàòîð
SaveTempTicks(); // Ñîõðàíåíèå äàííûõ î òèêàõ, íàêîïëåííûõ çà òåêóùèé ïåðèîä ðàáîòû èíäèêàòîðà
DeleteAllObjects();
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Ïðîâåðêà íàëè÷èÿ çàïèñàííûõ äàííûõ äðóãèì èíäèêàòîðîì |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
bool IsSavedFile()
{
// Ïîëó÷åíèå âðåìåíè ïîñòóïëåíèÿ ïîñëåäíåãî çàïèñàííîãî òèêà
int lastTickIndex=ArraySize(g_ticks)-1;
if(lastTickIndex<0) // Íè îäèí òèê íå áûë ïîëó÷åí. Çàïèñü äàííûõ íå òðåáóåòñÿ
return true;
// Îòêðûòèå ôàéëà òèêîâîé èñòîðèè
int hTicksFile=FileOpen(Symbol()+"temp.tks",FILE_BIN|FILE_READ|FILE_SHARE_READ|FILE_SHARE_WRITE);
if(hTicksFile<1)
return false;
// Ïåðåìåùåíèå ê ïîñëåäíåé çàïèñè â ôàéëå
if(!FileSeek(hTicksFile,-sizeof(TickStruct),SEEK_END))
{
FileClose(hTicksFile);
return false;
}
// ×òåíèå ïîñëåäíåé çàïèñè è çàêðûòèå ôàéëà
TickStruct tick;
uint readBytes=FileReadStruct(hTicksFile,tick);
FileClose(hTicksFile);
if(readBytes==0)
return false;
// Ñðàâíåíèå äàòû òèêà, çàïèñàííîãî â ôàéëå, è äàòû ïîñëåäíåãî ïîñòóïèâøåãî òèêà
return tick.time >= g_ticks[lastTickIndex].time; // Äàòà/âðåìÿ ïîñëåäíåãî çàïèñàííîãî â ôàéëå òèêà áîëüøå èëè ðàâíà äàòå/âðåìåíè..
// ..çàðåãèñòðèðîâàííîãî òèêà. Çíà÷èò, ôàéë óæå çàïèñàí, è ïîâòîðíàÿ çàïèñü íå òðåáóåòñÿ
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Ñîõðàíåíèå äàííûõ î òèêàõ, íàêîïëåííûõ çà òåêóùóþ ðàáî÷óþ ñåññèþ ïðîãðàììû |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void SaveTempTicks()
{
// Ñîçäàíèå ôàéëà òèêîâîé èñòîðèè
int hTicksFile=FileOpen(Symbol()+"temp.tks",FILE_BIN|FILE_READ|FILE_WRITE|FILE_SHARE_READ|FILE_SHARE_WRITE);
if(hTicksFile<1)
return;
// Çàïèñü ôàéëà
int total=ArraySize(g_ticks),i=0;
while(i<total)
{
if(FileWriteStruct(hTicksFile,g_ticks[i])==0)
{
Print("Îøèáêà ñîõðàíåíèÿ äàííûõ âî âðåìåííûé ôàéë...");
return;
}
i++;
}
FileClose(hTicksFile);
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Óäàëåíèå âñåõ îáúåêòîâ, ñîçäàííûõ ïðîãðàììîé |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void DeleteAllObjects()
{
for(int i=ObjectsTotal()-1; i>=0; i--)
if(StringSubstr(ObjectName(i),0,StringLen(PREFIX))==PREFIX)
ObjectDelete(ObjectName(i));
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Îòîáðàæåíèå ïðÿìîóãîëüíèêà |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void ShowTrendLine(datetime time1,double price1,datetime time2,double price2,string toolTip,color clr)
{
string name=PREFIX+"LINE_"+IntegerToString((int)MathRound(price1/g_point));
if(ObjectFind(0,name)<0)
{
ObjectCreate(0,name,OBJ_TREND,0,time1,price1,time2,price2);
ObjectSetInteger(0,name,OBJPROP_COLOR,clr);
ObjectSetInteger(0,name,OBJPROP_BACK,true);
ObjectSetInteger(0,name,OBJPROP_WIDTH,(int)i_lineWidth);
ObjectSetInteger(0,name,OBJPROP_RAY,false);
ObjectSetInteger(0,name,OBJPROP_HIDDEN,true);
ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false);
ObjectSetString(0,name,OBJPROP_TOOLTIP,toolTip);
return;
}
ObjectMove(0,name,0,time1,price1);
ObjectMove(0,name,0,time2,price2);
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Îòîáðàæåíèå îáúåêòà "Òåêñò" |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void ShowText(datetime time,double price,string text,color clr)
{
string name=PREFIX+"TEXT_"+IntegerToString((int)(price/g_point));
if(ObjectFind(0,name)<0)
{
ObjectCreate(0,name,OBJ_TEXT,0,time,price);
ObjectSetString(0,name,OBJPROP_FONT,FONT_NAME);
ObjectSetInteger(0,name,OBJPROP_FONTSIZE,FONT_SIZE);
ObjectSetInteger(0,name,OBJPROP_ANCHOR,ANCHOR_LEFT);
ObjectSetString(0,name,OBJPROP_TEXT,text);
ObjectSetString(0,name,OBJPROP_TOOLTIP,DoubleToString(price,Digits));
ObjectSetInteger(0,name,OBJPROP_COLOR,clr);
ObjectSetInteger(0,name,OBJPROP_BACK,false);
ObjectSetInteger(0,name,OBJPROP_HIDDEN,true);
ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false);
return;
}
ObjectMove(0,name,0,time,price);
ObjectSetInteger(0,name,OBJPROP_COLOR,clr);
ObjectSetString(0,name,OBJPROP_TEXT,text);
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Îòîáðàæåíèå îáúåêòà "Ïðÿìîóãîëüíèê" |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void ShowRectangle(datetime time1,double price1,datetime time2,double price2)
{
string name=PREFIX+VIEW_RANGE;
if(ObjectFind(0,name)<0)
{
ObjectCreate(0,name,OBJ_RECTANGLE,0,time1,price1,time2,price2);
ObjectSetInteger(0,name,OBJPROP_COLOR,i_viewRangeColor);
ObjectSetInteger(0,name,OBJPROP_BACK,false);
return;
}
ObjectMove(0,name,0,time1,price1);
ObjectMove(0,name,1,time2,price2);
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Îïðåäåëåíèå èíäåêñà áàðà, ñ êîòîðîãî íåîáõîäèìî ïðîèçâîäèòü ïåðåðàñ÷åò |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
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);
// Ïåðâîå îòîáðàæåíèå èíäèêàòîðà èëè ïðîèçîøëà ïîäêà÷êà äàííûõ, ò. å. íà ïðåäûäóùåì òèêå áàðîâ áûëî íå íà îäèí áàð ìåíüøå, êàê ïðè íîðìàëüíîì ðàçâèòèè èñòîðèè, à íà äâà èëè áîëåå áàðîâ ìåíüøå
if(prevCalculated<ratesTotal-1)
{
DeleteAllObjects();
return (total);
}
// Íîðìàëüíîå ðàçâèòèå èñòîðèè. Êîëè÷åñòâî áàðîâ òåêóùåãî òèêà îòëè÷àåòñÿ îò êîëè÷åñòâà áàðîâ ïðåäûäóùåãî òèêà íå áîëüøå, ÷åì íà îäèí áàð
return (MathMin(ratesTotal - prevCalculated, total));
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Ðàâíû ëè ÷èñëà? |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
bool IsValuesEquals(double first,double second)
{
return (MathAbs(first - second) < Point / 10);
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Çàïèñü äàííûõ î òèêå â ìàññèâ g_ticks |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
bool IsUpdateTicksArray(TickStruct &tick)
{
int total=ArraySize(g_ticks);
if(ArrayResize(g_ticks,total+1,100)<0)
{
Alert(WindowExpertName(),": èíäèêàòîðó íå õâàòàåò ïàìÿòè äëÿ ñîõðàíåíèÿ äàííûõ îá î÷åðåäíîì òèêå.");
return false;
}
g_ticks[total]=tick;
return true;
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Çàïèñü äàííûõ î òèêå â ìàññèâ g_levelsData |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
bool IsSaveTickData(TickStruct &curTick,TickStruct &prevTick)
{
// Ïðåîáðàçîâàíèå öåíû ê áëèæàéøåìó êëàñòåðó
double clusterPrice=CastPriceToCluster(curTick.bid);
// Ïîèñê òàêîé æå öåíû â ìàññèâå g_levelsData
int i=0;
int total=ArraySize(g_levelsData);
for(; i<total; i++)
if(IsValuesEquals(g_levelsData[i].price,clusterPrice))
break;
// Ïîõîæàÿ öåíà íàéäåíà
if(i<total)
{
g_levelsData[i].repeatCnt++;
SaveDeltaData(i,curTick.bid,prevTick.bid);
return true;
}
// Óêàçàííàÿ öåíà ÿâëÿåòñÿ íîâîé - ðàñøèðåíèå ìàññèâà
if(ArrayResize(g_levelsData,total+1)!=total+1)
{
Alert(WindowExpertName(),": èíäèêàòîðó íå õâàòàåò ïàìÿòè äëÿ êîððåêòíîé ðàáîòû.");
return false;
}
g_levelsData[total].price=clusterPrice;
g_levelsData[total].repeatCnt=1;
g_levelsData[i].bullsCnt = 0;
g_levelsData[i].bearsCnt = 0;
SaveDeltaData(i,curTick.bid,prevTick.bid);
return true;
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Ïðèâåäåíèå ðûíî÷íîé öåíû ê öåíå êëàñòåðà ñ ó÷åòîì åãî âûñîòû |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
double CastPriceToCluster(double price)
{
int priceInPoints=(int)MathRound(price/Point);
int clusterPrice =(int)MathRound(priceInPoints/1.0/i_pointsInBox);
return NormalizeDouble(clusterPrice * Point * i_pointsInBox, Digits);
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Çàïèñü äàííûõ î ðîñòå èëè ïàäåíèè öåíû â ïðåäåëàõ êëàñòåðà |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void SaveDeltaData(int index,double curBid,double prevBid)
{
if(curBid>prevBid)
g_levelsData[index].bullsCnt++;
if(curBid<prevBid)
g_levelsData[index].bearsCnt++;
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| ×òåíèå îäíîãî òèêà èç ôàéëà |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
bool IsReadTimeAndBidAskOfTick(int hTicksFile,TickStruct &tick)
{
if(FileIsEnding(hTicksFile))
return false;
uint bytesCnt=FileReadStruct(hTicksFile,tick);
return bytesCnt == sizeof(TickStruct);
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Ñ÷èòûâàíèå òèêîâ, ïðèíàäëåæàùèõ äåéñòâóþùåìó èíòåðâàëó îòîáðàæåíèÿ ãèñòîãðàììû |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
bool IsReadTicksFromFile(datetime &lastTime)
{
// Îáíóëåíèå òåêóùåé èñòîðèè
ArrayResize(g_levelsData,0);
// Îòêðûòèå ôàéëà òèêîâîé èñòîðèè
int hTicksFile=FileOpen(Symbol()+".tks",FILE_BIN|FILE_READ|FILE_SHARE_READ|FILE_SHARE_WRITE);
if(hTicksFile<1)
return true;
// ×òåíèå ôàéëà
TickStruct tick={0,0,0};
TickStruct prevTick;
bool result=true;
while(!IsStopped())
{
prevTick=tick;
bool fileClose=!IsReadTimeAndBidAskOfTick(hTicksFile,tick);
if(fileClose || tick.time==0)
break;
if(tick.time<g_viewRange.leftTime)
continue;
if(tick.time>g_viewRange.rightTime+PeriodSeconds())
break;
if(!IsSaveTickData(tick,prevTick))
{
result=false;
break;
}
}
FileClose(hTicksFile);
lastTime = tick.time; // Óêàçûâàåì ïîñëåäíþþ ïðî÷èòàííóþ äàòó, ÷òîáû äîïîëíèòü ïîëó÷åííûå äàííûå èç ëîêàëüíîãî áóôåðà..
// ..ïðè íåîáõîäèìîñòè
return result;
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| ×òåíèå äàííûõ î òèêàõ èç ëîêàëüíîãî áóôåðà, ÷òîáû íå ïîòåðÿòü òèêè, ïðèøåäøèå ïîñëå íà÷àëà ðàáîòû èíäèêàòîðà, íî íå ïîïàâøèå â ôàéë |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
bool ReadTicksFromBuffer(datetime lastTime)
{
// Äîïîëíåíèå íå òðåáóåòñÿ - ïðî÷èòàíû âñå òèêè
if(lastTime>g_viewRange.rightTime)
return true;
// Âîçìîæíî, äàííûå èç ãëàâíîãî òèêîâîãî ôàéëà çàêàí÷èâàþòñÿ äî ìîìåíòà íà÷àëà èíòåðâàëà îòîáðàæåíèÿ ãèñòîãðàììû
lastTime=(int)MathMax(lastTime,g_viewRange.leftTime);
// Ïîèñê èíäåêñà äëÿ g_ticks, ñ êîòîðîãî íåîáõîäèìî ïðîäîëæèòü ÷òåíèå äàííûõ
int total=ArraySize(g_ticks);
int i=0;
while(i<total && lastTime>=g_ticks[i].time)
i++;
// Îñóùåñòâëåíèå äîïîëíåíèÿ
datetime timeTotal=g_viewRange.rightTime+PeriodSeconds();
TickStruct prevTick={0,0,0};
if(i>0)
prevTick=g_ticks[i-1];
while(i<total && g_ticks[i].time<timeTotal)
{
if(!IsSaveTickData(g_ticks[i],prevTick))
return false;
prevTick=g_ticks[i];
i++;
}
return true;
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Îïðåäåëåíèå ìàêñèìàëüíîãî çíà÷åíèÿ òèêîâîãî îáúåìà â ñðåäè íàéäåííûõ êëàñòåðîâ |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
int GetMaxTickVolume()
{
int max=0;
int total = ArraySize(g_levelsData);
for(int i = 0; i < total; i++)
if(g_levelsData[i].repeatCnt>max)
max=g_levelsData[i].repeatCnt;
return max;
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Îòîáðàæåíèå ãèñòîãðàììû |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void ShowHistogramm()
{
DeleteHistogramm();
int maxVolume= GetMaxTickVolume();
if(maxVolume == 0)
return;
int total = ArraySize(g_levelsData);
for(int i = 0; i < total; i++)
{
int volumeLevel=GetVolumeLevel(g_levelsData[i].repeatCnt);
if(volumeLevel<0)
continue;
// Îòîáðàæåíèå ãèñòîãðàììû îáúåìà
string price="Êëàñòåð: "+DoubleToString(g_levelsData[i].price,Digits);
datetime histRightTime=GetHistRightTime(g_levelsData[i].repeatCnt,maxVolume);
ShowTrendLine(g_viewRange.leftTime,g_levelsData[i].price,histRightTime,g_levelsData[i].price,price+". Ëèíèÿ îáúåìà",g_volumeLevelsColor[volumeLevel].levelColor);
ShowText(g_viewRange.rightTime,g_levelsData[i].price," "+IntegerToString(g_levelsData[i].repeatCnt),g_volumeLevelsColor[volumeLevel].levelColor);
// Îòîáðàæåíèå ãèñòîãðàìì ðîñòà è ïàäåíèÿ öåíû
if(i_isShowDelta==NO)
continue;
histRightTime=GetHistRightTime(g_levelsData[i].bearsCnt,maxVolume);
ShowTrendLine(g_viewRange.leftTime,g_levelsData[i].price+g_pointMultiply*Point,histRightTime,g_levelsData[i].price+g_pointMultiply*Point,
price+". Îáúåì ïàäåíèÿ: "+IntegerToString(g_levelsData[i].bearsCnt),i_bearDeltaColor);
histRightTime=GetHistRightTime(g_levelsData[i].bullsCnt,maxVolume);
ShowTrendLine(g_viewRange.leftTime,g_levelsData[i].price+2*g_pointMultiply*Point,histRightTime,g_levelsData[i].price+2*g_pointMultiply*Point,
price+". Îáúåì ðîñòà: "+IntegerToString(g_levelsData[i].bullsCnt),i_bullDeltaColor);
}
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Óäàëåíèå âñåõ îáúåêòîâ, ñîñòàâëÿþùèõ ãèñòîãðàììó |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void DeleteHistogramm()
{
for(int i=ObjectsTotal()-1; i>=0; i--)
{
string name=ObjectName(i);
if(StringSubstr(name,0,StringLen(PREFIX+"LINE_"))==PREFIX+"LINE_" ||
StringSubstr(name,0,StringLen(PREFIX+"TEXT_"))==PREFIX+"TEXT_")
ObjectDelete(name);
}
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Îïðåäåëåíèå, êàêîìó èç óêàçàííûõ îáúåìîâ ñîîòâåòñòâóåò ðàññìàòðèâàåìàÿ âåëè÷èíà îáúåìà |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
int GetVolumeLevel(int ticksVolume)
{
for(int i=0; i<MAX_VOLUMES_SHOW; i++)
if(g_volumeLevelsColor[i].levelMinVolume>ticksVolume)
return i - 1;
return MAX_VOLUMES_SHOW - 1;
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Âû÷èñëåíèå âðåìåíè áàðà, íà êîòîðîì äîëæíà çàêàí÷èâàòüñÿ ëèíèÿ ãèñòîãðàììû ñ óêàçàííûì òèêîâûì îáúåìîì |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
datetime GetHistRightTime(int tickVolume,int maxVolume)
{
int barsIndex=(int)(g_viewRange.rightIndex+(g_viewRange.rangeDuration-1) *(1-tickVolume/1.0/maxVolume));
if(barsIndex<0 || barsIndex>=Bars)
return g_viewRange.rightTime;
return Time[barsIndex];
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Ïîääåðæêà èíòåðâàëà îòîáðàæåíèÿ ãèñòîãðàììû îáúåìîâ |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void ShowViewRange(int limit)
{
// Åñëè ïðîèçîøåë ïåðåõîä ê íîâîìó áàðó, íî èíòåðâàë ïîçèöèîíèðóåòñÿ íà áàðàõ 0 è âûøå
if(limit>0 && g_viewRange.rightIndex>=0)
{
if(g_viewRange.rightTime != Time[g_viewRange.rightIndex])
g_viewRange.rightIndex = iBarShift(NULL, 0, g_viewRange.rightTime);
}
else
// Âîçìîæíî, ïðîèçîøåë ïåðåõîä ê ñëåäóþùåìó áàðó ïðè ïîçèöèîíèðîâàíèè èíòåðâàëà íà íóëåâîì áàðå
if(g_viewRange.rightIndex==-1 && g_viewRange.rightTime!=Time[0]+PeriodSeconds())
MoveRangeToNullBar();
DefineCoordsAndShowViewRange();
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Ïåðåìåùåíèå èíòåðâàëà îòîáðàæåíèÿ ãèñòîãðàììû íà íîâûé "íóëåâîé" áàð |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void MoveRangeToNullBar()
{
g_viewRange.rightTime=Time[0]+PeriodSeconds();
int leftIndex=iBarShift(NULL,0,g_viewRange.leftTime);
g_viewRange.rangeDuration=leftIndex+2;
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Îïðåäåëåíèå êîîðäèíàò îòîáðàæåíèÿ èíòåðâàëà ãèñòîãðàììû |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void DefineCoordsAndShowViewRange()
{
// Îïðåäåëåíèå êîîðäèíàò èíòåðâàëà
double rightPrice=0,leftPrice=0;
g_viewRange.rightIndex=(int)MathMin(MathMax(g_viewRange.rightIndex,-1),Bars-1);
g_viewRange.rangeDuration = (int)MathMax(MathMin(g_viewRange.rangeDuration, Bars - g_viewRange.rightIndex - 2), 1);
if(g_viewRange.rightIndex == -1)
{
g_viewRange.rangeDuration=(int)MathMax(g_viewRange.rangeDuration,2);
rightPrice= CastPriceToCluster(High[iHighest(NULL,0,MODE_HIGH,g_viewRange.rangeDuration-1)])+2 * g_pointMultiply * Point;
leftPrice = CastPriceToCluster(Low[iLowest(NULL,0,MODE_LOW,g_viewRange.rangeDuration-1)]);
}
else
{
rightPrice= CastPriceToCluster(High[iHighest(NULL,0,MODE_HIGH,g_viewRange.rangeDuration,g_viewRange.rightIndex)])+2 * g_pointMultiply * Point;
leftPrice = CastPriceToCluster(Low[iLowest(NULL,0,MODE_LOW,g_viewRange.rangeDuration,g_viewRange.rightIndex)]);
}
// Îòîáðàæåíèå èíòåðâàëà
ShowRectangle(g_viewRange.rightTime,rightPrice,g_viewRange.leftTime,leftPrice);
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Îñóùåñòâëåíèå ÷òåíèÿ äàííûõ èç ôàéëà è ïîëíîå îòîáðàæåíèå ãèñòîãðàììû |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void ShowNewData()
{
// ×òåíèå äàííûõ èç ôàéëà
datetime lastTime=0;
if(!IsReadTicksFromFile(lastTime))
{
g_activate=false;
return;
}
// Äîïîëíåíèå äàííûõ èç ëîêàëüíîãî áóôåðà
if(!ReadTicksFromBuffer(lastTime))
{
g_activate=false;
return;
}
ShowHistogramm();
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Îáíîâëåíèå äàííûõ ïî êëàñòåðàì |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void UpdateData()
{
TickStruct tick;
tick.time= TimeCurrent();
tick.ask = Ask;
tick.bid = Bid;
// Äîáàâëåíèå îäíîãî òèêà â ìàññèâ õðàíåíèÿ òèêîâ
if(!IsUpdateTicksArray(tick))
{
g_activate=false;
return;
}
// Äàííûå â ìàññèâ êëàñòåðîâ íå äîáàâëÿþòñÿ, åñëè ïðàâàÿ ãðàíèöà ïðÿìîóãîëüíèêà íàõîäèòñÿ íà ïåðâîì áàðå èëè äàëåå
if(g_viewRange.rightIndex>0)
return;
// Äîáàâëåíèå òèêà â ìàññèâ êëàñòåðîâ
TickStruct prevTick={0,0,0};
int lastTickIndex=ArraySize(g_ticks)-1;
if(lastTickIndex>0)
prevTick=g_ticks[lastTickIndex-1];
if(!IsSaveTickData(tick,prevTick))
{
g_activate=false;
return;
}
DefineCoordsAndShowViewRange();
ShowHistogramm();
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Îòîáðàæåíèå äàííûõ èíäèêàòîðà |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void ShowIndicatorData(int limit,int total)
{
if(limit>0)
{
ShowNewData();
return;
}
UpdateData();
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Ïðîâåðêà ïðàâèëüíîñòè äàííûõ ïî êîîðäèíàòàì îáúåêòà è óñòàíîâêà íîâûõ ãðàíèö èíòåðâàëà |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void ValidateDataAndSetRange()
{
// Ïîëó÷åíèå êîîðäèíàò îáúåêòà
datetime rightTime=(datetime)ObjectGetInteger(0,PREFIX+VIEW_RANGE,OBJPROP_TIME1);
datetime leftTime =(datetime)ObjectGetInteger(0,PREFIX+VIEW_RANGE,OBJPROP_TIME2);
if(rightTime<leftTime)
{
leftTime=rightTime;
rightTime=(datetime)ObjectGetInteger(0,PREFIX+VIEW_RANGE,OBJPROP_TIME2);
}
// Ïðîâåðêà ïðàâèëüíîñòè óñòàíîâêè îáúåêòà
if(rightTime>Time[0]+PeriodSeconds())
{
rightTime=Time[0]+PeriodSeconds();
if(leftTime>Time[0])
leftTime=Time[VIEWRANGE_STARTBAR];
}
// Óñòàíîâêà íîâûõ êîîðäèíàò
g_viewRange.rightTime= rightTime;
g_viewRange.leftTime = leftTime;
if(g_viewRange.rightTime <= Time[0])
g_viewRange.rightIndex = iBarShift(NULL, 0, rightTime);
else
g_viewRange.rightIndex=-1;
int leftIndex=iBarShift(NULL,0,leftTime);
g_viewRange.rangeDuration=leftIndex-g_viewRange.rightIndex+1;
// Îòîáðàæåíèå èíòåðâàëà
DefineCoordsAndShowViewRange();
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Îáðàáîòêà ñîáûòèÿ ïåðåìåùåíèÿ îáúåêòà |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
{
if(id!=CHARTEVENT_OBJECT_DRAG)
return;
if(sparam!=PREFIX+VIEW_RANGE)
return;
// Ïåðåìåùåí îáúåêò, óêàçûâàþùèé ãðàíèöû èíòåðâàëà îòîáðàæåíèÿ ãèñòîãðàììû
ValidateDataAndSetRange();
ShowNewData();
WindowRedraw();
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| 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); // Ñ êàêîãî áàðà íà÷èíàòü îáíîâëåíèå?
ShowViewRange(limit); // Íà÷àëüíîå îòîáðàæåíèå è ïîääåðæêà ïðàâèëüíîãî îòîáðàæåíèÿ îáëàñòè ïîêàçà ãèñòîãðàììû
ShowIndicatorData(limit, total); // Îòîáðàæåíèå äàííûõ èíäèêàòîðà
WindowRedraw();
return rates_total;
}
//+------------------------------------------------------------------+
Comments