Возможности Мастера MQL5, которые вам нужно знать (Часть 57): Обучение с учителем совместно со скользящей средней и стохастическим осциллятором
Введение
Мы продолжаем исследовать простые паттерны, которые можно реализовать с помощью советников, собранных с помощью Мастера MQL. Главная цель всегда заключается в том, чтобы опробовать или протестировать идеи. Советники, собранные вручную требуют длительного тестирования перед запуском на реальном счете, а советники, собранные в Мастере, позволяют быстро запускать тесты с меньшими затратами времени на написание кода на начальном этапе.
Машинное обучение сейчас находится в центре внимания, и мы уже рассматривали некоторые его аспекты в предыдущих статьях этой серии. В этой и будущих статьях мы продолжим рассматривать некоторые более технические аспекты, однако они будут служить лишь фоном, поскольку мы будем больше сосредоточены на более известных и устоявшихся моделях индикаторов.
Кроме того, в контексте машинного обучения наши статьи будут посвящены трем основным направлениям обучения в рамках единого цикла статей. Для начала рассмотрим обучение с учителем, а в качестве индикаторных моделей будем использовать комбинацию индикатора тренда и индикатора импульса. Мы рассмотрим скользящую среднюю и стохастик.
В обучении с учителем мы будем стремиться реализовать каждый паттерн в отдельной нейронной сети. Как уже отмечалось в недавних статьях, эти алгоритмы лучше написаны и обучены на Python, чем на MQL5. Прирост эффективности просто зашкаливает. Python также позволяет легко проводить перекрестную проверку после обучающей сессии, поэтому мы будем выполнять ее для каждого паттерна.
Хотя в Python перекрестная проверка осуществляется путем сравнения значения функции потерь в тестовом запуске со значением функции потерь в последней эпохе обучения, этого недостаточно для оценки перекрестной проверки текущих весов и смещений сети.
Поэтому мы будем проводить форвард-тесты в тестере стратегий MetaTrader 5 с экспортированными сетями ONNX. В текущей статье в качестве данных для обучения, передаваемых в Python из MetaTrader 5, будут использоваться цены (x и y) за 2023 год для валютной пары EURJPY. Таким образом, дальнейший шаг будет предпринят в отношении того же символа, но уже в 2024 году. Мы проводим анализ на ежедневном временном интервале.
Сочетание скользящей средней (MA) со стохастическим осциллятором может генерировать разнообразные торговые сигналы. В целях тестирования и анализа мы будем рассматривать только 10 лучших сигнальных паттернов, которые трейдеры могут использовать при сочетании этих индикаторов.
Подход с использованием типов машинного обучения
Конвейер машинного обучения можно рассматривать как состоящий из трех взаимосвязанных этапов: обучение с учителем (Supervision), обучение с подкреплением (Reinforcement) и вывод результатов (Inference). Давайте еще раз вспомним определения.
Обучение с учителем (или фаза обучения) — это процесс, в ходе которого модель изучает исторические закономерности на основе размеченных данных (независимые или входные данные часто обозначаются как x, а независимые данные для прогнозирования также часто обозначаются как y).
Между тем, обучение с подкреплением (которое можно рассматривать как фазу обучения и оптимизации в процессе использования) – это этап, на котором модель совершенствуется посредством взаимодействия с окружающей средой и оптимизации своих действий для получения долгосрочных вознаграждений (почти как обратное распространение ошибки во время форвард-теста).
И наконец, этап обучения на основе вывода (или этап обучения без учителя) – это когда модель обобщает полученные ранее знания и применяет их к новым данным и новым задачам.
Для наглядности давайте рассмотрим, как эти 3 этапа могут быть связаны при решении различных групп проблем. Мы рассмотрим примеры прогноза погоды и финансовых временных рядов.
Обучение с учителем:
Цель обучения с учителем — создание прогностической модели на основе размеченных данных. Этот процесс включает в себя сбор и предварительную обработку исторических данных. Затем следует извлечение признаков, которое можно рассматривать как форму нормализации исторических данных до диапазона или формата, пригодного для ввода в нейронную сеть или обучающую модель. После этого модель обучается на сетях c долговременной и кратковременной памятью (LSTM), XGBoost или трансформерах с использованием размеченных данных. Маркировка означает, что мы помечаем независимые данные (известные исходные данные, используемые для прогнозирования) и зависимые данные (данные, которые мы хотим спрогнозировать) различными метками. Первоначальная цель здесь — минимизация потерь, то есть сведение разницы между прогнозируемым и фактическим значением к минимуму с помощью градиентного спуска.
В контролируемом обучении может использоваться перекрестная проверка, результаты которой могут обосновать необходимость полномасштабного применения модели. Однако, в рамках рассматриваемых нами трехэтапных процессов, заключительным этапом контролируемого обучения будет выбор модели и настройка гиперпараметров, где, по сути, оптимальная скорость обучения и размер обучающего пакета выбираются на основе того, что показало наилучшие результаты. Кроме того, архитектура, касающаяся типов активации и даже размеров слоев, выбирается в том случае, если эти варианты были протестированы на данном этапе обучения.
Для иллюстрации рассмотрим систему прогнозирования погоды: данные будут представлять собой прошлые значения температуры, влажности или давления; модель может быть основана на случайном лесу или сверточной нейронной сети (CNN); результатом будет система прогнозирования температурных трендов. В случае финансовых временных рядов, данные для прогнозирования будут представлять собой исторические цены с проектированием признаков для присвоения значений индикаторам. Модель может быть основана на LSTM; функция потерь — среднеквадратичная ошибка (MSE). Результатом будет обученная модель, которая прогнозирует следующую цену на основе прошлых наблюдений.
Обучение с подкреплением:
После проведения обучения с учителем в основном на размеченных исторических данных возникает вопрос: сможет ли модель эффективно работать с текущими или будущими данными? Или же она даже сможет адаптироваться, если в будущем условия изменятся по сравнению с теми, на которых она тренировалось? Для решения этой задачи применяется обучение с подкреплением. Статические модели обычно испытывают трудности в динамических средах, поэтому обучение с подкреплением помогает оптимизировать процесс принятия решений в будущем.
Таким образом, цель обучения с подкреплением состоит в улучшении прогнозов путем оптимизации принятия решений на основе обратной связи. Иными словами, как только у нас появится, скажем, нейронная сеть, прогнозирующая изменения цен, мы перейдем к разработке сетей политики и оценки, способных использовать эти изменения цен в качестве состояний. We are thus in a position where we separate price changes from trader actions, with the introduction of states and actions.
Эти примеры относятся к прогнозированию финансовых временных рядов. Если бы речь шла о погоде, как мы показали выше, то действиями и состояниями могли бы быть количество осадков и объем посева/посадки основной культуры. Более ощутимым и желаемым результатом прогнозирования финансовых временных рядов часто является прибыль/убытки от действий, предпринятых в соответствии с прогнозами сети. В качестве примера, иллюстрирующего погодные условия, можно использовать урожайность основной сельскохозяйственной культуры.
Как мы уже обсуждали ранее, процесс обучения с подкреплением включает в себя не только обучение и обновление критикующей сети, чтобы мы могли лучше прогнозировать наши прибыль/убытки, но и предоставление изменений, которые дополнительно уточняют веса и смещения сети политики.
Вкратце, процесс обучения с подкреплением включает в себя: настройку взаимодействия агента и среды, где определяются состояния, действия и вознаграждения. Как уже упоминалось, они будут формироваться или определяться на основе модели, обученной на этапе обучения с учителем (как показано в примерах выше). Кроме того, существует обучение на основе политик (policy-learning), в котором сеть актеров или аналогичный алгоритм оптимизируется для сопоставления состояний с требуемыми действиями. Также существует механизм оптимизации, основанный на вознаграждении (Reward based), который может принимать форму алгоритма оценки или сети критика, корректирующей прогнозы на основе долгосрочной прибыльности. И наконец, агент, который уравновешивает исследование (испытание различных вариантов политики с целью получения новых знаний) с эксплуатацией (приверженность тому, что хорошо работало в прошлом).
Продолжая примеры, начатые выше, на этапе обучения с учителем исходная модель предсказывает количество осадков на основе температуры, влажности и т. д., а также выдает прогнозируемые значения количества осадков, которые нам необходимы. Поскольку количество осадков является ключевым фактором, определяющим целесообразность посева урожая, обучение с подкреплением вводит дополнительный уровень интерпретации для принятия решения. Результаты прогноза осадков представляются в виде состояний. Если прогнозы этих состояний относительно количества осадков различаются в разных регионах или в разные моменты времени, то их интерпретация при принятии решения о необходимости посева может быть оптимизирована для получения выгоды, которой в данном случае будет урожайность. В результате получится прогностическая модель, которая будет самосовершенствоваться в ответ на реальные изменения погоды. Таким образом, если этап обучения с учителем можно назвать школой, то обучение с подкреплением - обучение на рабочем месте.
Для финансового прогнозирования мы бы использовали модель прогнозирования изменений цен на этапе обучения с учителем, выступающего в качестве состояния. Эти изменения могут представлять собой многомерный вектор, даже если, например, они охватывают более одного таймфрейма. Действия, которые будут предприняты трейдером или советником, — это покупка, продажа или нейтральная позиция. На основе действий, предпринятых для каждого состояния (изменения цен), мы затем, используя сеть критиков или алгоритм, сопоставим состояния и действия с ожидаемой прибылью для каждого из них. После этого политику (сопоставление состояний с действиями) можно постепенно обновлять, балансируя между исследованием и эксплуатацией, чтобы лучше интерпретировать результаты модели на этапе обучения с учителем (которые мы представляем в виде состояний).
Однако такое обучение в рабочих условиях по умолчанию происходит очень медленно, поскольку сами данные в реальном времени поступают крайне медленно. Таким образом, если обучение с учителем охватывало исторический период в 10 лет, то обучение с подкреплением в течение 1 года, теоретически, также займет 1 год! Это моделирования или более сложного класса среды.
Имитируя условия реальных данных на истории, мы не только ускоряем темпы обучения с подкреплением, но и охватываем гораздо больший объем данных, что должно предоставить нам более устойчивые модели.
Таким образом, для прогнозирования финансовых рядов это означает, что мы можем проводить тестовые запуски на тех же данных, которые использовали на этапе обучения с учителем, но теперь оптимизировать сети обучения с подкреплением для получения прибыли, основываясь на действиях, предпринятых с учетом прогнозов состояния (или изменения цены), сделанных с помощью сети на этапе обучения с учителем.
Вывод:
Получив две модели: одну для построения базовых прогнозов (в обучении с учителем) и другую для лучшей интерпретации и применения этих прогнозов (в обучении с подкреплением), мы переходим к третьему этапу — выводу, где, по сути, мы берем эти знания и модели и применяем их к другой области или, в нашем случае, к другому торговому символу.
Это также называется обобщением на основе ранее не встречавшихся данных, применение полученных знаний в реальных условиях, их совершенствование с течением времени и выявление ранее неизвестных закономерностей без использования меток в данных. На этом этапе ключевую роль играют графы и автокодировщики, а также кластеризация.
На этих трех этапах, с каждым последующим переходом, вычислительная нагрузка уменьшается. Что, по сути, и является оправданием для этих этапов. В этих сериях статей уже рассматривались методы, использующие обучение без учителя, однако я, возможно, вернусь к ним в ближайшее время, чтобы обсудить, как они могут «продолжать» обучение с подкреплением. Хотя обучение с подкреплением может использоваться автономно для прогнозирования, описанные этапы призваны доказать, что его использование в сочетании с другими режимами обучения может быть более эффективным.
Сочетание скользящей средней (MA) со стохастическим осциллятором может генерировать разнообразные торговые сигналы. Мы рассмотрим 10 наиболее эффективных сигнальных паттернов, которые формируются при сочетании этих индикаторов и которые могут быть использованы трейдерами.
Пересечение скользящих средних + стохастический осциллятор перекупленности/перепроданности
Паттерн возникает в результате сочетания индикатора следования за трендом (скользящей средней) и осциллятора импульса (стохастического осциллятора) и, как правило, обеспечивает высоковероятные торговые сигналы. Сигнал на покупку представляет собой бычье пересечение (когда быстрая скользящая средняя пересекает запаздывающую скользящую среднюю сверху вниз), подтверждающее потенциальный восходящий тренд, при этом стохастический осциллятор находится ниже 20. Это обычно указывает на то, что актив перепродан, и цена готова к развороту. Сигнал на продажу, с другой стороны, представляет собой медвежье пересечение. Быстрая скользящая средняя пересекает медленную сверху вниз с последующим падением, что само по себе потенциально является индикатором нисходящего тренда. Поскольку это также подтверждается тем, что стохастический осциллятор находится выше уровня 80, что является признаком перекупленности, это формирует сильный сигнал к продаже.
Благодаря немного измененному формату класса сигналов, где мы используем показания индикаторов в качестве характеристик для сетей, у нас теперь есть функция для создания одиночного паттерна, которую мы реализуем в коде следующим образом:
//+------------------------------------------------------------------+ //| Check for Pattern. | //+------------------------------------------------------------------+ double CSignal_MA_STO::IsPattern(int Index, ENUM_POSITION_TYPE T) { vectorf _x = Get(Index, m_time.GetData(X()), m_close, m_ma, m_ma_lag, m_sto); vectorf _y(1); _y.Fill(0.0); ResetLastError(); if(!OnnxRun(m_handles[Index], ONNX_NO_CONVERSION, _x, _y)) { printf(__FUNCSIG__ + " failed to get y forecast, err: %i", GetLastError()); return(double(_y[0])); } //printf(__FUNCSIG__+" y: "+DoubleToString(_y[0],2)); if(T == POSITION_TYPE_BUY && _y[0] > 0.5f) { _y[0] = 2.0f * (_y[0] - 0.5f); } else if(T == POSITION_TYPE_SELL && _y[0] < 0.5f) { _y[0] = 2.0f * (0.5f - _y[0]); } return(double(_y[0])); }
Мы включили файл '57_X.mqh', содержащий функцию для получения значений скользящего среднего и стохастического индекса, которые являются входными данными для наших нейронных сетей. Мы рассматриваем до 10 различных паттернов. Это означает, что мы будем использовать до 10 различных сетей. Таким образом, наша функция Get в этом включаемом файле будет возвращать до 10 различных наборов данных, по одному для каждой сети.
Функция Get возвращает вектор с плавающей запятой (vectorf), который можно легко ввести в нейронную сеть ONNX. Эту же функцию можно использовать в качестве альтернативы библиотеке импорта MetaTrader 5 в Python, которая требует нормализации цены до формата, аналогичного представленному здесь, прежде чем сеть сможет использовать ее в качестве входных данных. Наша нейронная сеть на Python будет довольно простой, и ее можно реализовать в коде следующим образом:
class SimpleNeuralNetwork(nn.Module): def __init__(self): super(SimpleNeuralNetwork, self).__init__() self.fc1 = nn.Linear(feature_size, 256) # Input layer to hidden layer 1 self.fc2 = nn.Linear(256, 256) # Hidden layer 1 to hidden layer 2 self.fc3 = nn.Linear(256, state_size) # Hidden layer 2 to output layer self.sigmoid = nn.Sigmoid() def forward(self, x): x = self.sigmoid(self.fc1(x)) # Activation for hidden layer 1 x = self.sigmoid(self.fc2(x)) # Activation for hidden layer 2 x = self.fc3(x) # Output layer (no activation) return x
Параметры feature_size и state_size — это размеры входных и выходных данных нашей сети. Результат будет стандартным для всех 10 паттернов, поскольку мы будем ориентироваться на значение в диапазоне от 0,0 до 1,0. Значение ниже 0,5 будет интерпретироваться как потенциальное снижение, значение выше 0,5 — как рост, а 0,5 — как отсутствие изменений.
Однако при настройке параметров не будут использоваться стандартные размеры векторов для входных данных, поскольку каждый паттерн может иметь различное количество условий. В нашем первом паттерне их 4. Поэтому мы присваиваем входной вектор сети внутри функции Get следующим образом.
if(Index == 0) { if(CopyBuffer(M.Handle(), 0, T, 2, _ma) >= 2 && CopyBuffer(M_LAG.Handle(), 0, T, 2, _ma_lag) >= 2 && CopyBuffer(S.Handle(), 0, T, 1, _sto_k) >= 1) { _v[0] = ((_ma_lag[1] > _ma[1] && _ma_lag[0] < _ma[0]) ? 1.0f : 0.0f); _v[1] = ((_sto_k[0] <= 20.0) ? 1.0f : 0.0f); _v[2] = ((_sto_k[0] >= 80.0) ? 1.0f : 0.0f); _v[3] = ((_ma_lag[1] < _ma[1] && _ma_lag[0] > _ma[0]) ? 1.0f : 0.0f); } }
Для бычьего паттерна существуют 2 условия, по одному для каждого индикатора, и то же самое относится к медвежьему. При задании входного вектора мы просто проверяем, выполняется ли каждое из условий. Учитывая зеркальный или булевый характер этих условий, максимальное значение, которое может быть достигнуто в любой момент времени, равно 2.
Тем не менее, все они представлены в векторе, поскольку это может быть полезно для нейронной сети. Кроме того, эти 4 условия в случае паттерна 0 можно дополнительно разделить на 6, поскольку первое условие принимает 2 аргумента, как и четвертое. Читатель может поэкспериментировать с исходным кодом, приложенным в конце статьи.
В результате выполнения обучающего и тестового запусков в Python были получены следующие значения потерь для обоих запусков:
Epoch 10/10, Train Loss: 0.2498
Test Loss: 0.2593 Значение ошибки на тестовом наборе данных меньше, чем начальное значение ошибки для 1-й эпохи (не указано), но все же больше, чем значение ошибки для 10-й эпохи. Данные о ценах, использованные для обучения и проверки модели, полностью относятся к 2023 году. Проведем фловард-тест за 2024 год, оптимизировав подходящие значения открытия/закрытия, а также пороговые значения паттерна 0 в 2023 году. Результаты следующие.

