//+------------------------------------------------------------------+ //| SymbolsInformer.mq5 | //| Copyright 2025, Yuriy Bykov | //| https://www.mql5.com/ru/users/antekov | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, Yuriy Bykov" #property link "https://www.mql5.com/ru/articles/17883" #property description "Информация о поведении цен." #property version "1.01" // Количество существующих таймфреймов #define TFN (21) // Библиотека обнаружения возникновения нового бара #include "Include/Adwizard/Utils/NewBarEvent.mqh" #include "ConsoleDialog.mqh" //+------------------------------------------------------------------+ //| Входные параметры | //+------------------------------------------------------------------+ input group "::: Период для расчётов" sinput ENUM_TIMEFRAMES mainTimeframe_ = PERIOD_D1; // Основной таймфрейм input int mainLength_ = 30; // Количество свечей основного таймфрейма input group "::: Символы и таймфреймы" sinput string symbols_ = "EURUSD,GBPUSD,EURGBP"; // Символы (через запятую) sinput string timeframes_ = "M5,M15,M30,H1,H4"; // Таймфреймы (напр. M5,H1,H4) // Глобальные переменные string g_symbols[]; // Массив всех используемых символов ENUM_TIMEFRAMES g_timeframes[]; // Массив всех используемых таймфреймов // Массивы расчётных значений. // Первый индекс - символ, второй индекс - таймфрейм double symbolAvrCandleSizes[][TFN]; // Массив средних размеров всех свечей double symbolAvrBuyCandleSizes[][TFN]; // Массив средних размеров свечей вверх double symbolAvrSellCandleSizes[][TFN]; // Массив средних размеров свечей вниз double symbolAvrSeriesLength[][TFN]; // Массив средних длин серий int symbolCountSeries2[][TFN]; // Массив количества серий длины 2 int symbolCountSeries3[][TFN]; // Массив количества серий длины 3 int symbolCountSeries4[][TFN]; // Массив количества серий длины 4 int symbolCountSeries5[][TFN]; // Массив количества серий длины 5 int symbolCountSeries6[][TFN]; // Массив количества серий длины 6 int symbolCountSeries7[][TFN]; // Массив количества серий длины 7 int symbolCountSeries8[][TFN]; // Массив количества серий длины 8 int symbolCountSeries9[][TFN]; // Массив количества серий длины 9 CConsoleDialog *dialog; // Диалог для вывода текста с результатами // Массив всех таймфреймов ENUM_TIMEFRAMES tfValues[] = { PERIOD_M1, PERIOD_M2, PERIOD_M3, PERIOD_M4, PERIOD_M5, PERIOD_M6, PERIOD_M10, PERIOD_M12, PERIOD_M15, PERIOD_M20, PERIOD_M30, PERIOD_H1, PERIOD_H2, PERIOD_H3, PERIOD_H4, PERIOD_H6, PERIOD_H8, PERIOD_H12, PERIOD_D1, PERIOD_W1, PERIOD_MN1 }; //+------------------------------------------------------------------+ //| Преобразование строкового названия в таймфрейм | //+------------------------------------------------------------------+ ENUM_TIMEFRAMES StringToTimeframe(string s) { // Если в строке есть символ "_", то оставляем только символы, идущие после него int pos = StringFind(s, "_"); if(pos != -1) { s = StringSubstr(s, pos + 1); } // Переводим в верхний регистр StringToUpper(s); // Массивы соответствующих строковых названий таймфреймов string keys[] = {"M1", "M2", "M3", "M4", "M5", "M6", "M10", "M12", "M15", "M20", "M30", "H1", "H2", "H3", "H4", "H6", "H8", "H12", "D1", "W1", "MN1" }; // Ищем соответствие и возвращаем, если нашли FOREACH(keys) { if(keys[i] == s) return tfValues[i]; } return PERIOD_CURRENT; } //+------------------------------------------------------------------+ //| Преобразование таймфрейма в строковое название | //+------------------------------------------------------------------+ string TimeframeToString(ENUM_TIMEFRAMES tf) { // Получаем название таймфрейма вида 'PERIOD_*' string s = EnumToString(tf); // Возвращаем часть названия после символа '_' return StringSubstr(s, StringFind(s, "_") + 1); } //+------------------------------------------------------------------+ //| Преобразование таймфрейма в индекс в массиве всех таймфреймов | //+------------------------------------------------------------------+ int TimeframeToIndex(ENUM_TIMEFRAMES tf) { // Ищем соответствие и возвращаем, если нашли FOREACH(tfValues) { if(tfValues[i] == tf) return i; } return WRONG_VALUE; } //+------------------------------------------------------------------+ //| Инициализация советника | //+------------------------------------------------------------------+ int OnInit(void) { // Наполняем массив символов для расчётов из входных параметров SPLIT(symbols_, g_symbols); // Если символы не указаны, используем один текущий символ if(ArraySize(g_symbols) == 0) { APPEND(g_symbols, Symbol()); } // Количество символов для расчётов int nSymbols = ArraySize(g_symbols); // Инициализируем массивы для расчётных значений Initialize(nSymbols); // Наполняем массив названий таймфреймов из входных параметров string strTimeframes[]; SPLIT(timeframes_, strTimeframes); ArrayResize(g_timeframes, 0); // Если таймфреймы не указаны, используем один текущий таймфрейм if(ArraySize(strTimeframes) == 0) { APPEND(strTimeframes, TimeframeToString(Period())); } // Наполняем массив таймфреймов из массива названий таймфреймов FOREACH(strTimeframes) { ENUM_TIMEFRAMES tf = StringToTimeframe(strTimeframes[i]); if(tf != PERIOD_CURRENT) { APPEND(g_timeframes, tf); } } // Создаём и запускаем диалог для вывода результатов dialog = new CConsoleDialog(); dialog.Create("Symbols Informer"); dialog.Run(); // Выполняем принудительный перерасчёт Calculate(true); // Показываем результаты Show(); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { if(!!dialog) { dialog.Destroy(); delete dialog; } ChartRedraw(); DestroyNewBar(); // Денициализация объектов отслеживания нового бара } //+------------------------------------------------------------------+ //| Обработка событий | //+------------------------------------------------------------------+ void OnChartEvent(const int id, // event ID const long & lparam, // event parameter of the long type const double & dparam, // event parameter of the double type const string & sparam) { // event parameter of the string type if(!!dialog) { dialog.ChartEvent(id, lparam, dparam, sparam); } } //+------------------------------------------------------------------+ //| Expert OnTick function | //+------------------------------------------------------------------+ void OnTick(void) { // Определяем новый бар по всем нужным символам и таймфреймам bool isNewBar = UpdateNewBar(); // Если нового бара нигде нет, то ничего не делаем if(!isNewBar) return; // Иначе выполняем перерасчёт и показываем результаты Calculate(); Show(); } //+------------------------------------------------------------------+ //| Ининциализация массивов для расчётных значений | //+------------------------------------------------------------------+ void Initialize(int nSymbols) { // Устанавливаем размеры всех массивов ArrayResize(symbolAvrCandleSizes, nSymbols); ArrayResize(symbolAvrBuyCandleSizes, nSymbols); ArrayResize(symbolAvrSellCandleSizes, nSymbols); ArrayResize(symbolAvrSeriesLength, nSymbols); ArrayResize(symbolCountSeries2, nSymbols); ArrayResize(symbolCountSeries3, nSymbols); ArrayResize(symbolCountSeries4, nSymbols); ArrayResize(symbolCountSeries5, nSymbols); ArrayResize(symbolCountSeries6, nSymbols); ArrayResize(symbolCountSeries7, nSymbols); ArrayResize(symbolCountSeries8, nSymbols); ArrayResize(symbolCountSeries9, nSymbols); // Заполняем начальными значениями все элементы всех массивов FOREACH(symbolAvrCandleSizes) { ArrayInitialize(symbolAvrCandleSizes, 0); ArrayInitialize(symbolAvrBuyCandleSizes, 0); ArrayInitialize(symbolAvrSellCandleSizes, 0); ArrayInitialize(symbolAvrSeriesLength, 0); ArrayInitialize(symbolCountSeries2, 0); ArrayInitialize(symbolCountSeries3, 0); ArrayInitialize(symbolCountSeries4, 0); ArrayInitialize(symbolCountSeries5, 0); ArrayInitialize(symbolCountSeries6, 0); ArrayInitialize(symbolCountSeries7, 0); ArrayInitialize(symbolCountSeries8, 0); ArrayInitialize(symbolCountSeries9, 0); } } //+------------------------------------------------------------------+ //| Расчёт всех величин | //+------------------------------------------------------------------+ void Calculate(bool force = false) { string symbol; ENUM_TIMEFRAMES tf; // Для каждого символа и таймфрейма FOREACH_AS(g_symbols, symbol) { FOREACH_AS(g_timeframes, tf) { // Если для данного символа и таймфрейма наступил новый бар, то if(IsNewBar(symbol, tf) || force) { // Находим количество свечей для расчёта int n = PeriodSeconds(mainTimeframe_) * mainLength_ / PeriodSeconds(tf); // Рассчитываем средние размеры свечей CalculateAvrSizes(symbol, tf, n); // Рассчитываем длины серий свечей CalculateSeries(symbol, tf, n); } } } } //+------------------------------------------------------------------+ //| Свеча вверх? | //+------------------------------------------------------------------+ bool IsBuyRate(const MqlRates &rate) { return rate.open <= rate.close; } //+------------------------------------------------------------------+ //| Свеча вниз? | //+------------------------------------------------------------------+ bool IsSellRate(const MqlRates &rate) { return rate.open >= rate.close; } //+------------------------------------------------------------------+ //| Расчёт средних размеров свечей | //+------------------------------------------------------------------+ void CalculateAvrSizes(string symbol, ENUM_TIMEFRAMES tf, int n) { // Находим индекс, который используется для нужного символа int s; FIND(g_symbols, symbol, s); // Находим индекс, который используется для нужного таймфрейма int t = TimeframeToIndex(tf); // Массив для свечей MqlRates rates[]; // Копируем нужное количество свечей в массив int res = CopyRates(symbol, tf, 1, n, rates); // Если всё скопировалось, то if(res == n) { // Количество свечей вверх и вниз int nBuy = 0, nSell = 0; // Обнуляем элементы для расчётных средних значений symbolAvrCandleSizes[s][t] = 0; symbolAvrBuyCandleSizes[s][t] = 0; symbolAvrSellCandleSizes[s][t] = 0; // Для всех свечей FOREACH(rates) { // Находим размер свечи double size = rates[i].high - rates[i].low; // Добавляем его в суммарный размер всех свечей symbolAvrCandleSizes[s][t] += size; // Если это свеча вверх, то учитываем её if(IsBuyRate(rates[i])) { symbolAvrBuyCandleSizes[s][t] += size; nBuy++; } // Если это свеча вниз, то учитываем её if(IsSellRate(rates[i])) { symbolAvrSellCandleSizes[s][t] += size; nSell++; } } // Получаем размер одного пункта для символа double point = SymbolInfoDouble(symbol, SYMBOL_POINT); // Находим средние значения в пунктах symbolAvrCandleSizes[s][t] /= n * point; symbolAvrBuyCandleSizes[s][t] /= nBuy * point; symbolAvrSellCandleSizes[s][t] /= nSell * point; // Округляем их до целых пунктов symbolAvrCandleSizes[s][t] = MathRound(symbolAvrCandleSizes[s][t]); symbolAvrBuyCandleSizes[s][t] = MathRound(symbolAvrBuyCandleSizes[s][t]); symbolAvrSellCandleSizes[s][t] = MathRound(symbolAvrSellCandleSizes[s][t]); } } //+------------------------------------------------------------------+ //| Расчёт длин серий свечей | //+------------------------------------------------------------------+ void CalculateSeries(string symbol, ENUM_TIMEFRAMES tf, int n) { // Находим индекс, который используется для нужного символа int s; FIND(g_symbols, symbol, s); // Находим индекс, который используется для нужного таймфрейма int t = TimeframeToIndex(tf); // Массив для свечей MqlRates rates[]; // Копируем нужное количество свечей в массив int res = CopyRates(symbol, tf, 1, n, rates); // Если всё скопировалось, то if(res == n) { // Текущая длина серии int curLen = 0; // Направление предыдущей свечи bool prevIsBuy = false; bool prevIsSell = false; // Массив количеств серий разной длины (индекс = длина серии) int seriesLens[]; // Устанавливаем размер и инициализируем ArrayResize(seriesLens, 100); ArrayInitialize(seriesLens, 0); // Для всех свечей FOREACH(rates) { // Определяем направление свечи bool isBuy = IsBuyRate(rates[i]); bool isSell = IsSellRate(rates[i]); // Если направление совпадает с предыдущим, то if((isBuy && prevIsBuy) || (isSell && prevIsSell)) { // Увеличиваем длину серии curLen++; } else { // Иначе если длина попадает в требуемый диапазон, то if(curLen > 1 && curLen < 100) { // Увеличивем счётчик серий этой длины seriesLens[curLen]++; } // Сбрасываем текущую длину серии curLen = 1; } // Запоминаем направление текущей свечи как предыдущее prevIsBuy = isBuy; prevIsSell = isSell; } // Инициализируем элемент массива для средней длины серии symbolAvrSeriesLength[s][t] = 0; int count = 0; // Для всех длин серий находим сумму и количество FOREACH(seriesLens) { symbolAvrSeriesLength[s][t] += seriesLens[i] * i; count += seriesLens[i]; } // Вычисляем среднюю длину серий свечей symbolAvrSeriesLength[s][t] /= (count > 0 ? count : 1); // Копируем значения длин серий в итоговые массивы symbolCountSeries2[s][t] = seriesLens[2]; symbolCountSeries3[s][t] = seriesLens[3]; symbolCountSeries4[s][t] = seriesLens[4]; symbolCountSeries5[s][t] = seriesLens[5]; symbolCountSeries6[s][t] = seriesLens[6]; symbolCountSeries7[s][t] = seriesLens[7]; symbolCountSeries8[s][t] = seriesLens[8]; symbolCountSeries9[s][t] = seriesLens[9]; } } //+------------------------------------------------------------------+ //| Получение всех результатов в виде текста | //+------------------------------------------------------------------+ string TextComment() { string text = ""; text += StringFormat("%-20s |", "Таймфреймы"); FOREACH(g_timeframes) { int t = TimeframeToIndex(g_timeframes[i]); text += StringFormat("%10s |", TimeframeToString(g_timeframes[i])); } text += StringFormat("\n%20s |", "Кол-во свечей"); FOREACH(g_timeframes) { int n = PeriodSeconds(mainTimeframe_) * mainLength_ / PeriodSeconds(g_timeframes[i]); text += StringFormat("%10.0f |", n); } FOREACH(g_symbols) { int s = i; text += StringFormat("\n======================================================================", g_symbols[i]); text += StringFormat("\n%-20s |", g_symbols[i]); FOREACH(g_timeframes) { int t = TimeframeToIndex(g_timeframes[i]); text += StringFormat("%10s |", TimeframeToString(g_timeframes[i])); } text += StringFormat("\n%20s", "Средний размер свечей в пунктах"); text += StringFormat("\n%20s |", "Все"); FOREACH(g_timeframes) { int t = TimeframeToIndex(g_timeframes[i]); text += StringFormat("%10.0f |", symbolAvrCandleSizes[s][t]); } text += StringFormat("\n%20s |", "Buy"); FOREACH(g_timeframes) { int t = TimeframeToIndex(g_timeframes[i]); text += StringFormat("%10.0f |", symbolAvrBuyCandleSizes[s][t]); } text += StringFormat("\n%20s |", "Sell"); FOREACH(g_timeframes) { int t = TimeframeToIndex(g_timeframes[i]); text += StringFormat("%10.0f |", symbolAvrSellCandleSizes[s][t]); } text += StringFormat("\n%-20s |", "Средняя длина серии"); FOREACH(g_timeframes) { int t = TimeframeToIndex(g_timeframes[i]); text += StringFormat("%10.2f |", symbolAvrSeriesLength[s][t]); } text += StringFormat("\n--------------------------------------------------------------------", g_symbols[i]); text += StringFormat("\n%-20s |", "Кол-во серий длины 2"); FOREACH(g_timeframes) { int t = TimeframeToIndex(g_timeframes[i]); text += StringFormat("%10.0f |", symbolCountSeries2[s][t]); } text += StringFormat("\n%-20s |", "Кол-во серий длины 3"); FOREACH(g_timeframes) { int t = TimeframeToIndex(g_timeframes[i]); text += StringFormat("%10.0f |", symbolCountSeries3[s][t]); } text += StringFormat("\n%-20s |", "Кол-во серий длины 4"); FOREACH(g_timeframes) { int t = TimeframeToIndex(g_timeframes[i]); text += StringFormat("%10.0f |", symbolCountSeries4[s][t]); } text += StringFormat("\n%-20s |", "Кол-во серий длины 5"); FOREACH(g_timeframes) { int t = TimeframeToIndex(g_timeframes[i]); text += StringFormat("%10.0f |", symbolCountSeries5[s][t]); } text += StringFormat("\n%-20s |", "Кол-во серий длины 6"); FOREACH(g_timeframes) { int t = TimeframeToIndex(g_timeframes[i]); text += StringFormat("%10.0f |", symbolCountSeries6[s][t]); } text += StringFormat("\n%-20s |", "Кол-во серий длины 7"); FOREACH(g_timeframes) { int t = TimeframeToIndex(g_timeframes[i]); text += StringFormat("%10.0f |", symbolCountSeries7[s][t]); } text += StringFormat("\n%-20s |", "Кол-во серий длины 8"); FOREACH(g_timeframes) { int t = TimeframeToIndex(g_timeframes[i]); text += StringFormat("%10.0f |", symbolCountSeries8[s][t]); } text += StringFormat("\n%-20s |", "Кол-во серий длины 9"); FOREACH(g_timeframes) { int t = TimeframeToIndex(g_timeframes[i]); text += StringFormat("%10.0f |", symbolCountSeries9[s][t]); } } text = StringFormat("Основной таймфрейм: %s (%d свечей)\n%s", TimeframeToString(mainTimeframe_), mainLength_, text); return text; } //+------------------------------------------------------------------+ //| Показ результатов | //+------------------------------------------------------------------+ void Show() { // Получаем результаты в виде текста string text = TextComment(); // Показываем его на графике в диалоговом окне dialog.Text(text); } //+------------------------------------------------------------------+