540 lines
40 KiB
MQL5
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);
|
|
}
|
|
//+------------------------------------------------------------------+
|