Совершаются только сделки на покупку. Для проверки возможности осуществления подобного тестирования с учетом коротких позиций необходимо провести обучение на более крупных наборах данных. Кроме того, форвард-тестирование использует тренировочные веса на периоде в 80% 2023 года, а не всего года. Однако они применяются на протяжении всего 2024 года.
Пересечение ценой скользящей средней + стохастик подтверждает тренд
В следующем паттерне для определения условий покупки и продажи используются логика пересечения цен скользящих средних и направление стохастика. Сигнал на покупку возникает, когда цена пересекает скользящую среднюю сверху вниз, а стохастический осциллятор %K пересекает %D или находится выше него. Таким образом, сигналом на продажу будет пересечение ценой скользящей средней ниже уровня %K, при этом %K находится ниже %D.
После импорта и форматирования данных о ценах в качестве входных данных для нашей нейронной сети, входной вектор будет аналогичен выходным данным нашей функции Get, как показано ниже:
else if(Index == 1) { if(C.GetData(T, 2, _c) >= 2 && CopyBuffer(M.Handle(), 0, T, 2, _ma) >= 2 && CopyBuffer(S.Handle(), 0, T, 2, _sto_k) >= 2 && CopyBuffer(S.Handle(), 1, T, 2, _sto_d) >= 2) { _v[0] = ((_c[1] < _ma[1] && _c[0] > _ma[0]) ? 1.0f : 0.0f); _v[1] = ((_sto_k[1] < _sto_d[1] && _sto_k[0] > _sto_d[0]) ? 1.0f : 0.0f); _v[2] = ((_sto_k[1] > _sto_d[1] && _sto_k[0] < _sto_d[0]) ? 1.0f : 0.0f); _v[3] = ((_c[1] > _ma[1] && _c[0] < _ma[0]) ? 1.0f : 0.0f); } }
Мы используем размер 4, но всего условий больше 4, поскольку, как упоминалось выше, некоторые условия требуют более одного условия. В данном случае все 4 условия принимают по 2 аргумента, что означает, что мы могли бы использовать входные данные размером 8 для сети.
Обучение нейронной сети осуществляется с помощью функции Train, показанной ниже:
# Train function def Train(model, train_loader, optimizer, loss_fn, epochs): device = T.device('cuda:0' if T.cuda.is_available() else 'cpu') model.to(device) for epoch in range(epochs): model.train() train_loss = 0.0 for batch_idx, (data, target) in enumerate(tqdm(train_loader, desc=f"Epoch {epoch + 1}/{epochs} (Train)")): # Step 1: Ensure proper tensor dimensions data = data.view(-1, 1, feature_size) # Step 2: Verify dimensions expected_shape = [feature_size] actual_shape = list(data.shape[2:]) if actual_shape != (expected_shape): raise RuntimeError(f"Invalid spatial dimensions after reshaping. Got {data.shape[2:]}, expected {[x_data.shape[1]]}") # Step 3: Move to device and forward pass data, target = data.to(device), target.to(device) optimizer.zero_grad() output = model(data) target = target.view(-1, 1, state_size) loss = loss_fn(output, target) # Step 4: Backpropagation loss.backward() optimizer.step() train_loss += loss.item() train_loss /= len(train_loader) print(f"Epoch {epoch + 1}/{epochs}, Train Loss: {train_loss:.4f}")
Train проходит по 2023 году со следующими записями функции потерь:
Epoch 10/10, Train Loss: 0.2491
Test Loss: 0.2592 Форвард-тест за 2024 год позволяет получить следующие результаты:

