clusterbox_ad
Download scriptThis script is designed to provide a visual representation of price action by grouping individual market "ticks" into clusters. Instead of just showing you the final price of a candle, it helps you visualize the density of trading activity at specific price levels.
Core Functionality
The logic revolves around identifying where the most "action" happened during a specific time period. It looks at every single price change (tick) and categorizes them into "bins" or clusters based on a user-defined point size. If many ticks occur at the same price level within that span, it identifies that area as a high-activity cluster.
How the Logic Works:
- Data Collection & Storage: The script maintains a history of market movements. To ensure no data is lost when you close your trading platform, it includes a system to save and load this tick data from a temporary file on your computer.
- Cluster Mapping: It takes the price of every tick and rounds it to the nearest "cluster" size (for example, grouping every 50 points together). This allows the script to count how many times the market touched specific price zones.
- Dynamic Scaling: As a new candle forms or the current price moves to a new high or low, the script dynamically adjusts the visual map. It expands the "grid" of clusters to accommodate the new price range while keeping the counts accurate for the existing data.
- Visual Feedback:
- Color Coding: The script allows you to set different thresholds for volume. Depending on how many ticks occur in a specific cluster, the script will change the color of that data point (e.g., a low-activity area might be blue, while a high-activity "hot spot" might turn magenta).
- Histogram View: It displays these counts as a histogram or text labels directly on your chart, making it easy to see exactly where the most aggressive trading occurred.
- Grid System: Optionally, it can draw a horizontal grid across the chart to help you see the boundaries of these price clusters clearly.
- User Interaction: There is a dedicated "On/Off" button on the chart that allows you to toggle the visibility of these clusters instantly without having to remove the script from your chart.
Summary for the User
In simple terms, this tool acts like a "heat map" for price. It tells you not just where the price went, but where it spent the most time. By identifying clusters with high tick counts, traders can better see areas of heavy interest, support/resistance zones, and where significant "buying or selling pressure" was concentrated during a specific session.
//+------------------------------------------------------------------+
//| clusterbox_ad.mq4 |
//| Copyright 2015, Scriptong |
//| http://advancetools.net |
//+------------------------------------------------------------------+
#property copyright "Scriptong"
#property link "http://advancetools.net"
#property description "English: Displays the ticks volume of candles in the form of clusters.\nRussian: Îòîáðàæåíèå òèêîâûõ îáúåìîâ ñâå÷è â âèäå êëàñòåðîâ."
#property strict
#property indicator_chart_window
#property indicator_buffers 1
#define MAX_POINTS_IN_CANDLE 30000 // Ïðèáðîñêà äëÿ ñâå÷åé ìåñÿ÷íîãî ãðàôèêà ïÿòèçíàêà
#define MAX_TICKS_IN_CANDLE 1000000 // Ïðèáðîñêà äëÿ ñâå÷åé ìåñÿ÷íîãî ãðàôèêà ïÿòèçíàêà
#define MAX_VOLUMES_SHOW 5 // Êîëè÷åñòâî óðîâíåé ìàêñèìàëüíîãî îáúåìà, êîòîðûå ñëåäóåò îòîáðàæàòü
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
struct LevelVolumeColor // Ñòðóêòóðà ñîîòâåòñòâèÿ óðîâíåé îáúåìà, äîñòèæåíèå êîòîðûõ íà öåíîâîì óðîâíå îòîáðàæàåòñÿ..
{ // ..ñîîòâåòñòâóþùèì öâåòîì
color levelColor;
int levelMinVolume;
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
struct TickStruct // Ñòðóêòóðà äëÿ çàïèñè äàííûõ îá îäíîì òèêå
{
datetime time;
double bid;
double ask;
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
enum ENUM_YESNO
{
YES, // Yes / Äà
NO // No / Íåò
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
enum ENUM_CHARTSCALE
{
SCALE_SMALLER, // Smallest / Íàèìåíüøèé
SCALE_SMALL, // Small / Ìàëûé
SCALE_MEDIUM, // Medium / Ñðåäíèé
SCALE_BIG, // Big / Áîëüøîé
SCALE_BIGGEST, // Greater / Áîëüøèé
SCALE_LARGE // Biggest / Íàèáîëüøèé
};
//--- Íàñòðîå÷íûå ïàðàìåòðû èíäèêàòîðà
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 = 250; // Minimal volume. Level 2 / Ìèíèìàëüíûé îáúåì. Óðîâåíü 2
input color i_colorLevel2 = clrTurquoise; // Color of level 2 / Öâåò óðîâíÿ 2
input int i_minVolumeLevel3 = 500; // Minimal volume. Level 3 / Ìèíèìàëüíûé îáúåì. Óðîâåíü 3
input color i_colorLevel3 = clrRoyalBlue; // Color of level 3 / Öâåò óðîâíÿ 3
input int i_minVolumeLevel4 = 1000; // Minimal volume. Level 4 / Ìèíèìàëüíûé îáúåì. Óðîâåíü 4
input color i_colorLevel4 = clrBlue; // Color of level 4 / Öâåò óðîâíÿ 4
input int i_minVolumeLevel5 = 2000; // Minimal volume. Level 5 / Ìèíèìàëüíûé îáúåì. Óðîâåíü 5
input color i_colorLevel5 = clrMagenta; // Color of level 5 / Öâåò óðîâíÿ 5
input string i_string2 = "Ïàðàìåòðû ãðàôèêà"; // ==============================================
input ENUM_YESNO i_useNeededScale = YES; // Use the specific chart scale? / Çàäàòü ìàñøòàá ãðàôèêà?
input ENUM_CHARTSCALE i_chartScale = SCALE_LARGE; // Chart scale / Ìàñøòàá
input ENUM_YESNO i_showClusterGrid = YES; // Display the cluster grid / Ïîêàçûâàòü ñåòêó êëàñòåðîâ
input color i_gridColor = clrDarkGray; // Color of clusters lines / Öâåò ëèíèé êëàñòåðîâ
input int i_indBarsCount=10000; // Number of bars to display / Êîë-âî áàðîâ îòîáðàæåíèÿ
//--- Ïðî÷èå ãëîáàëüíûå ïåðåìåííûå èíäèêàòîðà
bool g_activate, // Ïðèçíàê óñïåøíîé èíèöèàëèçàöèè èíäèêàòîðà
g_isShowInfo, // Ïðèçíàê íåîáõîäèìîñòè îòîáðàæåíèÿ äàííûõ èíäèêàòîðà
g_chartForeground, // Ïðèçíàê íàõîæäåíèÿ ñâå÷åé íà ïåðåäíåì ïëàíå
g_init; // Ïåðåìåííàÿ äëÿ èíèöèàëèçàöèè ñòàòè÷åñêèõ ïåðåìåííûõ âíóòðè ôóíêöèé â ìîìåíò ïðîâåäåíèÿ..
// ..ïîâòîðíîé èíèöèàëèçàöèè
int g_currentScale,// Ìàñøòàá ãðàôèêà äåéñòâóþùèé íà ìîìåíò ïðèñîåäèíåíèÿ èíäèêàòîðà
g_volumePriceArray[MAX_POINTS_IN_CANDLE]; // Ðàáî÷èé ìàññèâ óðîâíåé, â êîòîðûé çàïèñûâàåòñÿ êîëè÷åñòâî òèêîâ, êîòîðûå ïîïàëè íà..
// ..ñîîòâåòñòâóþùóþ öåíó ñâå÷è. Êîëè÷åñòâî çàïîëíåííûõ ýëåìåíòîâ ìàññèâà - âûñîòà ñâå÷è
double g_ticksPrice[MAX_TICKS_IN_CANDLE]; // Ìàññèâ äëÿ âðåìåííîãî õðàíåíèÿ íàáîðà òèêîâ, ïðèõîäÿùèõñÿ íà îäíó ñâå÷ó
double g_point,
g_tickSize;
TickStruct g_ticks[]; // Ìàññèâ äëÿ õðàíåíèÿ òèêîâ, ïîñòóïèâøèõ ïîñëå íà÷àëà ðàáîòû èíäèêàòîðà
LevelVolumeColor g_volumeLevelsColor[MAX_VOLUMES_SHOW]; // Ìàññèâ îáúåìîâ è, ñîîòâåòñòâóþùèì èì, öâåòîâ óðîâíåé
#define PREFIX "CLSTRBX_" // Ïðåôèêñ ãðàôè÷åñêèõ îáúåêòîâ, îòîáðàæàåìûõ èíäèêàòîðîì
#define SIGN_BUTTON "INFO_BUTTON_" // Êîðåíü èìåíè ãðàôè÷åñêîãî îáúåêòà "êíîïêà"
#define BUTTON_FONT_NAME "MS Sans Serif" // Èìÿ øðèôòà äëÿ îòîáðàæåíèÿ òåêñòà êíîïêè
#define BUTTON_TOOLTIP "Âêë/âûêë îòîáðàæåíèå êëàñòåðîâ è ñåòêè" // Ïîäñêàçêà ê íàçíà÷åíèþ êíîïêè
#define BUTTON_XCOORD 2 // Õ-êîîðäèíàòà ëåâîãî âåðõíåãî óãëà êíîïêè
#define BUTTON_YCOORD 14 // Y-êîîðäèíàòà ëåâîãî âåðõíåãî óãëà êíîïêè
#define BUTTON_WIDTH 110 // Øèðèíà êíîïêè
#define BUTTON_HEIGHT 20 // Âûñîòà êíîïêè
#define BUTTON_FONT_SIZE 7 // Ðàçìåð øðèôòà äëÿ òåêñòà êíîïêè
#define BUTTON_TEXT_COLOR clrBlack // Öâåò øðèôòà òåêñòà â êíîïêå
#define BUTTON_BORDER_COLOR clrNONE // Öâåò ãðàíèöû êíîïêè
#define BUTTON_BACKGROUND_COLOR clrLightGray // Öâåò çàëèâêè êíîïêè
#define FONT_NAME "MS Sans Serif"
#define FONT_SIZE 7
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Custom indicator initialization function |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
int OnInit()
{
g_activate=false; // Èíäèêàòîð íå èíèöèàëèçèðîâàí
g_init=true;
if(!IsTuningParametersCorrect()) // Íåâåðíî óêàçàííûå çíà÷åíèÿ íàñòðîå÷íûõ ïàðàìåòðîâ - ïðè÷èíà íåóäà÷íîé èíèöèàëèçàöèè
return INIT_FAILED;
if(!IsLoadTempTicks()) // Çàãðóçêà äàííûõ î òèêàõ, ñîõðàíåííûõ çà ïðåäûäóùèé ïåðèîä ðàáîòû èíäèêàòîðà
return INIT_FAILED;
CreateVolumeColorsArray(); // Êîïèðîâàíèå äàííûõ î öâåòå è âåëè÷èíå óðîâíåé â ìàññèâ
SetChartView(); // Óñòàíîâêà ñïåöèôè÷åñêîãî âèäà ãðàôèêà
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);
}
if(i_pointsInBox<1)
{
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 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;
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Óñòàíîâêà íóæíîãî ìàñøòàáà ãðàôèêà äëÿ ðàáîòû èíäèêàòîðà |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void SetChartView()
{
//--- Ïîëîæåíèå ñâå÷íîãî ãðàôèêà îòíîñèòåëüíî ïðî÷åé ãðàôèêè
g_chartForeground=(bool)ChartGetInteger(0,CHART_FOREGROUND);
ChartSetInteger(0,CHART_FOREGROUND,false);
if(i_useNeededScale==NO)
return;
//--- Ìàñøòàá ãðàôèêà
g_currentScale=(int)ChartGetInteger(0,CHART_SCALE);
ChartSetInteger(0,CHART_SCALE,(long)i_chartScale);
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(!IsSavedFile()) // Åñëè íè îäèí èç ïîäêëþ÷åííûõ èíäèêàòîðîâ íå ñîõðàíèë äàííûå, òî èõ ñîõðàíèò òåêóùèé èíäèêàòîð
SaveTempTicks(); // Ñîõðàíåíèå äàííûõ î òèêàõ, íàêîïëåííûõ çà òåêóùèé ïåðèîä ðàáîòû èíäèêàòîðà
DeleteAllObjects();
RestoreChartView();
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Ïðîâåðêà íàëè÷èÿ çàïèñàííûõ äàííûõ äðóãèì èíäèêàòîðîì |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
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 ShowInfoViewButton()
{
if(!g_init)
return;
g_isShowInfo=true;
ShowButton(BUTTON_XCOORD,BUTTON_YCOORD,"Êëàñòåðû âûêë.");
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Îòîáðàæåíèå ãðàôè÷åñêîãî îáúåêòà "Êíîïêà" |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void ShowButton(int x,int y,string text)
{
string name=PREFIX+SIGN_BUTTON+IntegerToString(x)+IntegerToString(y);
if(ObjectFind(0,name)<0)
{
ObjectCreate(0,name,OBJ_BUTTON,0,0,0);
ObjectSetInteger(0,name,OBJPROP_CORNER,0);
ObjectSetInteger(0,name,OBJPROP_XDISTANCE,x);
ObjectSetInteger(0,name,OBJPROP_YDISTANCE,y);
ObjectSetInteger(0,name,OBJPROP_XSIZE,BUTTON_WIDTH);
ObjectSetInteger(0,name,OBJPROP_YSIZE,BUTTON_HEIGHT);
ObjectSetString(0,name,OBJPROP_TEXT,text);
ObjectSetString(0,name,OBJPROP_FONT,BUTTON_FONT_NAME);
ObjectSetString(0,name,OBJPROP_TOOLTIP,BUTTON_TOOLTIP);
ObjectSetInteger(0,name,OBJPROP_FONTSIZE,BUTTON_FONT_SIZE);
ObjectSetInteger(0,name,OBJPROP_COLOR,BUTTON_TEXT_COLOR);
ObjectSetInteger(0,name,OBJPROP_BORDER_COLOR,BUTTON_BORDER_COLOR);
ObjectSetInteger(0,name,OBJPROP_BGCOLOR,BUTTON_BACKGROUND_COLOR);
ObjectSetInteger(0,name,OBJPROP_BACK,false);
ObjectSetInteger(0,name,OBJPROP_HIDDEN,true);
ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false);
return;
}
ObjectSetInteger(0,name,OBJPROP_XDISTANCE,x);
ObjectSetInteger(0,name,OBJPROP_YDISTANCE,y);
ObjectSetString(0,name,OBJPROP_TEXT,text);
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Óäàëåíèå âñåõ îáúåêòîâ, ñîçäàííûõ ïðîãðàììîé |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void DeleteAllObjects()
{
for(int i=ObjectsTotal()-1; i>=0; i--)
if(StringSubstr(ObjectName(i),0,StringLen(PREFIX))==PREFIX)
ObjectDelete(ObjectName(i));
g_init=true;
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Âîçâðàùåíèå äåéñòâóþùåãî ìàñøòàáà ãðàôèêà |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void RestoreChartView()
{
ChartSetInteger(0,CHART_FOREGROUND,g_chartForeground);
if(i_useNeededScale==NO)
return;
ChartSetInteger(0,CHART_SCALE,g_currentScale);
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Îïðåäåëåíèå èíäåêñà áàðà, ñ êîòîðîãî íåîáõîäèìî ïðîèçâîäèòü ïåðåðàñ÷åò |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
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 IsFirstMoreThanSecond(double first,double second)
{
return (first - second > Point / 10);
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Ðàâíû ëè ÷èñëà? |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
bool IsValuesEquals(double first,double second)
{
return (MathAbs(first - second) < Point / 10);
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| ×òåíèå îäíîãî òèêà èç ôàéëà |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
bool IsReadTimeAndBidAskOfTick(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;
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Ïðèâåäåíèå ðûíî÷íîé öåíû ê öåíå êëàñòåðà ñ ó÷åòîì åãî âûñîòû |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
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 ReadTicksFromFile(int hTicksFile,datetime limitTime,TickStruct &tick,int &ticksCount,bool &fileClose)
{
while(!fileClose)
{
fileClose=!IsReadTimeAndBidAskOfTick(hTicksFile,tick);
if(tick.time>=limitTime || fileClose || tick.time==0)
break;
g_ticksPrice[ticksCount]=CastPriceToCluster(tick.bid);
ticksCount++;
if(ticksCount>MAX_TICKS_IN_CANDLE)
break;
}
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Ðàñïðåäåëåíèå òèêîâ ïî êëàñòåðàì |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void SortTicksByCluster(int ticksCount,int &arraySize)
{
arraySize=1;
ArrayInitialize(g_volumePriceArray,0);
g_volumePriceArray[0]=1;
for(int i=1; i<ticksCount; i++)
{
if(!IsValuesEquals(g_ticksPrice[i-1],g_ticksPrice[i]))
{
arraySize+=(int)MathRound((g_ticksPrice[i]-g_ticksPrice[i-1])/g_tickSize);
if(arraySize>MAX_POINTS_IN_CANDLE)
break;
}
g_volumePriceArray[arraySize-1]++;
}
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| ×òåíèå òèêîâûõ äàííûõ èç áóôåðà òèêîâ |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void AddDataFromBuffer(datetime limitTime,TickStruct &tick,int &ticksCount)
{
//--- Ïîèñê â áóôåðå òèêà, âðåìÿ êîòîðîãî áîëüøå ïîñëåäíåãî ñ÷èòàííîãî òèêà
int total=ArraySize(g_ticks),i=0;
while(i<total && tick.time>=g_ticks[i].time)
i++;
//--- Äîñòèãëè êîíöà áóôåðà - óõîäèì
if(i>=total)
{
tick.time=0; // Óêàçàíèå öèêëó while â ôóíêöèè ProcessOldCandles íà òî, ÷òî äàííûå â áóôåðå çàêîí÷èëèñü
return;
}
//--- Ïåðåçàïèñü äàííûõ èç îäíîãî áóôåðà â äðóãîé
while(i<total && g_ticks[i].time<limitTime)
{
g_ticksPrice[ticksCount]=CastPriceToCluster(g_ticks[i].bid);
ticksCount++;
i++;
}
//--- Ñîõðàíåíèå äàííûõ î òèêå ñëåäóþùåãî áàðà
if(i<total)
tick=g_ticks[i];
else
tick.time=0; // Óêàçàíèå öèêëó while â ôóíêöèè ProcessOldCandles íà òî, ÷òî äàííûå â áóôåðå çàêîí÷èëèñü
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Ïîäãîòîâêà äàííûõ äëÿ îäíîãî áàðà |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void FormDataForOneBar(int hTicksFile,datetime limitTime,TickStruct &tick,double &lowPrice,int &arraySize,bool &fileClose)
{
//--- Ñ÷èòûâàíèå òèêîâ, ïðèíàäëåæàùèõ îäíîé ñâå÷å
int ticksCount=1;
g_ticksPrice[0]=CastPriceToCluster(tick.bid);
if(!fileClose)
ReadTicksFromFile(hTicksFile,limitTime,tick,ticksCount,fileClose);
if(fileClose) // Ýòî íå îøèáêà - else íå íóæåí, ò. ê. ïîñëå âûïîëíåíèÿ ReadTicksFromFile ìîæåò èçìåíèòüñÿ fileClose
AddDataFromBuffer(limitTime,tick,ticksCount);
//--- Ñîðòèðîâêà ìàññèâà â ïîðÿäêå âîçðàñòàíèÿ. Ïîñëå íåå íóëåâîé ýëåìåíò ñîäåðæèò ìèíèìóì ñâå÷è, à ýëåìåíò [ticksCount - 1] - ìàêñèìóì
ArraySort(g_ticksPrice,ticksCount);
lowPrice=g_ticksPrice[0];
//--- Ðàñïðåäåëåíèå òèêîâ ïî êëàñòåðàì
SortTicksByCluster(ticksCount,arraySize);
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Îòîáðàæåíèå ãîðèçîíòàëüíîé ëèíèè |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void ShowHLine(double price,color clr)
{
string name=PREFIX+"HLINE_"+IntegerToString((int)(price/g_point));
if(ObjectFind(0,name)<0)
{
ObjectCreate(0,name,OBJ_HLINE,0,0,price);
ObjectSetInteger(0,name,OBJPROP_COLOR,clr);
ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_DOT);
ObjectSetInteger(0,name,OBJPROP_BACK,true);
ObjectSetInteger(0,name,OBJPROP_HIDDEN,true);
ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false);
return;
}
ObjectMove(0,name,0,1,price);
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Îòîáðàæåíèå îáúåêòà "Òåêñò" |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void ShowText(int index,datetime time,double price,string text,string toolTip,color clr)
{
string name=PREFIX+IntegerToString(time)+IntegerToString(index);
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);
ObjectSetString(0,name,OBJPROP_TEXT,text);
ObjectSetString(0,name,OBJPROP_TOOLTIP,toolTip);
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);
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Îïðåäåëåíèå, êàêîìó èç óêàçàííûõ îáúåìîâ ñîîòâåòñòâóåò ðàññìàòðèâàåìàÿ âåëè÷èíà îáúåìà |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
int GetVolumeLevel(int index)
{
for(int i=0; i<MAX_VOLUMES_SHOW; i++)
if(g_volumeLevelsColor[i].levelMinVolume>g_volumePriceArray[index])
return i - 1;
return MAX_VOLUMES_SHOW - 1;
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Îòîáðàæåíèå ãèñòîãðàìì îäíîãî áàðà |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void ShowBarHistogramms(int barIndex,double lowPrice,int arraySize)
{
if(!g_isShowInfo)
return;
for(int i=0; i<arraySize; i+=i_pointsInBox)
{
//--- ßâëÿåòñÿ ëè îáúåì óðîâíÿ äîñòàòî÷íî áîëüøèì?
int volumeLevel=GetVolumeLevel(i);
if(volumeLevel<0)
continue;
//--- Îòîáðàæåíèå îáúåìîâ
double price=lowPrice+i*g_tickSize;
ShowText(i,Time[barIndex],price,IntegerToString(g_volumePriceArray[i]),DoubleToString(price,Digits),g_volumeLevelsColor[volumeLevel].levelColor);
}
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Îòîáðàæåíèå äàííûõ ïî èñòîðè÷åñêèì áàðàì, íà÷èíàÿ ñ óêàçàííîãî |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void ProcessOldCandles(int limit,double &lowPrice,int &arraySize)
{
//--- Îòêðûòèå ôàéëà òèêîâîé èñòîðèè
bool fileClose = false;
int hTicksFile = FileOpen(Symbol() + ".tks", FILE_BIN | FILE_READ | FILE_SHARE_READ | FILE_SHARE_WRITE);
if(hTicksFile<1)
fileClose=true;
//--- Ïîèñê ïåðâîãî òèêà, ïðèíàäëåæàùåãî áàðó limit èëè ëþáîìó áîëåå ïîçäíåìó áàðó
TickStruct tick;
tick.time= Time[limit];
tick.bid = Open[limit];
while(!IsStopped() && !fileClose)
{
if(!IsReadTimeAndBidAskOfTick(hTicksFile,tick))
return;
if(tick.time>=Time[limit])
break;
}
//--- Îòîáðàæåíèå äàííûõ
datetime extremeTime=Time[0]+PeriodSeconds();
while(tick.time<extremeTime && tick.time!=0)
{
int barIndex=iBarShift(NULL,0,tick.time);
FormDataForOneBar(hTicksFile,Time[barIndex]+PeriodSeconds(),tick,lowPrice,arraySize,fileClose);
ShowBarHistogramms(barIndex,lowPrice,arraySize);
}
if(!fileClose)
FileClose(hTicksFile);
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Îáðàçîâàíèå íîâîãî áàðà |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void ProcessNewBarForming(double bid,double &lowPrice,int &arraySize)
{
ArrayInitialize(g_volumePriceArray,0);
arraySize= 1;
lowPrice = bid;
g_volumePriceArray[0]=1;
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Îáíîâëåíèå ìèíèìóìà òåêóùåé ñâå÷è |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void ProcessCandleMinimumUpdate(int priceIndex,double bid,double &lowPrice,int &arraySize)
{
priceIndex = MathAbs(priceIndex);
arraySize += priceIndex;
if(arraySize>MAX_POINTS_IN_CANDLE)
return;
//--- Óâåëè÷åíèå êîëè÷åñòâà çíà÷èìûõ ýëåìåíòîâ ìàññèâà íà priceIndex ýëåìåíòîâ
for(int i=arraySize-1; i>priceIndex-1; i--)
g_volumePriceArray[i]=g_volumePriceArray[i-priceIndex];
//--- Çàïîëíåíèå íóëÿìè ýëåìåíòîâ, ñîîòâåòñòâóþùèõ öåíàì ìåæäó ïðåäûäóùèì ìèíèìóìîì è òåêóùèì
for(int i=priceIndex-1; i>=0; i--)
g_volumePriceArray[i]=0;
g_volumePriceArray[0]=1;
//--- Íîâûé ìèíèìóì
lowPrice=bid;
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Çàïèñü äàííûõ î òèêå â ìàññèâ 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;
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Äîáàâëåíèå îäíîãî íîâîãî òèêà ê èìåþùåéñÿ ñâå÷å |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void ProcessOneTick(int limit,double &lowPrice,int &arraySize)
{
TickStruct tick;
tick.time= TimeCurrent();
tick.ask = Ask;
tick.bid = Bid;
//--- Äîáàâëåíèå îäíîãî òèêà â ìàññèâ õðàíåíèÿ òèêîâ
if(!IsUpdateTicksArray(tick))
{
g_activate=false;
return;
}
double bid=CastPriceToCluster(Bid);
//--- Îáðàçîâàíèå íîâîãî áàðà èëè íà÷àëî ðàáîòû "ñ íóëÿ"
if(limit==1 || lowPrice==0 || arraySize==0)
{
ProcessNewBarForming(bid,lowPrice,arraySize);
return;
}
//--- Åñëè ýêñòðåìóìû ñâå÷è íå îáíîâëåíû, òî ïðîñòî äîáàâëÿåòñÿ îáúåì îäíîìó èç ñóùåñòâóþùèõ óðîâíåé
int priceIndex=(int)MathRound((bid-lowPrice)/g_tickSize); // Èíäåêñ ýëåìåíòà ìàññèâà g_volumePriceArray, êîòîðîìó ñîîòâåòñòâóåò öåíà Bid
if(priceIndex>=0 && priceIndex < arraySize)
{
g_volumePriceArray[priceIndex]++;
return;
}
//--- Îáíîâëåí ìèíèìóì òåêóùåé ñâå÷è. Íóæíî ñäâèíóòü âñå ýëåìåíòû ìàññèâà g_volumePriceArray íà priceIndex ââåðõ
if(IsFirstMoreThanSecond(lowPrice,bid))
{
ProcessCandleMinimumUpdate(priceIndex,bid,lowPrice,arraySize);
return;
}
//--- Îáíîâëåí ìàêñèìóì òåêóùåé ñâå÷è.
if(priceIndex+1>MAX_POINTS_IN_CANDLE)
return;
arraySize=priceIndex+1;
g_volumePriceArray[priceIndex]=1;
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Îòîáðàæåíèå ñåòêè êëàñòåðîâ |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void ShowGrid()
{
if(!g_init)
return;
g_init=false;
if(i_showClusterGrid==NO)
return;
//--- Îïðåäåëåíèå èñòîðè÷åñêèõ ýêñòðåìóìîâ
double highPrice= CastPriceToCluster(High[iHighest(NULL,0,MODE_HIGH)]);
double lowPrice = CastPriceToCluster(Low[iLowest(NULL,0,MODE_LOW)]);
//--- Îòîáðàæåíèå ëèíèé êëàñòåðîâ
for(double price=lowPrice; price<=highPrice; price=NormalizeDouble(price+i_pointsInBox*g_point,Digits))
ShowHLine(price,i_gridColor);
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Îòîáðàæåíèå äàííûõ èíäèêàòîðà |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void ShowIndicatorData(int limit,int total)
{
static double lowPrice = 0; // Öåíà, ñîîòâåòñòâóþùàÿ ìèíèìóìó ñâå÷è è íóëåâîìó ýëåìåíòó ìàññèâà g_volumePriceArray. Ïåðâîìó..
// ..ýëåìåíòó áóäåò ñîîòâåòñòâîâàòü öåíà lowPrice + Point è ò.ä.;
static int arraySize = 0; // Êîëè÷åñòâî ýëåìåíòîâ, çàïèñàííûõ â ìàññèâ g_volumePriceArray.  èäåàëå ýòî çíà÷åíèå äîëæíî áûòü..
// ..ðàâíî êîëè÷åñòâó ïóíêòîâ, èç êîòîðûõ ñîñòîèò ñâå÷à. Íî èç-çà ðàçäåëüíîé çàïèñè òèêîâ è..
// ..ðåàëüíîãî ôîðìèðîâàíèÿ ñâå÷åé âîçìîæíû ñäâèãè
if(limit>1) // Âûçîâ ïðîèñõîäèò òîëüêî â ìîìåíò îòîáðàæåíèÿ âñåé èñòîðèè - íà÷àëüíàÿ çàãðóçêà èëè îáíîâëåíèå..
{ // ..áàðîâ ñ èíäåêñîì áîëåå 1
ProcessOldCandles(limit,lowPrice,arraySize);
return;
}
//--- Íîðìàëüíîå îáíîâëåíèå - ïðèõîä íîâîãî òèêà èëè îáðàçîâàíèå íîâîãî áàðà
ProcessOneTick(limit,lowPrice,arraySize);
ShowBarHistogramms(0,lowPrice,arraySize);
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| 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;
ProcessGlobalTick(rates_total,prev_calculated);
return rates_total;
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Âûïîëíåíèå îäíîé èòåðàöèè îòîáðàæåíèÿ äàííûõ |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void ProcessGlobalTick(const int rates_total,const int prev_calculated)
{
int total;
int limit=GetRecalcIndex(total,rates_total,prev_calculated); // Ñ êàêîãî áàðà íà÷èíàòü îáíîâëåíèå?
ShowInfoViewButton(); // Îòîáðàæåíèå êíîïêè âêë./âûêë. âèçóàëèçàöèè ïîêàçàíèé èíäèêàòîðà
ShowGrid(); // Îòîáðàæåíèå ëèíèé êëàñòåðîâ
ShowIndicatorData(limit, total); // Îòîáðàæåíèå äàííûõ èíäèêàòîðà
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Îáðàáîò÷èê ñîáûòèé ÷àðòà |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
void OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
{
if(id!=CHARTEVENT_OBJECT_CLICK)
return;
if(sparam!=PREFIX+SIGN_BUTTON+IntegerToString(BUTTON_XCOORD)+IntegerToString(BUTTON_YCOORD))
return;
//--- Âûêëþ÷åíèå êëàñòåðîâ
if(g_isShowInfo)
{
DeleteAllObjects();
ShowButton(BUTTON_XCOORD,BUTTON_YCOORD,"Êëàñòåðû âêë.");
g_isShowInfo=false;
g_init=false;
return;
}
//--- Âêëþ÷åíèå êëàñòåðîâ
g_init=true;
ProcessGlobalTick(Bars,0);
}
//+------------------------------------------------------------------+
Comments