SymbolsInformer/SymbolsInformer.mq5

540 lines
40 KiB
MQL5

//+------------------------------------------------------------------+
//| 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);
}
//+------------------------------------------------------------------+