Результаты форвард-теста многообещающие, однако коротких позиций чуть меньше, чем длинных. Дополнительное обучение должно решить эту проблему. Этот паттерн отражает ранние движения тренда, поскольку целью является пересечение ценой скользящей средней, что может сигнализировать о сдвиге тренда и должно отставать менее чем на два пересечения скользящих средних, как мы видели в паттерне 0. Кроме того, он отфильтровывает множество ложных пробоев, поскольку стохастический осциллятор используется в качестве подтверждения, что помогает избежать преждевременных сделок.
У этого подхода также есть свои недостатки и ограничения. Во-первых, запаздывающий характер скользящей средней, особенно при использовании более длительных периодов, может привести к медленному открытию сделок. По умолчанию мы используем период 8 на дневном таймфрейме, но это параметр, который можно оптимизировать для пользовательского класса сигналов, поэтому читатели могут точно настроить его под свои нужды. Во-вторых, стохастический осциллятор известен своей высокой волатильностью, особенно на рынках с боковым движением. Это может привести к большому количеству ложных подтверждений. Кроме того, в ситуациях, когда актив уже находится в сильном тренде, ожидание пересечения скользящей средней может привести к упущению множества ранних точек входа.
Что касается оптимизации периодов индикатора, то короткая скользящая средняя в диапазоне менее 20 периодов, скорее всего, будет реагировать на изменения цен быстрее, чем, скажем, более длинная скользящая средняя в диапазоне от 50 до 200 периодов. Поэтому пользователям необходимо учитывать используемый таймфрейм, чтобы решить, ищут ли они быструю реакцию или же им нужна стабильность и большая уверенность, прежде чем они примут изменения.
Аналогично для стохастика (5,3,3) обеспечивает более быструю настройку, но генерируемые сигналы неизбежно будут шумными. Более медленные настройки, например (14,5,5), могут показаться более подходящими, но опять же нужно учитывать таймфрейм. В пользовательском классе сигналов, который мы используем для этих тестов, мы применили стандартный период индикатора для скользящей средней и стохастического осциллятора. Эти параметры можно настроить, но для наглядности, если период нашей скользящей средней равен N, то стохастический осциллятор будет равен (N,3,3), а медленная или запаздывающая скользящая средняя будет равна 2 x N.
Всплески объема также можно использовать для дальнейшего подтверждения пересечений скользящих средних или силы прорыва. Уровни поддержки и сопротивления также могут использоваться для дальнейшего повышения надежности модели.
Наклон скользящей средней + подтверждение тренда стохастиком
Паттерн фокусируется на определении направления тренда с помощью наклона скользящей средней, одновременно подтверждая импульс с помощью стохастического осциллятора. Сигналом к покупке считается ситуация, когда скользящая средняя имеет восходящий наклон, а стохастический осциллятор превышает 50 и продолжает расти. Сигнал на продажу — обратный. При этом скользящая средняя будет иметь нисходящий наклон, а стохастический осциллятор будет находиться ниже 50 и продолжать падать. Вот как наша функция Get извлекает входной вектор, проверяющий наличие этих условий:
else if(Index == 2) { if(C.GetData(T, 2, _c) >= 2 && CopyBuffer(M.Handle(), 0, T, 2, _ma) >= 2 && CopyBuffer(S.Handle(), 0, T, 2, _sto_k) >= 2) { _v[0] = ((_ma[1] < _ma[0]) ? 1.0f : 0.0f); _v[1] = ((_sto_k[1] < _sto_k[0] && _sto_k[1] >= 50.0) ? 1.0f : 0.0f); _v[2] = ((_sto_k[1] > _sto_k[0] && _sto_k[1] <= 50.0) ? 1.0f : 0.0f); _v[3] = ((_ma[1] > _ma[0]) ? 1.0f : 0.0f); } }
Это первый из десяти паттернов, где мы используем второй стохастический буфер (%K). Он всегда отстает от основного буфера (%D), и его использование здесь помогает подтвердить наклон этого осциллятора. После экспорта векторов этих данных за 2023 год для EURJPY в Python мы обучим простую нейронную сеть, описанную выше, с помощью функции Train, также указанной выше. Тестирование или перекрестная проверка будут выполняться функцией, очень похожей на нашу функцию Train, которую мы обозначили как Test. Вот его код на Python:
# Test function def Test(model, test_loader, optimizer, loss_fn, epochs): device = T.device('cuda:0' if T.cuda.is_available() else 'cpu') model.to(device) with T.no_grad(): test_loss = 0.0 for batch_idx, (data, target) in enumerate(tqdm(test_loader, desc=f"Loss at {test_loss} in (Test)")): # Step 1: Ensure proper tensor dimensions data = data.view(-1, 1, feature_size) ... # Step 3: Move to device and forward pass data, target = data.to(device), target.to(device) optimizer.zero_grad() output = model(data) target = target.view(-1, 1, 1) #print(f"target: {target.shape}, plus output: {output.shape}") loss = loss_fn(output, target) test_loss += loss.item() test_loss /= len(test_loader) print(f"Test Loss: {test_loss:.4f}")
Сравнительные показатели на обучающей и тестовой выборках соответствуют данным, приведенным выше для шаблонов 0 и 1, поэтому здесь публиковать их не будем. Форвард-тестирование в тестере стратегий MetaTrader за 2024 год дает нам следующий отчет:

