MultiSymbolSignals/Indicators/MultiSymbol/ATR_MS.mq5
super.admin f343eff89f convert
2025-05-30 16:10:44 +02:00

1207 lines
106 KiB
MQL5

//+------------------------------------------------------------------+
//| ATR_MS.mq5 |
//| Copyright 2013, https://login.mql5.com/ru/users/tol64 |
//| Site, http://tol64.blogspot.com/2013/05/216.html |
//+------------------------------------------------------------------+
//--- Свойства индикатора
#property copyright "Copyright 2013, http://tol64.blogspot.com"
#property link "https://www.mql5.com/de/articles/752"
#property description "email: hello.tol64@gmail.com"
#property version "1.0"
#property indicator_separate_window // Индикатор в отдельном подокне
#property indicator_minimum 0 // Минимальное значение индикатора
#property indicator_buffers 6 // Количество буферов для расчёта индикатора
#property indicator_plots 6 // Количество графических серий
//--- Константа для возврата терминалу команды на пересчёт индикатора
#define RESET 0
#define LEVELS 6 // Количество уровней
#define SYMBOLS 6 // Количество символов
//--- Подключим класс для работы с канвой
#include <Canvas\Canvas.mqh>
//--- Внешние параметры
input int IndicatorPeriod=14; // Indicator Period
sinput string dlm01=""; //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
input string Symbol02 ="GBPUSD"; // Symbol 02
input string Symbol03 ="AUDUSD"; // Symbol 03
input string Symbol04 ="NZDUSD"; // Symbol 04
input string Symbol05 ="USDCAD"; // Symbol 05
input string Symbol06 ="USDCHF"; // Symbol 06
sinput string dlm02=""; //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
input int Level01 =10; // Level 01
input int Level02 =50; // Level 02
input int Level03 =100; // Level 03
input int Level04 =200; // Level 04
input int Level05 =400; // Level 05
input int Level06 =600; // Level 06
//--- Глобальные переменные и массивы
CCanvas canvas; // Загрузка класса
//--- Переменные/массивы для копирования данных из OnCalculate()
int on_calc_rates_total =0; // Размер входных таймсерий
int on_calc_prev_calculated =0; // Обработано баров на предыдущем вызове
datetime on_calc_time[]; // Время открытия
double on_calc_open[]; // Цены открытия
double on_calc_high[]; // Максимальные цены
double on_calc_low[]; // Минимальные цены
double on_calc_close[]; // Цены закрытия
long on_calc_tick_volume[]; // Тиковые объёмы
long on_calc_volume[]; // Реальные объёмы
int on_calc_spread[]; // Спред
//--- Структура массивов буферов
// для отрисовки значений индикатора
struct buffers { double data[]; };
buffers buffer_atr[SYMBOLS];
//--- Структуры массивов для подготовки данных
struct temp_time { datetime time[]; };
temp_time temp_symbol_time[SYMBOLS];
struct temp_atr { double value[]; };
temp_atr temp_atr_values[SYMBOLS];
//--- Для хранения и проверки времени первого бара в терминале
datetime series_first_date[SYMBOLS];
datetime series_first_date_last[SYMBOLS];
//--- Массив времени бара, от которого начинать отрисовку
datetime limit_time[SYMBOLS];
//--- Массив уровней индикатора
int levels[LEVELS];
//--- Массив названий символов
string symbols_names[SYMBOLS];
//--- Хэндлы символов
int handle_symbol[SYMBOLS];
//--- Цвета линий индикатора
color line_color[SYMBOLS]=
{clrRed,clrDodgerBlue,clrLimeGreen,clrGold,clrAqua,clrMagenta};
//--- Строка символизирующая отсутствие символа
string empty_symbol="EMPTY";
//--- Свойства подокна индикатора
int number_subwindow =WRONG_VALUE; // Номер подокна
int chart_width =0; // Ширина графика
int subwindow_height =0; // Высота подокна
int last_chart_width =0; // Последняя в памяти ширина графика
int last_subwindow_height =0; // Последняя в памяти высота подокна
int subwindow_center =0; // Центр подокна по горизонтали
int subwindow_vcenter =0; // Центр подокна по вертикали
string shortname_subwindow ="ATR_MS"; // Короткое имя индикатора
string prefix =shortname_subwindow+"_"; // Префикс для объектов
//--- Свойства канвы
string canvas_name =prefix+"canvas"; // Название канвы
color canvas_bg_color =clrBlack; // Цвет фона канвы
uchar canvas_opacity =190; // Степень прозрачности
int font_size =16; // Размер шрифта
string font_name ="Calibri"; // Шрифт
ENUM_COLOR_FORMAT clr_format =COLOR_FORMAT_ARGB_RAW; // Компоненты цвета не обрабатываются терминалом
//--- Сообщения канвы
string message_invalid_handle ="Невалидный хэндл индикатора! Подождите пожалуйста...";
string message_data_available ="Подготовка данных! Подождите пожалуйста...";
string message_number_data ="Данных меньше, чем период индикатора! Попробуйте уменьшить значение.";
string message_is_synchronized ="Данные не синхронизированы! Подождите пожалуйста...";
string message_formation_data ="";
string message_synchro_update ="";
string message_last ="";
//--- Максимальное количество баров установленное в настройках терминала
int terminal_max_bars=0;
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- Проверим корректность входных параметров
if(!CheckInputParameters())
return(INIT_PARAMETERS_INCORRECT);
//--- Включим таймер с интервалом 1 секунда
EventSetMillisecondTimer(1000);
//--- Установим шрифт для отображения в канве
canvas.FontSet(font_name,font_size,FW_NORMAL);
//--- Инициализация массивов
InitArrays();
//--- Инициализируем массив символов
InitSymbolsNames();
//--- Инициализируем массив уровней
InitLevels();
//--- Получим хэндлы индикаторов
GetHandles();
//--- Установим свойства индикатора
SetPropertiesIndicator();
//--- Получим количество баров установленное в настройках терминала
terminal_max_bars=TerminalInfoInteger(TERMINAL_MAXBARS);
//--- Очистим комментарий
Comment("");
//--- Обновим график
ChartRedraw();
//--- Инициализация прошла успешно
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Деинициализация |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(reason==REASON_REMOVE || // Если индикатор удалён с графика или
reason==REASON_CHARTCHANGE || // символ или период был изменён или
reason==REASON_RECOMPILE || // программа была перекомпилирована или
reason==REASON_CHARTCLOSE || // график был закрыт или
reason==REASON_CLOSE || // терминал был закрыт или
reason==REASON_PARAMETERS) // параметры были изменены
{
//--- Отключим таймер
EventKillTimer();
//--- Удалим уровни
DeleteLevels();
//--- Удалим вертикальные линии
DeleteVerticalLines();
//--- Удалим канву
DeleteCanvas();
//--- Освободим расчётную часть индикатора
for(int s=0; s<SYMBOLS; s++)
IndicatorRelease(handle_symbol[s]);
//--- Очистим комментарий
Comment("");
}
//--- Обновим график
ChartRedraw();
}
//+------------------------------------------------------------------+
//| 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[]) // Спред
{
//--- Для определения, с какого бара производить расчёт
// To determine which bar to calculate
int limit=0;
//--- Сделаем копию параметров OnCalculate()
// Make a copy of the OnCalculate() parameters
CopyDataOnCalculate(rates_total,prev_calculated,
time,open,high,low,close,
tick_volume,volume,spread);
//--- Установим размер массивам для подготовки данных
// Set size for arrays for data preparation
ResizeCalculateArrays();
//--- Если это первый расчёт или загружена более глубокая история или были заполнены пропуски истории
// If this is the FIRST calculation or DOWNLOAD of deeper history or FILLING of history gaps
if(prev_calculated==0)
{
//--- Обнулим массивы для подготовки данных
// Zero the arrays for data preparation.
ZeroCalculateArrays();
//--- Обнулим индикаторные буферы
// Zero indicator buffers
ZeroIndicatorBuffers();
//--- Получим свойства подокна
SetSubwindowProperties();
//--- Установим канву
SetCanvas();
//--- rf. https://www.mql5.com/en/docs/series/timeseries_access
// LoadFormationData() - SERIES_FIRSTDATE, SERIES_SERVER_FIRSTDATE
// CheckAvailableData() - CopyTime() and CopyBuffer() from SERIES_TERMINAL_FIRSTDATE to TimeCurrent()
// CheckEventLoadHistory() -
// CheckSymbolIsSynchronized() - SERIES_SYNCHRONIZED, TERMINAL_CONNECTED
// DetermineBeginForCalculate() -
//--- Загрузим и сформируем необходимое/имеющееся количество данных
// Load and generate the necessary / available amount of data
LoadFormationData();
//--- Если есть невалидный хэндл, попробуем получить его снова
// If there is an invalid handle, try to get it again.
if(!GetHandles())
return(RESET);
//--- Проверяет количество доступных данных у всех символов
// Checks the amount of data available for all symbols.
if(!CheckAvailableData())
return(RESET);
//--- Если загружена более глубокая история
// if loading a deeper history
if(!CheckEventLoadHistory())
return(RESET);
//--- Проверим синхронизированность данных по символу/периоду на данный момент
// Check the synchronization of data on the symbol / period at the moment
if(!CheckSymbolIsSynchronized())
return(RESET);
//--- Определим для каждого символа, с какого бара начинать отрисовку
// Determine for each symbol, from which bar to start drawing
if(!DetermineBeginForCalculate())
return(RESET);
//--- Если дошли до этого момента, то значит OnCalculate() вернёт ненулевое значение и
// это нужно запомнить
// If you have reached this point, it means OnCalculate () returns a nonzero value and
// you need to remember this.
on_calc_prev_calculated=rates_total;
}
//--- Если нужно пересчитать только последние значения
// If you need to recount only the last values
else
limit=prev_calculated-1;
//--- Подготовим данные для отрисовки
// Prepare data for rendering
for(int s=0; s<SYMBOLS; s++)
{
//--- Если символ существует
// If symbol exists
if(symbols_names[s]!=empty_symbol)
{
double percent=0.0; // Для расчёта процента прогресса - To calculate the percentage of progress
message_last=message_synchro_update="Подготовка данных ("+IntegerToString(rates_total)+" баров) : "+
symbols_names[s]+"("+IntegerToString(s+1)+"/"+IntegerToString(SYMBOLS)+") - 00% ... ";
//--- Выведем сообщение - Display message
ShowCanvasMessage(message_synchro_update);
//--- Проконтролируем каждое значение массива
// We control each array value
for(int i=limit; i<rates_total; i++)
{
PreparationData(i,s,time);
//--- Каждые 1000 баров обновляем сообщение
// Every 1000 bars update message
if(!(i%1000))
{
//--- Процент прогресса - percentage of progress
PercentProgress(i,s,percent);
//--- Выведем сообщение - We will display the message
ShowCanvasMessage(message_synchro_update);
}
//--- Каждые 2000 баров проверяем размеры подокна
// и если размер изменился подгоним под него размер канвы
// Every 2000 bars we check the dimensions of the subwindow
// and if the size has changed we will fit the size of the canvas
if(!(i%2000))
EventChartChange();
}
}
}
//--- Заполним индикаторные буферы
// We will fill indicator buffers
for(int s=0; s<SYMBOLS; s++)
{
//--- Если указанного символа не существует, обнулим буфер
// If the specified character does not exist, zero the buffer
if(symbols_names[s]==empty_symbol)
ArrayInitialize(buffer_atr[s].data,EMPTY_VALUE);
else
{
//--- Сформируем сообщение - We will form a message
message_last=message_synchro_update="Обновление данных индикатора: "+
symbols_names[s]+"("+IntegerToString(s+1)+"/"+IntegerToString(SYMBOLS)+") ... ";
//--- Выведем сообщение - We will display the message
ShowCanvasMessage(message_synchro_update);
//--- Заполним индикаторные буферы значениями
// Fill indicator buffers with values
for(int i=limit; i<rates_total; i++)
{
FillIndicatorBuffers(i,s,time);
//--- Каждые 2000 баров проверяем размеры подокна
// и если размер изменился подгоним под него размер канвы
// Every 2000 bars we check the dimensions of the subwindow
// and if the size has changed we will fit the size of the canvas
if(!(i%2000))
EventChartChange();
}
}
}
//--- Удалим канву
DeleteCanvas();
//--- Установим уровни индикатора
SetLevels();
//--- Обнулим переменные
message_last="";
message_synchro_update="";
//--- Обновим график
ChartRedraw();
//--- Вернём размер массива данных текущего символа
return(rates_total);
}
//+------------------------------------------------------------------+
//| Timer function |
//+------------------------------------------------------------------+
void OnTimer()
{
//--- Если была загружена более глубокая история
if(!CheckEventLoadHistory())
on_calc_prev_calculated=0;
//--- Если по какой-то причине расчёты не были завершены или
// подкачана более глубокая история или
// были заполнены пропуски истории, то
// не дожидаясь тика сделаем ещё одну попытку
if(on_calc_prev_calculated==0)
{
OnCalculate(on_calc_rates_total,on_calc_prev_calculated,
on_calc_time,on_calc_open,on_calc_high,on_calc_low,on_calc_close,
on_calc_tick_volume,on_calc_volume,on_calc_spread);
}
}
//+------------------------------------------------------------------+
//| Проверяет входные параметры на корректность |
//+------------------------------------------------------------------+
bool CheckInputParameters()
{
if(IndicatorPeriod>500)
{
Comment("Уменьшите период индикатора! Indicator Period: ",IndicatorPeriod,"; Limit: 500;");
printf("Уменьшите период индикатора! Indicator Period: %d; Limit: %d;",IndicatorPeriod,500);
return(false);
}
//---
return(true);
}
//+------------------------------------------------------------------+
//| Устанавливает свойства индикатора |
//+------------------------------------------------------------------+
void SetPropertiesIndicator()
{
//--- Установим короткое имя
IndicatorSetString(INDICATOR_SHORTNAME,shortname_subwindow);
//--- Установим количество знаков
IndicatorSetInteger(INDICATOR_DIGITS,_Digits);
//--- Определим буферы для отрисовки
for(int s=0; s<SYMBOLS; s++)
SetIndexBuffer(s,buffer_atr[s].data,INDICATOR_DATA);
//--- Установим метки для текущего таймфрейма
for(int s=0; s<SYMBOLS; s++)
PlotIndexSetString(s,PLOT_LABEL,
"ATR ("+IntegerToString(s)+", "+symbols_names[s]+")");
//--- Установим тип линий
for(int s=0; s<SYMBOLS; s++)
PlotIndexSetInteger(s,PLOT_DRAW_TYPE,DRAW_LINE);
//--- Установим толщину линий
for(int s=0; s<SYMBOLS; s++)
PlotIndexSetInteger(s,PLOT_LINE_WIDTH,1);
//--- Установим цвет линий
for(int s=0; s<SYMBOLS; s++)
PlotIndexSetInteger(s,PLOT_LINE_COLOR,line_color[s]);
//--- Пустое значение для построения, для которого нет отрисовки
for(int s=0; s<SYMBOLS; s++)
PlotIndexSetDouble(s,PLOT_EMPTY_VALUE,EMPTY_VALUE);
}
//+------------------------------------------------------------------+
//| Первая инициализация массивов |
//+------------------------------------------------------------------+
void InitArrays()
{
ArrayInitialize(limit_time,NULL);
ArrayInitialize(series_first_date,NULL);
ArrayInitialize(series_first_date_last,NULL);
ArrayInitialize(handle_symbol,INVALID_HANDLE);
//---
for(int s=0; s<SYMBOLS; s++)
ArrayInitialize(buffer_atr[s].data,EMPTY_VALUE);
}
//+------------------------------------------------------------------+
//| Инициализирует массив символов |
//+------------------------------------------------------------------+
void InitSymbolsNames()
{
symbols_names[0]=CheckGetSymbol(_Symbol);
symbols_names[1]=CheckGetSymbol(Symbol02);
symbols_names[2]=CheckGetSymbol(Symbol03);
symbols_names[3]=CheckGetSymbol(Symbol04);
symbols_names[4]=CheckGetSymbol(Symbol05);
symbols_names[5]=CheckGetSymbol(Symbol06);
}
//+------------------------------------------------------------------+
//| Инициализирует массив уровней |
//+------------------------------------------------------------------+
void InitLevels()
{
levels[0]=Level01;
levels[1]=Level02;
levels[2]=Level03;
levels[3]=Level04;
levels[4]=Level05;
levels[5]=Level06;
}
//+------------------------------------------------------------------+
//| Загружает и формируем необходимое/имеющееся кол-во данных |
//| It loads and we form the necessary / available amount of data |
//+------------------------------------------------------------------+
void LoadFormationData()
{
int number_bars=100; // Количество подгружаемых баров
//---
for(int s=0; s<SYMBOLS; s++)
{
int count_try =0; // Счётчик попыток копирования данных
int array_size =0; // Размер массива
datetime server_firstdate =NULL; // Время первого бара на сервере
datetime series_firstdate =NULL; // Время первого бара в базе терминала
//--- Получим первую дату символа-периода в базе терминала
// Get the first date of the symbol-period in the terminal base
SeriesInfoInteger(symbols_names[s],Period(),SERIES_FIRSTDATE,series_firstdate);
//--- Получим первую дату символа-периода на сервере
// Get the first date of the symbol-period on the server
SeriesInfoInteger(symbols_names[s],Period(),SERIES_SERVER_FIRSTDATE,server_firstdate);
//--- Выведем сообщение - We will display the message
message_last=message_formation_data="Процесс загрузки и формирования данных: "+
symbols_names[s]+"("+IntegerToString(s+1)+"/"+IntegerToString(SYMBOLS)+") ... ";
ShowCanvasMessage(message_formation_data);
//--- Загрузим/сформируем данные, если размер массива меньше, чем максимальное количество баров в терминале,
// а также между первой датой серии в терминале и первой датой серии на сервере
// больше указанного количества баров
//--- Load / generate data if the size of the array is less than the maximum number of bars in the terminal,
// as well as between the first date of the series in the terminal and the first date of the series
// on the server more than the specified number of bars
while(array_size<on_calc_rates_total &&
series_firstdate-server_firstdate>PeriodSeconds()*number_bars)
{
datetime copied_time[];
//--- Получим первую дату символа-периода в базе терминала
// Get the first date of the symbol-period in the terminal base
SeriesInfoInteger(symbols_names[s],Period(),SERIES_FIRSTDATE,series_firstdate);
//--- Загрузим/скопируем ещё указанное количество баров
// Download / copy another specified number of bars
if(CopyTime(symbols_names[s],Period(),0,array_size+number_bars,copied_time))
{
//--- Если время первого бара массива с вычетом кол-ва подгружаемых баров раньше,
// чем время первого бара на графике, остановим цикл
// If the time of the first bar of the array minus the number of loaded bars
// is earlier than the time of the first bar on the chart, we stop the cycle
if(copied_time[0]-PeriodSeconds()*number_bars<on_calc_time[0])
break;
//--- Если размер массива не увеличился, увеличим счётчик
// If the size of the array has not increased, we will increase the counter
if(ArraySize(copied_time)==array_size)
count_try++;
//--- Иначе получим текущий размер массива
// Otherwise, we will get the current size of the array
else
array_size=ArraySize(copied_time);
//--- Если размер массива не увеличивается в течении 100 попыток, остановим цикл
// If the size of the array does not increase within 100 attempts, stop the loop
if(count_try==100)
{
count_try=0;
break;
}
}
//--- Каждые 2000 баров проверяем размеры подокна
// и если размер изменился подгоним под него размер канвы
// Every 2000 bars we check the dimensions of the subwindow
// and if the size has changed we will fit the size of the canvas
if(!(array_size%2000))
EventChartChange();
}
}
}
//+------------------------------------------------------------------+
//| Получает хэндлы |
//+------------------------------------------------------------------+
bool GetHandles()
{
//--- Признак того, что все хэндлы валидны
bool invalid=true;
//--- Пройдёмся в цикле по всем символам и ...
for(int s=0; s<SYMBOLS; s++)
{
//--- Если символ есть и ...
if(symbols_names[s]!=empty_symbol)
{
// ...хэндл текущего таймфрейма невалиден, получим его
if(handle_symbol[s]==INVALID_HANDLE)
{
handle_symbol[s]=iATR(symbols_names[s],Period(),IndicatorPeriod);
//--- Если не удалось получить хэндл
if(handle_symbol[s]==INVALID_HANDLE)
invalid=false; // Попробуем в следующий раз
}
}
}
//--- Выведем сообщение, если хэндл для одного из символов не получен
// We will display the message if the handle for one of the symbols is not received
if(!invalid)
{
message_last=message_invalid_handle;
ShowCanvasMessage(message_invalid_handle);
}
//---
return(invalid);
}
//+------------------------------------------------------------------+
//| Проверяет количество доступных данных у всех символов |
//+------------------------------------------------------------------+
bool CheckAvailableData()
{
for(int s=0; s<SYMBOLS; s++)
{
//--- Если такой символ есть
if(symbols_names[s]!=empty_symbol)
{
double data[]; // Массив для проверки количества данных индикатора
datetime time[]; // Массив для проверки количества баров
int calculated_values =0; // Количество данных индикатора
int total_period_bars =0; // Количество баров текущего периода
datetime terminal_first_date =NULL; // Первая дата имеющихся данных текущего периода в терминале
//--- Получим кол-во рассчитанных значений индикатора
// Get the number of calculated indicator values
calculated_values=BarsCalculated(handle_symbol[s]);
//--- Получим первую дату данных текущего периода в терминале
// Get the first date of the current period data in the terminal
terminal_first_date=(datetime)SeriesInfoInteger(symbols_names[s],Period(),SERIES_TERMINAL_FIRSTDATE);
//--- Получим количество доступных баров от указанной даты
// Get the number of available bars from the specified date.
total_period_bars=Bars(symbols_names[s],Period(),terminal_first_date,TimeCurrent());
//--- Проверим готовность данных баров
for(int i=0; i<5; i++)
{
//--- Скопируем указанное количество данных
// Copy the specified amount of time data (all between terminal_first_date and TimeCurrent())
if(CopyTime(symbols_names[s],Period(),0,total_period_bars,time))
{
//--- Если скопировалось нужное количество, остановим цикл
// If the required amount has been copied, stop the cycle
if(ArraySize(time)>=total_period_bars)
break;
}
}
//--- Проверим готовность данных индикатора
for(int i=0; i<5; i++)
{
//--- Скопируем указанное количество данных
// Copy the specified amount of time data (all calculated for the indicator)
if(CopyBuffer(handle_symbol[s],0,0,calculated_values,data))
{
//--- Если скопировалось нужное количество, остановим цикл
// If the required amount has been copied, stop the cycle
if(ArraySize(data)>=calculated_values)
break;
}
}
//--- Если скопировано меньше данных
// значит нужно совершить ещё одну попытку
if(ArraySize(time)<total_period_bars || ArraySize(data)<calculated_values)
{
message_last=message_data_available;
ShowCanvasMessage(message_data_available);
on_calc_prev_calculated=0;
return(false);
}
}
}
//---
return(true);
}
//+------------------------------------------------------------------+
//| Проверка события загрузки более глубокой истории |
//| Check the event of loading a deeper history if there is a symbol |
//| such that |
//+------------------------------------------------------------------+
bool CheckEventLoadHistory()
{
bool load=false;
//---
for(int s=0; s<SYMBOLS; s++)
{
//--- Если такой символ есть
if(symbols_names[s]!=empty_symbol)
{
//--- Если нужно обновить серии
if(on_calc_prev_calculated==0)
{
//--- Получим первую дату символа-периода
// Get the first date of the period symbol
series_first_date[s]=(datetime)SeriesInfoInteger(symbols_names[s],Period(),SERIES_FIRSTDATE);
//--- Если здесь в первый раз (отсутствует значение), то
if(series_first_date_last[s]==NULL)
//--- Запомним первую дату символа-периода для последующих сравнений с целью определения загрузки
// более глубокой истории
// Remember the first date of the symbol-period for subsequent comparisons to determine the load
// of a deeper history.
series_first_date_last[s]=series_first_date[s];
}
else
{
//--- Получим первую дату символа-периода
// Get the first date of the period symbol
series_first_date[s]=(datetime)SeriesInfoInteger(symbols_names[s],Period(),SERIES_FIRSTDATE);
//--- Если даты отличаются, то есть дата в памяти более поздняя, чем та, которую получили сейчас, то
// значит была загрузка более глубокой истории
// If the dates are different, then there is a date in memory later than the one that we received
// now, then it means that there was a deeper loading
if(series_first_date_last[s]>series_first_date[s])
{
//--- Выведем сообщение в журнал
Print("(",symbols_names[s],",",TimeframeToString(Period()),
") > Была загружена/сформирована более глубокая история: ",
series_first_date_last[s]," > ",series_first_date[s]);
//--- Запомним дату
series_first_date_last[s]=series_first_date[s];
load=true;
}
}
}
}
//--- Если была загружена/сформирована более глубокая история, то
// отправим команду на обновление графических серий индикатора
//--- If a deeper history has been loaded / formed, then
// send a command to update the graphic series of the indicator
if(load)
return(false);
//---
return(true);
}
//+------------------------------------------------------------------+
//| Проверяет синхронизированность по символу/периоду |
//+------------------------------------------------------------------+
bool CheckSymbolIsSynchronized()
{
//--- Если есть соединение с сервером, то проверим синхронизированность данных
// If there is a connection to the server, then we will check the data synchronization.
if(TerminalInfoInteger(TERMINAL_CONNECTED))
{
for(int s=0; s<SYMBOLS; s++)
{
//--- Если символ есть
if(symbols_names[s]!=empty_symbol)
{
//--- Если данные не синхронизированы, то сообщим об этом и попробуем снова
// If the data is not synchronized, then let us know and try again.
if(!SeriesInfoInteger(symbols_names[s],Period(),SERIES_SYNCHRONIZED))
{
message_last=message_is_synchronized;
ShowCanvasMessage(message_is_synchronized);
return(false);
}
}
}
}
//---
return(true);
}
//+------------------------------------------------------------------+
//| Определение времени первого истинного бара для отрисовки |
//+------------------------------------------------------------------+
bool DetermineBeginForCalculate()
{
for(int s=0; s<SYMBOLS; s++)
{
datetime time[]; // Массив времени баров
int total_period_bars=0; // Количество баров
//--- Если такого символа нет, перейти к следующему
if(symbols_names[s]==empty_symbol)
continue;
//--- Получим общее количество баров символа
// Get the total number of bars of the symbol
total_period_bars=Bars(symbols_names[s],Period());
//--- Скопируем массив времени баров. Если не получилось, попробуем ещё раз
// Copy the array of time bars. If not, try again.
if(CopyTime(symbols_names[s],Period(),0,total_period_bars,time)<total_period_bars)
return(false);
//--- Получим время первого истинного бара, который соответствует текущему таймфрейму
// Get the time of the first true bar that corresponds to the current timeframe
limit_time[s]=GetFirstTruePeriodBarTime(time);
//--- Установим вертикальную линию на истинном баре
CreateVerticalLines(0,0,limit_time[s],prefix+symbols_names[s]+": begin time series",
2,STYLE_SOLID,line_color[s],false,TimeToString(limit_time[s]),"\n");
}
//---
return(true);
}
//+------------------------------------------------------------------+
//| Возвращает время первого истинного бара текущего периода |
//| Returns the time of the first true bar of the current period |
//+------------------------------------------------------------------+
datetime GetFirstTruePeriodBarTime(datetime &time[])
{
datetime true_period =NULL; // Время первого истинного бара - Time of the first true bar
int array_size =0; // Размер массива
//--- Получим размер массива - We will have got the size of the array
array_size=ArraySize(time);
ArraySetAsSeries(time,false);
//--- Поочерёдно проверяем каждый бар
for(int i=1; i<array_size; i++)
{
//--- Если бар соответствует текущему таймфрейму
if(time[i]-time[i-1]==PeriodSeconds())
{
//--- Запомним и остановим цикл
true_period=time[i-1];
break;
}
}
//--- Вернём время первого истинного бара
return(true_period);
}
//+------------------------------------------------------------------+
//| Подготовка данных перед рисованием |
//+------------------------------------------------------------------+
void PreparationData(int i,int symbol_number,datetime const &time[])
{
int try
=100; // Количество попыток копирования
//--- Время бара указанного символа и тф
datetime symbol_time[];
//--- Массив для копирования значения индикатора
double atr_value[];
//--- Если в зоне баров текущего таймфрейма
if(time[i]>=limit_time[symbol_number])
{
//--- Скопируем время
for(int k=0; k<try
; k++)
{
if(CopyTime(symbols_names[symbol_number],0,time[i],1,symbol_time)==1)
{
temp_symbol_time[symbol_number].time[i]=symbol_time[0];
break;
}
}
//--- Скопируем значение индикатора
for(int k=0; k<try
; k++)
{
if(CopyBuffer(handle_symbol[symbol_number],0,time[i],1,atr_value)==1)
{
temp_atr_values[symbol_number].value[i]=atr_value[0];
break;
}
}
}
//--- Если вне зоны баров текущего таймфрейма,
// установим пустое значение
else
temp_atr_values[symbol_number].value[i]=EMPTY_VALUE;
}
//+------------------------------------------------------------------+
//| Заполняет индикаторные буферы |
//+------------------------------------------------------------------+
void FillIndicatorBuffers(int i,int symbol_number,datetime const &time[])
{
//--- Для проверки полученного значения индикатора
bool check_value=false;
//--- Счётчик баров текущего таймфрейма
static int count_current_period=0;
//--- Обнулим счётчик баров текущего таймфрейма
// в начале таймсерии символа
if(i==0)
count_current_period=0;
//--- Если в зоне баров текущего таймфрейма и
// счётчик меньше указанного периода индикатора, то
// увеличим счётчик
if(count_current_period<IndicatorPeriod &&
time[i]>=limit_time[symbol_number])
count_current_period++;
//--- Если в зоне индикатора и
// время текущего символа и время указанного символа совпадают
if(count_current_period>=IndicatorPeriod &&
time[i]==temp_symbol_time[symbol_number].time[i])
{
//--- Если полученное значение не пустое
// If the received value is not empty
if(temp_atr_values[symbol_number].value[i]!=EMPTY_VALUE)
{
check_value=true;
buffer_atr[symbol_number].data[i]=temp_atr_values[symbol_number].value[i];
}
}
//--- Установим пустое значение, если не получилось установить выше
if(!check_value)
buffer_atr[symbol_number].data[i]=EMPTY_VALUE;
}
//+------------------------------------------------------------------+
//| Добавляет указанный символ в окно обзор рынка |
//+------------------------------------------------------------------+
string CheckGetSymbol(string symbol)
{
int symbol_total =0; // Количество символов
string nm_symbol =""; // Имя символа
//--- Если передали пустую строку, то вернуть пустую строку
if(symbol=="")
return(empty_symbol);
//--- Всего символов на сервере
symbol_total=SymbolsTotal(false);
//--- Пройтись по всему списку символов
for(int s=symbol_total-1; s>=0; s--)
{
//--- Имя символа на сервере
nm_symbol=SymbolName(s,false);
//--- Если есть такой символ, то
if(nm_symbol==symbol)
{
//--- установим его в окно Обзор Рынка и
SymbolSelect(nm_symbol,true);
//--- вернём его имя
return(symbol);
}
}
//--- Если такого символа нет, то
// вернём строку символизирующую отсутствие символа
return(empty_symbol);
}
//+------------------------------------------------------------------+
//| Установим свойства подокна |
//+------------------------------------------------------------------+
void SetSubwindowProperties()
{
//--- Получим номер подокна индикатора
number_subwindow=ChartWindowFind(0,shortname_subwindow);
//--- Получим ширину и высоту подокна
chart_width=(int)ChartGetInteger(0,CHART_WIDTH_IN_PIXELS);
subwindow_height=(int)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS,number_subwindow);
//--- Получим центр подокна
subwindow_center=chart_width/2;
subwindow_vcenter=subwindow_height/2;
}
//+------------------------------------------------------------------+
//| Копирует данные из OnCalculate |
//+------------------------------------------------------------------+
void CopyDataOnCalculate(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[])
{
on_calc_rates_total=rates_total;
on_calc_prev_calculated=prev_calculated;
ArrayCopy(on_calc_time,time);
ArrayCopy(on_calc_open,open);
ArrayCopy(on_calc_high,high);
ArrayCopy(on_calc_low,low);
ArrayCopy(on_calc_close,close);
ArrayCopy(on_calc_tick_volume,tick_volume);
ArrayCopy(on_calc_volume,volume);
ArrayCopy(on_calc_spread,spread);
}
//+------------------------------------------------------------------+
//| Изменяет размер массивов под размер основного массива |
//+------------------------------------------------------------------+
void ResizeCalculateArrays()
{
for(int s=0; s<SYMBOLS; s++)
{
ArrayResize(temp_symbol_time[s].time,on_calc_rates_total);
ArrayResize(temp_atr_values[s].value,on_calc_rates_total);
}
}
//+------------------------------------------------------------------+
//| Обнуление массивов для подготовки данных |
//+------------------------------------------------------------------+
void ZeroCalculateArrays()
{
for(int s=0; s<SYMBOLS; s++)
{
ArrayInitialize(temp_symbol_time[s].time,NULL);
ArrayInitialize(temp_atr_values[s].value,EMPTY_VALUE);
}
}
//+------------------------------------------------------------------+
//| Обнуление индикаторных буферов |
//+------------------------------------------------------------------+
void ZeroIndicatorBuffers()
{
for(int s=0; s<SYMBOLS; s++)
ArrayInitialize(buffer_atr[s].data,EMPTY_VALUE);
}
//+------------------------------------------------------------------+
//| Следит за размерами канвы |
//+------------------------------------------------------------------+
void EventChartChange()
{
//--- Получим свойства подокна
SetSubwindowProperties();
//--- Если размеры подокна не изменились, выйдем
if(!CheckSubwindowSize())
return;
//--- Если размер подокна меньше одного пикселя или
// центр рассчитан некорректно, выйдем
if(subwindow_height<1 || subwindow_vcenter<1)
return;
//--- Установим новый размер канве
ResizeCanvas();
//--- Покажем последнее сообщение
ShowCanvasMessage(message_last);
}
//+------------------------------------------------------------------+
//| Проверим размеры подокна |
//+------------------------------------------------------------------+
bool CheckSubwindowSize()
{
//--- Если размеры подокна не изменились, выйдем
if(last_chart_width==chart_width &&
last_subwindow_height==subwindow_height)
return(false);
//--- Если же изменились, то запомним их
else
{
last_chart_width=chart_width;
last_subwindow_height=subwindow_height;
}
//---
return(true);
}
//+------------------------------------------------------------------+
//| Установить канву |
//+------------------------------------------------------------------+
void SetCanvas()
{
//--- Если канвы нет, установим её
if(ObjectFind(0,canvas_name)<0)
{
//--- Создадим канву
canvas.CreateBitmapLabel(0,number_subwindow,canvas_name,0,0,chart_width,subwindow_height,clr_format);
//--- Сделаем канву полностью прозрачной
canvas.Erase(ColorToARGB(canvas_bg_color,0));
//--- Обновим канву
canvas.Update();
}
}
//+------------------------------------------------------------------+
//| Изменяет размер канвы |
//+------------------------------------------------------------------+
void ResizeCanvas()
{
//--- Если канва есть в подокне индикатора, установим новый размер
if(ObjectFind(0,canvas_name)==number_subwindow)
canvas.Resize(chart_width,subwindow_height);
}
//+------------------------------------------------------------------+
//| Выводит сообщение в канве |
//+------------------------------------------------------------------+
void ShowCanvasMessage(string message_text)
{
SetSubwindowProperties();
//--- Если канва есть в подокне индикатора
if(ObjectFind(0,canvas_name)==number_subwindow)
{
//--- Отобразим сообщение,
// если передана не пустая строка и координаты получены
if(message_text!="" && subwindow_center>0 && subwindow_vcenter>0)
{
canvas.Erase(ColorToARGB(canvas_bg_color,canvas_opacity));
canvas.TextOut(subwindow_center,subwindow_vcenter,message_text,ColorToARGB(clrRed),TA_CENTER|TA_VCENTER);
canvas.Update();
}
}
}
//+------------------------------------------------------------------+
//| Удаляет канву |
//+------------------------------------------------------------------+
void DeleteCanvas()
{
//--- Удалим канву, если она есть
if(ObjectFind(0,canvas_name)>0)
{
//--- Перед удалением произведём эффект исчезания
for(int i=canvas_opacity; i>0; i-=5)
{
canvas.Erase(ColorToARGB(canvas_bg_color,(uchar)i));
canvas.Update();
}
//--- Удаление канвы
canvas.Destroy();
}
}
//+------------------------------------------------------------------+
//| Процент прогресса |
//+------------------------------------------------------------------+
void PercentProgress(int i,int symbols_number,double &percent)
{
string message_text="";
percent=(double(i)/on_calc_rates_total)*100;
//---
if(percent<=9.99)
message_text="0"+DoubleToString(percent,0);
else
if(percent<99)
message_text=DoubleToString(percent,0);
else
message_text="99";
//---
message_last=message_synchro_update="Подготовка данных ("+IntegerToString(on_calc_rates_total)+" баров) : "+
symbols_names[symbols_number]+
"("+IntegerToString(symbols_number+1)+"/"+IntegerToString(SYMBOLS)+") - "+message_text+"% ... ";
}
//+------------------------------------------------------------------+
//| Установка уровней |
//+------------------------------------------------------------------+
void SetLevels()
{
//--- Получим номер подокна индикатора
number_subwindow=ChartWindowFind(0,shortname_subwindow);
//--- Установим уровни
for(int s=0; s<LEVELS; s++)
CreateHorizontalLines(0,number_subwindow,
prefix+"level_0"+IntegerToString(s+1)+"",
CorrectValueBySymbolDigits(levels[s]*_Point),
1,STYLE_DOT,clrLightSteelBlue,false,false,false,"\n");
}
//+------------------------------------------------------------------+
//| Удаляет уровни |
//+------------------------------------------------------------------+
void DeleteLevels()
{
for(int i=0; i<LEVELS; i++)
DeleteObjectByName(prefix+"level_0"+IntegerToString(i+1)+"");
}
//+------------------------------------------------------------------+
//| Удаляет вертикальные линии начала серий |
//+------------------------------------------------------------------+
void DeleteVerticalLines()
{
for(int s=0; s<SYMBOLS; s++)
DeleteObjectByName(prefix+symbols_names[s]+": begin time series");
}
//+------------------------------------------------------------------+
//| Удаляет объект по имени |
//+------------------------------------------------------------------+
void DeleteObjectByName(string Name)
{
//--- Если есть такой объект
if(ObjectFind(0,Name)>=0)
{
//--- Если была ошибка при удалении, сообщим об этом
if(!ObjectDelete(0,Name))
Print("Ошибка ("+IntegerToString(GetLastError())+") при удалении объекта!");
}
}
//+------------------------------------------------------------------+
//| Создание горизонтальной линии |
//+------------------------------------------------------------------+
void CreateHorizontalLines(long chart_id, // id графика
int number_window, // номер окна
string name_line, // имя объекта
double price, // уровень цены
int width_line, // толщина линии
ENUM_LINE_STYLE style_line, // стиль линии
color color_line, // цвет линии
bool selectable, // нельзя выделить объект, если FALSE
bool select, // выделение
bool back, // фоновое расположение
string tooltip) // нет всплывающей подсказки, если "\n"
{
//--- Если объект успешно создан...
if(ObjectCreate(chart_id,name_line,OBJ_HLINE,number_window,0,price))
{
// ...установим ему свойства
ObjectSetInteger(chart_id,name_line,OBJPROP_SELECTABLE,selectable);
ObjectSetInteger(chart_id,name_line,OBJPROP_SELECTED,select);
ObjectSetInteger(chart_id,name_line,OBJPROP_BACK,back);
ObjectSetInteger(chart_id,name_line,OBJPROP_STYLE,style_line);
ObjectSetInteger(chart_id,name_line,OBJPROP_WIDTH,width_line);
ObjectSetInteger(chart_id,name_line,OBJPROP_COLOR,color_line);
ObjectSetString(chart_id,name_line,OBJPROP_TOOLTIP,tooltip);
}
}
//+------------------------------------------------------------------+
//| Создание вертикальной линии |
//+------------------------------------------------------------------+
void CreateVerticalLines(long chart_id, // id графика
int number_window, // номер окна
datetime time, // время
string name_line, // имя объекта
int width_line, // толщина линии
ENUM_LINE_STYLE style_line, // стиль линии
color color_line, // цвет линии
bool selectable, // нельзя выделить объект, если FALSE
string description_text, // текст описания
string tooltip) // нет всплывающей подсказки, если "\n"
{
//--- Если объект успешно создан...
if(ObjectCreate(chart_id,name_line,OBJ_VLINE,number_window,time,0))
{
// ...установим ему свойства
ObjectSetInteger(chart_id,name_line,OBJPROP_TIME,time);
ObjectSetInteger(chart_id,name_line,OBJPROP_SELECTABLE,selectable);
ObjectSetInteger(chart_id,name_line,OBJPROP_STYLE,style_line);
ObjectSetInteger(chart_id,name_line,OBJPROP_WIDTH,width_line);
ObjectSetInteger(chart_id,name_line,OBJPROP_COLOR,color_line);
ObjectSetString(chart_id,name_line,OBJPROP_TEXT,description_text);
ObjectSetString(chart_id,name_line,OBJPROP_TOOLTIP,tooltip);
}
}
//+------------------------------------------------------------------+
//| Коррекция значения по количеству знаков в цене (double) |
//+------------------------------------------------------------------+
double CorrectValueBySymbolDigits(double value)
{
return(_Digits==3 || _Digits==5) ? value*=10 : value;
}
//+------------------------------------------------------------------+
//| Преобразует таймфрейм в строку |
//+------------------------------------------------------------------+
string TimeframeToString(ENUM_TIMEFRAMES timeframe)
{
string str="";
//--- Если переданное значение некорректно, берем таймфрейм текущего графика
if(timeframe==WRONG_VALUE || timeframe== NULL)
timeframe= Period();
switch(timeframe)
{
case PERIOD_M1 :
str="M1";
break;
case PERIOD_M2 :
str="M2";
break;
case PERIOD_M3 :
str="M3";
break;
case PERIOD_M4 :
str="M4";
break;
case PERIOD_M5 :
str="M5";
break;
case PERIOD_M6 :
str="M6";
break;
case PERIOD_M10 :
str="M10";
break;
case PERIOD_M12 :
str="M12";
break;
case PERIOD_M15 :
str="M15";
break;
case PERIOD_M20 :
str="M20";
break;
case PERIOD_M30 :
str="M30";
break;
case PERIOD_H1 :
str="H1";
break;
case PERIOD_H2 :
str="H2";
break;
case PERIOD_H3 :
str="H3";
break;
case PERIOD_H4 :
str="H4";
break;
case PERIOD_H6 :
str="H6";
break;
case PERIOD_H8 :
str="H8";
break;
case PERIOD_H12 :
str="H12";
break;
case PERIOD_D1 :
str="D1";
break;
case PERIOD_W1 :
str="W1";
break;
case PERIOD_MN1 :
str="MN1";
break;
}
//---
return(str);
}
//+------------------------------------------------------------------+