Наш паттерн позволяет проводить ограниченное тестирование, поскольку доступны только длинные сделки. Обучение на более крупных наборах данных должно решить эту проблему. Однако паттерн-2 — это хороший фильтр флетовых рынков. Наклон скользящей средней гарантирует, что сделки совершаются только при наличии четко выраженного тренда. Сила тренда подтверждается с помощью стохастического осциллятора, поскольку относительное положение по отношению к пороговому значению 50 подтверждает импульс в преобладающем тренде. В целом, также наблюдается сокращение задержки, поскольку нет необходимости ждать пересечения скользящих средних, как это было в случае с описанными выше паттернами. Это означает, что данный паттерн должен хорошо работать при ускорении тренда.
К основным недостаткам относится медленная реакция на развороты тренда, поскольку наклон скользящей средней может изменяться или сдвигаться не сразу после разворота тренда. Кроме того, в подобных ситуациях стохастический осциллятор может давать сигналы с задержкой. Если тренд уже сформировался, ожидание подтверждения от стохастического осциллятора может привести к упущенным торговым возможностям. В целом, этот паттерн также не подходит для флета, учитывая его сильную зависимость от трендов.
Сочетание с дополнительными фильтрами, такими как ADX (средний индекс направленности) может помочь подтвердить наличие устойчивых трендов. Кроме того, точность должна повыситься при проверке на пробои или коррекции ценового движения. В качестве примеров практического применения можно привести валютный рынок (именно на нем мы сейчас и тестируем), акции, поскольку этот паттерн помогает выявлять продолжения тренда после небольших коррекций или криптовалюты и товары, так как он полезен для отслеживания движений, вызванных импульсом, в таких активах, как золото или биткоин.
Отскок от скользящей средней со стохастической дивергенцией
Паттерн работает за счет сочетания динамической поддержки/сопротивления отскоков от скользящих средних с дивергенцией импульса от стохастического осциллятора, что позволяет выявлять потенциальные развороты еще до того, как они произойдут. Сигналом к покупке является отскок цены от восходящей скользящей средней (которая в данном случае выступает в качестве уровня поддержки), при этом стохастический осциллятор показывает бычью дивергенцию, так что цена формирует более низкий минимум, а осциллятор — более высокий.
Сигнал на продажу возникает, когда цена отталкивается от нисходящей скользящей средней, выступающей в качестве сопротивления, а стохастический осциллятор указывает на медвежью дивергенцию, где цена формирует более высокий максимум, а стохастический осциллятор — более низкий. Реализация на языке MQL5 и определение входных данных для сети, обрабатывающей этот паттерн, осуществляется следующим образом:
else if(Index == 3) { _v.Init(6); _v.Fill(0.0); if(C.GetData(T, 3, _c) >= 3 && CopyBuffer(M.Handle(), 0, T, 3, _ma) >= 3 && CopyBuffer(S.Handle(), 0, T, 3, _sto_k) >= 3) { _v[0] = ((_c[2] > _c[1] && _c[1] < _c[0] && _c[0] < _c[2] && _ma[1] >= _c[1]) ? 1.0f : 0.0f); _v[1] = ((_sto_k[2] > _sto_k[1] && _sto_k[1] < _sto_k[0] && _sto_k[1] >= 40.0) ? 1.0f : 0.0f); _v[2] = ((_ma[2] > _ma[0]) ? 1.0f : 0.0f); _v[3] = ((_ma[2] < _ma[0]) ? 1.0f : 0.0f); _v[4] = ((_sto_k[2] < _sto_k[1] && _sto_k[1] > _sto_k[0] && _sto_k[1] <= 60.0) ? 1.0f : 0.0f); _v[5] = ((_c[2] < _c[1] && _c[1] > _c[0] && _c[0] > _c[2] && _ma[1] <= _c[1]) ? 1.0f : 0.0f); } }
Показатели функции потерь при валидации на обучающей и тестовой выборках не сильно отличаются от полученных в первых двух паттернах. Функция потерь на тестовом наборе данных меньше, чем функция потерь на обучающем наборе данных на первой эпохе, но она все еще меньше, чем функция потерь на 10-й эпохе. Результаты форвард-теста за 2024 год после обучения на 2023 году:

Положительный форвард-тест всегда является хорошим знаком, при условии, что как длинные, так и короткие позиции открываются сбалансированным образом. Здесь же такой смысл появляется. Однако паттерн 3 хорошо подходит для раннего обнаружения разворотов тренда, поскольку стохастическая дивергенция обычно сигнализирует о развороте тренда до того, как цена отреагирует. Это также позволяет избежать погони за трендом, благодаря чему трейдеры не входят в сделку на поздних этапах, а открываются вблизи динамических уровней поддержки/сопротивления. Этот метод также хорошо работает на трендовых рынках, поскольку отскок от скользящей средней подтверждает тренд, а дивергенция указывает на истощение.
Слабые стороны и ограничения — это ложные сигналы, которые поступают, когда на рынке наблюдается сильный тренд. Это объясняется тем, что в подобных ситуациях одной лишь дивергенции часто недостаточно для разворота тренда. Кроме того, запаздывающий эффект скользящей средней, возникающий после отскока от нее, не всегда идеально совпадает со временем дивергенции, а необходимость подтверждения для повышения точности также являются недостатком.
Паттерн 3 можно оптимизировать, сочетая его с паттернами свечей (такими как пин-бары или поглощающие бары) на уровне скользящих средних, чтобы усилить позицию. Для подтверждения расхождения можно также использовать всплески объема.
Скользящая средняя в роли динамической поддержки/сопротивления + пересечение стохастика
Стратегия паттерна-4 представляет собой сочетание следования за трендом (с использованием скользящей средней для определения направления, а также уровней поддержки/сопротивления) и подтверждения импульса (что достигается путем проверки пересечений стохастического осциллятора) для генерации полезных сигналов на покупку и продажу.
Сигналом к покупке считается момент, когда цена удерживается выше восходящей скользящей средней, а стохастический осциллятор %K пересекает осциллятор %D сверху вниз. Этот бычий паттерн помогает обеспечить выполнение ряда ключевых предпосылок для открытия длинной позиции. Во-первых, он служит подтверждением тренда, поскольку восходящая скользящая средняя гарантирует, что совершенные сделки соответствуют текущему направлению рынка.
Во-вторых, пересечение стохастического осциллятора обеспечивает точку входа, выбранную в момент импульса. Это объясняется тем, что изменение динамики часто подтверждает возобновление покупательского давления. Преждевременные входы в позицию после касания любой скользящей средней исключаются, поскольку пересечение стохастического осциллятора гарантирует наличие импульса перед покупкой. Таким образом, количество ложных сигналов уменьшается. Потенциальным недостатком является то, что он может пропустить ранние прорывы, поскольку ожидает выполнения обоих условий, что приводит к небольшой задержке в определении точек входа.
Сигнал на продажу возникает, когда цена остается ниже нисходящей скользящей средней, а стохастический осциллятор %K пересекает %D снизу вверх. Аналогично бычьему варианту, этот сигнал подтверждает тренд, вход или выход основаны на импульсе, отсутствуют преждевременные точки входа, а слабые сигналы отфильтровываются. Потенциальный недостаток заключается в том, что на нестабильных рынках цена может пробить скользящую среднюю, прежде чем возобновить нисходящее движение. Это особенно важно, поскольку для большинства торговых символов их падение, как правило, более волатильно, чем рост. В MQL5 мы реализуем это следующим образом:
else if(Index == 4) { if(C.GetData(T, 2, _c) >= 2 && CopyBuffer(M.Handle(), 0, T, 2, _ma) >= 2 && CopyBuffer(S.Handle(), 0, T, 2, _sto_k) >= 2 && CopyBuffer(S.Handle(), 1, T, 2, _sto_d) >= 2) { _v[0] = ((_sto_k[1] < _sto_d[1] && _sto_k[0] > _sto_d[0]) ? 1.0f : 0.0f); _v[1] = ((_ma[1] < _c[1] && _ma[0] < _c[0] && _ma[1] < _ma[0]) ? 1.0f : 0.0f); _v[2] = ((_ma[1] > _c[1] && _ma[0] > _c[0] && _ma[1] > _ma[0]) ? 1.0f : 0.0f); _v[3] = ((_sto_k[1] > _sto_d[1] && _sto_k[0] < _sto_d[0]) ? 1.0f : 0.0f); } }
Мы объединяем входной вектор, поступающий в сеть для обработки этого паттерна, в четырехмерную структуру. Как показано на приведенных выше примерах, этот вектор можно сделать более наглядным, выделив каждый из аргументов условия в качестве части входных данных. Форвард-тест сети, обученной только на паттерне 4, дает нам следующие результаты:

Паттерн должен хорошо работать на рынках, следующих за трендом, однако он неизбежно будет испытывать трудности в условиях бокового движения. Несмотря на благоприятные перспективы, тестирование на более крупных наборах данных важно, чтобы открывать как длинные, так и короткие позиции, а не только длинные, как указывалось выше.
Стохастический разворот перекупленности/перепроданности вблизи скользящей средней
Паттерн 5 объединяет следование за трендом со скользящей средней в качестве динамической поддержки/сопротивления и разворот импульса со стохастическим индикатором. Сигнал на покупку возникает, когда стохастический осциллятор пересекает отметку 20, при этом цена получает поддержку от скользящей средней. Этот паттерн обеспечивает динамическое подтверждение уровня поддержки, поскольку скользящая средняя выступает в качестве зоны поддержки, гарантируя, что цена отскакивает в рамках существующего восходящего тренда. Пересечение стохастического осциллятора выше 20 также сигнализирует об ослаблении медвежьего импульса и появлении покупателей на рынке. Использование двух индикаторов не только позволяет избежать преждевременных входов в сделку, но и представляет собой высоковероятностную стратегию. Потенциальный недостаток заключается в том, что при сильных нисходящих трендах цена может временно удержаться на уровне скользящей средней, прежде чем пробить нижнюю границу.
Сигнал на продажу возникает, когда стохастический осциллятор пересекает уровень 80 снизу вверх, в то время как цена находится вблизи сопротивления нисходящей скользящей средней. Это также служит подтверждением динамического уровня сопротивления, поскольку скользящая средняя выступает в роли зоны сопротивления, которая подтверждает, что цена не смогла подняться выше. Подтверждение разворота тренда, указывающего на перекупленность, сигнализирует об ослаблении бычьего импульса и о начале прихода продавцов. Использование двух индикаторов, как и прежде, позволяет избежать преждевременных входов/выходов из сделок, а также отфильтровывает слабые коррекции. Потенциальным недостатком медвежьего сигнала паттерна 5 является то, что в сильных нисходящих трендах цена может консолидироваться выше скользящей средней перед пробитием вниз, что может привести к поздним выходам из позиции или упущенной прибыли.
В MQL5 паттерн реализуется следующим образом:
else if(Index == 5) { if(C.GetData(T, 3, _c) >= 3 && CopyBuffer(M.Handle(), 0, T, 3, _ma) >= 3 && CopyBuffer(S.Handle(), 0, T, 2, _sto_k) >= 2) { _v[0] = ((_sto_k[1] < 20.0 && _sto_k[0] > 20.0) ? 1.0f : 0.0f); _v[1] = ((_c[2] > _c[1] && _c[1] < _c[0] && _c[1] >= _ma[1]) ? 1.0f : 0.0f); _v[2] = ((_c[2] < _c[1] && _c[1] > _c[0] && _c[1] <= _ma[1]) ? 1.0f : 0.0f); _v[3] = ((_sto_k[1] > 80.0 && _sto_k[0] < 80.0) ? 1.0f : 0.0f); } }
В данной статье мы реализовали паттерны MQL5 для установки или определения входного вектора для нейронной сети. Мы определяем эти входные векторы как просто набор нулей и единиц, где единица означает выполнение определенного условия покупки или продажи. Индексы для длинных и коротких позиций разделены, что технически означает невозможность наличия входного вектора, заполненного единицами, поскольку длинные и короткие позиции всегда зеркально отражают друг друга.
Кроме того, условие по каждому индексу может быть дополнительно детализировано или расширено за счет того, что каждый из аргументов условия будет занимать свой собственный индекс. Это привело бы к значительному увеличению длины входных векторов, однако мы этот вариант не рассматривали. Поскольку мы проводим тестирование и обучение за 2023 год, наш дальнейший план действий на 2024 год выглядит следующим образом. Результаты следующие:

Мы провести форвард-тестирование паттерна-5 с одной оговоркой. Открываются только длинные сделки! В основном это связано с обучением на ограниченных/небольших наборах данных, из-за чего выходные данные сети искажаются в соответствии с этой небольшой выборкой. Обучение с использованием больших наборов данных должно исправить эту ситуацию.
Золотой крест/крест смерти + стохастическое подтверждение
В паттерне 6 используется золотой крест/крест смерти, где скользящая средняя с более коротким периодом пересекает запаздывающую скользящую среднюю, а импульс также подтверждается пересечением стохастиком уровня 50. Сигналом к покупке является золотой крест — пересечение скользящей средней с более коротким периодом снизу вверх скользящей средней с более длинным периодом, при этом стохастический осциллятор находится выше 50 и продолжает расти. Сигнал на продажу — это крест смерти, когда скользящая средняя с коротким периодом пересекает скользящую среднюю с более длинным периодом сверху вниз, а стохастический осциллятор находится ниже 50 и падает. Мы задали входные параметры для сети с паттерном 6 следующим образом:
else if(Index == 6) { if(CopyBuffer(M.Handle(), 0, T, 2, _ma_lag) >= 2 && CopyBuffer(M.Handle(), 0, T, 2, _ma) >= 2 && CopyBuffer(S.Handle(), 0, T, 2, _sto_k) >= 2 && CopyBuffer(S.Handle(), 0, T, 2, _sto_d) >= 2) { _v[0] = ((_ma_lag[1] > _ma[1] && _ma_lag[0] < _ma[0]) ? 1.0f : 0.0f); _v[1] = ((50.0 <= _sto_d[0] && _sto_k[0] > _sto_d[0]) ? 1.0f : 0.0f); _v[2] = ((50.0 >= _sto_d[0] && _sto_k[0] < _sto_d[0]) ? 1.0f : 0.0f); _v[3] = ((_ma_lag[1] > _ma[1] && _ma_lag[0] < _ma[0]) ? 1.0f : 0.0f); } }
В используемом нами исходном коде идентификатор '_ma' обозначает скользящую среднюю за более короткий период, для которого в процессе обучения мы присвоили период 8. Для усреднения за более длительный период используется идентификатор '_ma_lag', а его период вдвое больше, чем у более короткого, и составляет 16. Форвард-тестирование за 2024 год на основе данных за 2023 год дает следующие результаты:

Модель паттерна-6 потерпела неудачу не только в плане прибыльности, но и в плане баланса между длинными и короткими позициями. Тем не менее, метод можно усовершенствовать, проведя обширное обучение и тестирование.
Стохастический экстремальный разворот + подтверждение тренда скользящей средней
В паттерне 7 для определения сигналов на вход используются стохастические изгибы на экстремальных уровнях в сочетании с относительным положением цены относительно скользящей средней. Сигнал на покупку возникает, когда стохастический осциллятор находится ниже 10 и начинает разворачиваться вверх, в то время как цена находится выше восходящей скользящей средней. Сигнал на продажу — снижение на уровне выше 90, когда цена находится ниже нисходящей скользящей средней. Реализация в MQL5-коде:
else if(Index == 7) { if(C.GetData(T, 2, _c) >= 2 && CopyBuffer(M.Handle(), 0, T, 2, _ma) >= 2 && CopyBuffer(S.Handle(), 0, T, 3, _sto_k) >= 3) { _v[0] = ((_ma[0] > _ma[1] && _c[0] > _ma[0]) ? 1.0f : 0.0f); _v[1] = ((_sto_k[0] > _sto_k[1] && _sto_k[1] < _sto_k[2] && _sto_k[2] <= 10.0) ? 1.0f : 0.0f); _v[2] = ((_sto_k[0] < _sto_k[1] && _sto_k[1] > _sto_k[2] && _sto_k[2] >= 90.0) ? 1.0f : 0.0f); _v[3] = ((_ma[0] < _ma[1] && _c[0] < _ma[0]) ? 1.0f : 0.0f); } }
Здесь мы используем исключительно %K-маркер для стохастического осциллятора, и всё, что мы делаем, это проверяем наличие n-образного разворота при бычьих условиях или U-образного разворота при медвежьих условиях на экстремальных уровнях. Результаты форвард-теста:

Как видно из приведенных выше результатов, особого прогресса на основе результатов тестирования за предыдущий год нет. Кроме того, совершаются только короткие продажи. Размещение односторонних сделок в форвард-тестировании - вряд ли хорошая идея. Поскольку на выходе мы получаем скалярное значение с плавающей запятой в диапазоне 0,0–1,0, это означает, что все прогнозы были ниже 0,5. Для исправления этой ситуации важно провести тестирование/обучение на больших наборах данных.
Прорыв стохастика с подтверждением скользящей средней
Наш предпоследний паттерн, паттерн-8, сочетает в себе 50-процентный стохастический порог с пересечениями цены и скользящей средней. Сигнал на покупку возникает, когда наблюдается усиление импульса, о чем свидетельствует пересечение стохастическим осциллятором порога 50 снизу вверх и закрытие выше него, при этом цена одновременно пересекает скользящую среднюю аналогичным образом и также закрывается выше нее. Сигнал на продажу выглядит зеркально: осциллятор пересекает отметку 50, а цена пробивает уровень поддержки скользящей средней. Если применять эту модель аналогично тому, что мы делали в предыдущих статьях, она, скорее всего, не принесет много сделок. Однако сейчас мы отдельно проверяем условия скользящей средней и стохастика как для бычьих, так и для медвежьих сигналов. Реализация в MQL-коде:
else if(Index == 8) { if(C.GetData(T, 2, _c) >= 2 && CopyBuffer(M.Handle(), 0, T, 2, _ma) >= 2 && CopyBuffer(S.Handle(), 0, T, 2, _sto_k) >= 2 && CopyBuffer(S.Handle(), 1, T, 2, _sto_d) >= 2) { _v[0] = ((_c[1] < _ma[1] && _c[0] > _ma[0]) ? 1.0f : 0.0f); _v[1] = ((_sto_k[1] < 50.0 && _sto_k[0] > 50.0) ? 1.0f : 0.0f); _v[2] = ((_sto_k[1] > 50.0 && _sto_k[0] < 50.0) ? 1.0f : 0.0f); _v[3] = ((_c[1] > _ma[1] && _c[0] < _ma[0]) ? 1.0f : 0.0f); } }
Теперь наш входной вектор для сети паттерна-8 с большей вероятностью будет иметь или регистрировать хотя бы одно из бычьих или медвежьих условий, что помогает не только в обучении, но и в адаптации при развертывании сети. Это объясняется тем, что помимо обработки обоих показателей, система будет выдавать прогнозы, если только один из них зарегистрирует целевые паттерны. Форвард-тестирование дает следующие результаты:

Результаты обнадеживающие. Здесь стоит упомянуть несколько важных оговорок. Во-первых, эти тестовые запуски проводятся с целевыми ценами (тейк-профитами) и без стоп-лоссов. Действительно, стоп-лосс никогда не гарантирует исполнение по цене выхода, но необходима определенная стратегия на случай неудачных сделок. Во-вторых, мы проводили обучение в предыдущем году, а тестирование — в следующем, что представляет собой относительно короткий период тестирования. Предпочтительны более длительные тестовые периоды с качественными данными от брокера.
Сжатие скользящей средней с прорывом стохастика
Наш заключительный паттерн-9 использует сжатие скользящих средних для определения условий низкой волатильности в сочетании со стохастическим импульсом для определения момента прорыва из этих условий. Сигнал на покупку возникает, когда две скользящие средние (короткая и длинная) сближаются на длительный период, после чего, согласно данным стохастического осциллятора, происходит резкое прохождение уровня 50. Медвежий сигнал регистрируется при тех же условиях, при этом первое изменение заключается в том, что при бычьем сжатии более короткая скользящая средняя находится выше более длинной скользящей средней, тогда как для сигнала на продажу ситуация обратная, и стохастический осциллятор для продажи резко падает ниже уровня 50 (вместо роста).
Мы получаем входной вектор сети из MQL5 следующим образом:
else if(Index == 9) { if(CopyBuffer(M.Handle(), 0, T, 3, _ma) >= 3 && CopyBuffer(M_LAG.Handle(), 0, T, 3, _ma_lag) >= 3 && CopyBuffer(S.Handle(), 0, T, 2, _sto_k) >= 2) { _v[0] = ((_ma_lag[0] < _ma[0] && fabs(fabs(_ma_lag[2] - _ma[2]) - fabs(_ma_lag[0] - _ma[0])) <= fabs(_ma[2] - _ma[0])) ? 1.0f : 0.0f); _v[1] = ((_sto_k[1] <= 45.0 && _sto_k[0] >= 55.0) ? 1.0f : 0.0f); _v[2] = ((_sto_k[1] >= 55.0 && _sto_k[0] <= 45.0) ? 1.0f : 0.0f); _v[3] = ((_ma_lag[0] > _ma[0] && fabs(fabs(_ma_lag[2] - _ma[2]) - fabs(_ma_lag[0] - _ma[0])) <= fabs(_ma[2] - _ma[0])) ? 1.0f : 0.0f); } }
Результаты форвард-теста следующие:

Паттерн-9, похоже, тоже нежизнеспособен, несмотря на возможность открытия как длинных, так и коротких позиций. При равном весе этот паттерн не стоит дальнейшего изучения, поскольку выше мы видели некоторые паттерны с более внушительными результатами форвард-тестов, но это решение остается за трейдером и зависит от того, сколько дополнительных тестов он готов провести.
Заключение
Мы не занимались комбинированием паттернов или подбором разных паттернов для создания единой системы. Как мы уже отмечали в предыдущих статьях, в частности, в предыдущей, это может быть небезопасно. Использование нескольких паттернов одновременно требует от трейдера знания этих паттернов, поскольку они могут взаимно нейтрализовать сделки друг друга. Если в отслеживании используются "магические числа", это может помочь, но проблемы с ограничениями по марже остаются. В следующей статье мы будем развивать наши наработки используя обучение с подкреплением.
| Файл | Описание |
|---|---|
| 57_0.onnx | сеть паттерна-0 |
| 57_1.onnx | паттерн-1 |
| 57_2.onnx | паттерн-2 |
| 57_3.onnx | паттерн-3 |
| 57_4.onnx | паттерн-4 |
| 57_5.onnx | паттерн-5 |
| 57_6.onnx | паттерн-6 |
| 57_7.onnx | паттерн-7 |
| 57_8.onnx | паттерн-8 |
| 57_9.onnx | паттерн-9 |
| SignalWZ_57.mqh | Файл класса сигналов |
| 57_X.mqh | Файл сигналов для обработки входных данных сети |
| wz_57.mq5 | Файлы, используемые при сборке советника |
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/17479
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Особенности написания Пользовательских Индикаторов
От новичка до эксперта: Развиваем географическую осознанность рынка с помощью визуализации на MQL5
Нейросети в трейдинге: Пространственно-управляемая агрегация рыночных событий (Окончание)
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Здравствуйте, в одном приложении отсутствует файл SignalWZ_57.mqh
Да, я тоже столкнулся с той же проблемой отсутствия файла SignalWZ_57.mqh.