English Deutsch 日本語
preview
Прогнозирование на основе глубокого обучения и открытие ордеров с помощью пакета MetaTrader 5 python и файла модели ONNX

Прогнозирование на основе глубокого обучения и открытие ордеров с помощью пакета MetaTrader 5 python и файла модели ONNX

MetaTrader 5Торговые системы | 16 мая 2024, 11:32
403 4
Javier Santiago Gaston De Iriarte Cabrera
Javier Santiago Gaston De Iriarte Cabrera

Введение

Глубокое обучение — это область машинного обучения, в которой основное внимание уделяется искусственным нейронным сетям, основанным на структуре и функциях человеческого мозга. Он включает в себя обучение моделей для выполнения задач без явного программирования, а путем изучения шаблонов и представлений на основе данных. Глубокое обучение привлекло значительное внимание благодаря своей способности автоматически изучать иерархические функции и представления, что делает его эффективным в различных областях, таких как распознавание изображений и речи, обработка естественного языка и многое другое.

Ключевые понятия глубокого обучения:

  • Нейронные сети - базовые единицы глубокого обучения, состоящие из взаимосвязанных узлов или нейронов, организованных в слои.
  • Глубокие нейронные сети (Deep Neural Networks, DNNs) - нейронные сети с несколькими слоями, позволяющие им изучать сложные закономерности.
  • Обучение - корректировка параметров модели с использованием размеченных данных для минимизации разницы между прогнозируемыми и фактическими результатами.
  • Функции активации - функции, которые применяются к выходным сигналам нейронов для введения нелинейности, что позволяет модели изучать более сложные взаимосвязи.
  • Обратное распространение ошибки (backpropagation) - алгоритм оптимизации, используемый для обновления весов модели на основе ошибки в ее прогнозах.

Python - универсальный язык программирования высокого уровня, известный своей читабельностью и простотой использования. Он имеет обширную экосистему библиотек и фреймворков, которые делают его подходящим для различных приложений, включая веб-разработку, обработку данных, искусственный интеллект и многое другое.

Ключевые особенности Python:

  • Читабельность - синтаксис Python удобен для чтения и понятен, что позволяет разработчикам легко выражать концепции в меньшем количестве строк кода.
  • Обширные библиотеки - Python имеет богатый набор библиотек и платформ для различных приложений, таких как NumPy и pandas для работы с данными, TensorFlow и PyTorch для глубокого обучения, Django и Flask для веб-разработки и многие другие.
  • Поддержка сообщества - Python располагает большим и активным сообществом разработчиков, что способствует его постоянному совершенствованию и доступности многочисленных ресурсов для обучения.

Использование Python для глубокого обучения - Python является популярным языком для глубокого обучения благодаря своим обширным библиотекам и платформам. Две основные библиотеки для глубокого обучения в Python — это TensorFlow и PyTorch. Эти библиотеки предоставляют абстракции высокого уровня для построения и обучения нейронных сетей, что делает их доступными как для новичков, так и для опытных пользователей.

Подводя итог, можно сказать, что глубокое обучение — это функциональный подход в более широкой области машинного обучения, а Python служит отличным языком программирования для реализации моделей и решений глубокого обучения. Сочетание Python и глубокого обучения привело к прогрессу в различных областях, сделав сложные задачи более доступными и автоматизируемыми.


Работа с ордерами с помощью Python и MQL5: Если вы читаете эту статью, вы наверняка заинтересованы в объединении Python с пакетом MQL5 для обработки торговых ордеров. MQL5 — специализированный язык, предназначенный для алгоритмической торговли на платформе MetaTrader 5 (MT5). Вот общая схема того, как вы можете подойти к интеграции Python с MQL5 для торговли:

  • Скрипт Python для глубокого обучения - разработайте сценарий Python, используя библиотеку глубокого обучения (например, TensorFlow, PyTorch) для создания и обучения модели для вашей конкретной торговой стратегии. Этот скрипт будет выполнять анализ и принимать решения на основе вашей модели глубокого обучения.

  • Связь между Python и MQL5 - установите связь между вашим скриптом Python и платформой MetaTrader 5. Это можно сделать с помощью различных методов, таких как сокеты, REST API или другие протоколы связи. Цель состоит в том, чтобы ваш скрипт Python мог отправлять торговые сигналы или ордера в терминал MetaTrader.

  • Исполнение ордеров в MQL5 - реализуйте логику получения сигналов от скрипта Python и выполнения соответствующих ордеров на покупку/продажу на платформе MetaTrader 5 в своем скрипте или советнике MQL5. Это предполагает использование торговых функций, предоставляемых языком MQL5.


ONNX

или Open Neural Network Exchange — это формат с открытым исходным кодом, предназначенный для облегчения взаимодействия моделей искусственного интеллекта (ИИ) в различных средах. ONNX, разработанный и поддерживаемый Microsoft и Facebook, позволяет пользователям обучать модели с использованием одной платформы и развертывать их с помощью другой.

Ключевым преимуществом ONNX является его способность служить промежуточным форматом между различными платформами глубокого обучения, такими как TensorFlow, PyTorch и другими. Эта взаимозаменяемость упрощает процесс интеграции моделей машинного обучения в различные приложения, среды и платформы.

В контексте MQL5 (MetaQuotes Language 5) интеграция моделей ONNX предполагает преобразование обученной модели машинного обучения в формат ONNX. После перехода в формат ONNX модель можно загрузить в скрипты MQL5 или советники, что позволяет использовать расширенные возможности машинного обучения для алгоритмических торговых стратегий на платформе MetaTrader 5.


Краткое введение

Мы углубимся в увлекательный мир глубокого обучения, применяемого в автоматизированной торговле. Основное внимание будет уделено программе, предназначенной для автоматической торговли и использующей передовые методы глубокого обучения. Мы изучим тонкости тестирования производительности модели с использованием таких ключевых показателей, как средняя абсолютная ошибка (MAE), средняя квадратичная ошибка (MSE) и R-квадрат (R2). Это исследование направлено на то, чтобы дать представление об эффективности модели глубокого обучения в контексте алгоритмической торговли, а также оценить точность ее прогнозирования и общую надежность. Мы раскроем взаимосвязь глубокого обучения, автоматической торговли и комплексного тестирования производительности.


Кроме того, мы рассмотрим процесс преобразования сценария Python (.py) в исполняемый файл для получения комплексного и удобного пакета. Это пошаговое руководство предоставит подробную информацию для полного понимания всего процесса. К концу статьи вы не только получите глубокое понимание реализации глубокого обучения в автоматизированной торговле, но и практические знания о том, как обернуть свое решение в удобный исполняемый формат. Следите за подробным пошаговым руководством, которое поможет плавно перейти от написания кода к полнофункциональной исполняемой программе, что сделает ваш опыт одновременно образовательным и практически полезным.


В качестве заключительного шага в статье я создам скрипт для генерации вывода в формате ONNX. Скрипт будет способствовать включению модели ONNX в MetaTrader 5 (MT5) с использованием советника. Это повысит адаптивность и интеграцию возможностей машинного обучения в платформу MetaTrader 5, облегчая разработку более сложных и эффективных алгоритмических торговых стратегий.


Прежде чем приступить к написанию кода

Наша цель — это разработка бота Python, который проходит двухэтапный процесс: сначала он занимается глубоким анализом доступных данных, а затем выполняет торговые ордера. Учитывая, что MetaTrader 5 облегчает загрузку большого количества тиковых данных, наша стратегия предполагает получение этих данных и преобразование их в информацию о ценах. Методология конвертации будет включать в себя взятие среднего значения между значениями тиков, что позволит нам создать более управляемое представление рыночных цен для дальнейшего анализа и принятия решений в нашем боте.


Но прежде чем мы продолжим, нам нужно использовать Python. Для удобной настройки Python рекомендуется загрузить и установить Anaconda. Кроме того, установка Visual Studio Code предоставит удобную среду для написания скриптов.


Anaconda

VSC

После установки Anaconda и Visual Studio Code следующим шагом будет установка некоторых пакетов с помощью pip в Visual Studio Code.

Вы можете сделать это, открыв встроенный терминал в Visual Studio Code и используя следующие команды (если вы используете Conda/Anaconda, запустите эти команды в Anaconda Prompt):

pip install [package1] [package2] ...

Замените [package1], [package2] и т. д. названиями конкретных пакетов Python, которые вам нужны для вашего проекта. Это гарантирует, что ваша среда оснащена необходимыми инструментами и библиотеками для продолжения разработки вашего бота Python.


Пакеты

Вам необходимо установить определенные пакеты для среды Python, включая MetaTrader 5, TensorFlow и другие библиотеки, поставляемые с Anaconda. Вот общее руководство по установке этих библиотек:

  1. MetaTrader 5 (MT5) - торговая платформа. К сожалению, на момент моего последнего обновления знаний в январе 2022 года MetaTrader 5 не имеет прямого Python API, доступного в официальном дистрибутиве. Однако могут существовать некоторые сторонние библиотеки или оболочки. Пожалуйста, обратитесь к официальной документации MetaTrader 5 или соответствующим форумам сообщества для получения последней информации.   

    pip install MetaTrader5
  2. TensorFlow - популярная библиотека машинного обучения. Вы можете установить ее, используя следующую команду pip в терминале или командной строке:

    pip install tensorflow


Начинаем писать код

После успешной установки необходимых библиотек, следующим шагом будет написание кода Python. Для этого вам необходимо импортировать библиотеки, которые вы установили. В Python для добавления в код функционала библиотеки используется оператор import.
import MetaTrader5 as mt5
from MetaTrader5 import *
import numpy as np
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.regularizers import l2
from sklearn.model_selection import KFold


from MetaTrader5 import *

Эта строка импортирует все функции и классы из библиотеки MetaTrader 5. Использование * (подстановочный знак) означает, что вы импортируете всё, хотя это обычно не рекомендуется из-за потенциальных конфликтов пространства имен.

import numpy as np

Эта строка импортирует библиотеку NumPy и присваивает ей псевдоним np. NumPy широко используется для числовых операций в Python.

from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score

Эти строки импортируют определенные функции/классы из библиотеки scikit-learn. Они включают в себя утилиты для предварительной обработки данных, оценки и выбора модели.

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.regularizers import l2

Эти строки импортируют компоненты из библиотеки TensorFlow, которая обычно используется для глубокого обучения. Они включают в себя последовательную модель (Sequential model), плотные слои (Dense layers) и регуляризацию L2.

from sklearn.model_selection import KFold

Эта строка импортирует класс KFold из scikit-learn, который часто используется для перекрестной проверки в машинном обучении.

Таким образом, ваш скрипт готовится использовать библиотеку MetaTrader 5 для финансовых данных, NumPy для числовых операций, scikit-learn для утилит машинного обучения и TensorFlow для глубокого обучения. Необходимо настроить среду для анализа финансовых данных и, возможно, построить модель машинного обучения для финансовых прогнозов. Убедитесь, что эти библиотеки установлены в вашей среде Python с помощью соответствующего менеджера пакетов (например, pip или conda).


Загрузка данных

Чтобы скачать тики, сначала нам нужно подключиться к провайдеру данных (MT5), а уже оттуда мы сможем скачать то, что нам нужно. Для этой цели мы будем использовать следующий код:
# You will need to update the values for path, login, pass, and server according to your specific case.
creds = {
    "path": "C:/Program Files/XXX MT5/terminal64.exe",
    "login": account,
    "pass": clave_secreta,
    "server": server_account,
    "timeout": 60000,
    "portable": False
}
# We launch the MT5 platform and connect to the server with our username and password.
if mt5.initialize(path=creds['path'],
                  login=creds['login'],
                  password=creds['pass'],
                  server=creds['server'],
                  timeout=creds['timeout'],
                  portable=creds['portable']):
    
    print("Plataform MT5 launched correctly")
else:
    print(f"There has been a problem with initialization: {mt5.last_error()}")

Как только это будет сделано, приступаем к загрузке интересующих нас данных по интересующему нас символу в виде тиков.

rates = rates.drop(index=rates.index)
rates = mt5.copy_ticks_from(symbol, utc_from, 1000000000, mt5.COPY_TICKS_ALL)

Код сначала очищает все существующие данные в 'rates' DataFrame, а затем извлекает данные о тиках для определенного символа и временного диапазона с помощью функции copy_ticks_from из библиотеки MetaTrader 5.

Поскольку мы стремимся хранить данные в DataFrame Pandas (который обычно используется вместе с NumPy) для наилучшей работы с данными в Python, мы перенесем данные в DataFrame Pandas.
rates_frame=pd.DataFrame()
# Empty DataFrame
rates_frame = rates_frame.drop(index=rates_frame.index)
rates_frame = pd.DataFrame(rates)

Этот код инициализирует пустой DataFrame Pandas с именем rates_frame, очищает все существующие в нем данные, а затем заполняет его данными из переменной rates.

Чтобы упростить задачу, давайте сложим и разделим значения bid и ask в данных на два. Таким образом, мы получаем среднее значение, которое будет нашим входом для глубокого обучения.
rates_frame['time']=pd.to_datetime(rates_frame['time'], unit='s')
rates_frame['close']=(rates_frame['ask']+rates_frame['bid'])/2

Этот код преобразует столбец time в формат даты и времени, используя секунды в качестве единицы измерения, и вычисляет среднее значение ask и bid, присваивая результат новому столбцу close в rates_frame DataFrame.

Выбор входных данных для глубокого обучения в финансовых приложениях, таких как прогнозирование движения цен, зависит от различных факторов, и универсального решения не существует. Однако следует учитывать некоторые общие рекомендации:


Необработанные данные vs производные функции (осцилляторы и индикаторы):

1. Необработанные данные:

  • Преимущества:
    • Сохраняют всю доступную информацию без дополнительных допущений.
    • Позволяют нейронной сети изучать паттерны непосредственно из необработанных входных данных.
  • Недостатки
    • Могут содержать шум или нерелевантную информацию.
    • Возможно, потребуется дополнительная предварительная обработка данных.

2. Производные функции (осцилляторы и индикаторы):

  • Преимущества:
    • Могут уловить конкретную динамику рынка.
    • Могут обеспечить более структурированное представление данных.
  • Недостатки:
    • Вводят допущения о том, какие функции релевантны.
    • Эффективность зависит от выбранных показателей и их параметров.


Практические рекомендации:

1. Нормализация данных:

  • Это позволяет уложить серию в некий диапазон, у которого среднее 0 и разброс 1, что улучшает сходимость при обучении.

2. Длина последовательности:

  • Для данных временных рядов длина последовательности имеет значение. Поэкспериментируйте с различной длиной, чтобы найти оптимальный компромисс между обнаружением соответствующих паттернов и эффективностью вычислений.

3. Временные аспекты:

  • Рассмотрите возможность включения временных аспектов, таких как значения с задержкой или скользящие окна, чтобы уловить временные зависимости.

4. Выбор функции:

  • Поэкспериментируйте с различными подмножествами функций, чтобы определить, какие из них наиболее информативны для вашей конкретной задачи.

5. Архитектура модели:

  • Настройте архитектуру нейронной сети в зависимости от характера входных данных. Рекуррентные нейронные сети (RNN) и сети долгой краткосрочной памяти (LSTM) эффективны для последовательных данных.

6. Регуляризация:

  • Используйте методы регуляризации (например, отсев), чтобы предотвратить переобучение, особенно при работе с многомерными данными.

7. Настройка гиперпараметра:

  • Поэкспериментируйте с различными настройками гиперпараметров, чтобы найти оптимальную конфигурацию для вашей модели.

На практике часто бывает полезно сравнить различные подходы эмпирически. Некоторые задачи могут выиграть от простоты необработанных данных, в то время как другие могут повысить производительность за счет хорошо продуманных производных функций. Крайне важно найти баланс между предоставлением достаточного количества информации для обучения модели и избеганием шума или ненужных деталей. Кроме того, регулярно переоценивайте выбранный подход, поскольку динамика рынка может со временем измениться.


Смещение данных

Теперь у нас есть данные в DataFrame Pandas, и мы можем отправить их на глубокое обучение для обработки в качестве входных данных. По глубокому обучению в MQL5 уже есть много интересных статей, поэтому я не буду углубляться в эту тему. Я перейду сразу к практическому аспекту Python. Но прежде чем отправлять данные в TensorFlow, нам нужно иметь входные данные для временного сдвига, который мы будем применять к данным для прогнозирования (сдвинутое время будет временем, которое мы стремимся спрогнозировать). Вот почему у нас есть следующий код.


Сдвиг DataFrame в контексте глубокого обучения, особенно при прогнозировании временных рядов, обычно выполняется для создания последовательностей входных и целевых переменных. Вот причины, по которым сдвиг используется в контексте глубокого обучения для прогнозирования временных рядов:

  1. Временные зависимости - модели глубокого обучения, такие как рекуррентные нейронные сети (RNN) или сети долгой краткосрочной памяти (LSTM), могут фиксировать временные зависимости в последовательных данных. Сдвиг DataFrame позволяет создавать последовательности, в которых каждая входная последовательность соответствует сегменту прошлых данных, а соответствующая целевая последовательность представляет будущие данные.

  2. Последовательное обучение - модели глубокого обучения эффективны при изучении закономерностей и зависимостей в последовательных данных. Смещая DataFrame, вы гарантируете, что входная и целевая последовательности совпадают во времени, позволяя модели учиться на историческом контексте и делать прогнозы на основе этого контекста.

  3. Обучение пар ввода-вывода - сдвиг помогает создавать обучающие примеры для модели глубокого обучения. Каждую строку в сдвинутом DataFrame можно рассматривать как пару ввода-вывода, где входными данными является последовательность прошлых наблюдений, а выходными данными — целевая переменная, которую необходимо спрогнозировать в будущем.


number_of_rows= seconds
empty_rows = pd.DataFrame(np.nan, index=range(number_of_rows), columns=df.columns)
df = df._append(empty_rows, ignore_index=True)
df['target'] = df['close'].shift(-seconds)
print("df modified",df)
  • number_of_rows - переменная, представляющая количество секунд.
  • empty_rows создает новый DataFrame со значениями NaN, имеющими те же столбцы, что и исходный DataFrame (df).
  • df.append(empty_rows, ignore_index=True) добавляет пустые строки к исходному DataFrame (df), игнорируя индекс, чтобы обеспечить непрерывный индекс.
  • df['target'] = df['close'].shift(-seconds) создает новый столбец target, содержащий значения close, сдвинутые на отрицательное значение на указанное количество секунд. Обычно это делается при подготовке данных временных рядов для прогнозного моделирования.

Теперь осталось только очистить данные, чтобы мы могли использовать их в качестве входных данных для TensorFlow.

# Drop NaN values
df=df.dropna()

Результатом является измененный DataFrame (df) с дополнительными строками, заполненными значениями NaN, и новым "целевым" столбцом для сдвинутых по времени значений close.


Глубокое обучение

Использование TensorFlow и Keras для создания и обучения нейронной сети для прогнозирования временных рядов.

# Split the data into features (X) and target variable (y)
X=[]
y=[]
X = df2[['close']]
y = df2['target']

# Split the data into training and testing sets
X_train=[]
X_test=[]
y_train=[]
y_test=[]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, shuffle=False)

# Standardize the features
X_train_scaled=[]
X_test_scaled=[]
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

# Build a neural network model
model=None
model = Sequential()
model.add(Dense(128, activation='relu', input_shape=(X_train.shape[1],), kernel_regularizer=l2(k_reg)))
model.add(Dense(256, activation='relu', kernel_regularizer=l2(k_reg)))
model.add(Dense(128, activation='relu', kernel_regularizer=l2(k_reg)))
model.add(Dense(64, activation='relu', kernel_regularizer=l2(k_reg)))
model.add(Dense(1, activation='linear'))

# Compile the model[]
model.compile(optimizer='adam', loss='mean_squared_error')

# Train the model
model.fit(X_train_scaled, y_train, epochs=int(epoch), batch_size=256, validation_split=0.2, verbose=1)

# Use the model to predict the next 4 instances
X_predict=[]
X_predict_scaled=[]

predictions = pd.DataFrame()
predictions=[]
# Empty DataFrame
#predictions = predictions.drop(index=predictions.index)
X_predict = df2.tail(segundos)[['close']]
X_predict_scaled = scaler.transform(X_predict)
predictions = model.predict(X_predict_scaled)

# Print actual and predicted values for the next 
    n  instances
print("Actual Value for the Last Instances:")
print(df.tail(1)['close'].values)

print("\nPredicted Value for the Next Instances:")
print(predictions[:, 0])
predictions=pd.DataFrame(predictions)

Подготовка и обучение нейронной сети с использованием исторических финансовых данных для прогнозирования будущих значений. Она использует библиотеки TensorFlow и Keras для построения и обучения модели. Затем прогнозы распечатываются для следующих экземпляров на основе обученной модели.

Пояснения:

  • Модель инициализируется как последовательная модель, то есть представляет собой линейный набор слоев.

  • Плотные слои представляют собой полностью связанные слои нейронной сети. Параметры включают количество нейронов (единиц), функцию активации, форму ввода (применимо только для первого слоя) и регуляризацию ядра (в данном случае регуляризацию L2 с силой, заданной k_reg).

  • activation='relu' подразумевает функцию активации блока линейной ректификации (Rectified Linear Unit, ReLU), обычно используемую в скрытых слоях.

  • Последний слой имеет один нейрон с линейной функцией активации, что указывает на выход регрессии. Если бы это была задача классификации, использовалась бы другая функция активации, например sigmoid или softmax.

Эта архитектура представляет собой нейронную сеть прямого распространения с несколькими скрытыми слоями для целей регрессии. Настройте параметры и слои в соответствии с вашей конкретной задачей и характеристиками набора данных.


Прогнозирование

Насколько хороша аппроксимация этой модели?

# Calculate and print mean squared error
mse = mean_squared_error(y_test, model.predict(X_test_scaled))
print(f"\nMean Squared Error: {mse}")

# Calculate and print mean absolute error
mae = mean_absolute_error(y_test, model.predict(X_test_scaled))
print(f"\nMean Absolute Error: {mae}")

# Calculate and print R2 Score
r2 = r2_score(y_test, model.predict(X_test_scaled))
print(f"\nR2 Score: {r2}")

Предоставленный код вычисляет и печатает три показателя оценки для обученной регрессионной модели.

Пояснения:

  • Средняя абсолютная ошибка (Mean Squared Error, MSE) - измеряет среднеквадратическую разницу между прогнозируемыми и фактическими значениями. Чем ниже MSE, тем лучше модель.

  • Средняя абсолютная ошибка (Mean Absolute Error, MAE) - измеряет среднюю абсолютную разницу между прогнозируемыми и фактическими значениями. Как и в случае с MSE, более низкие значения указывают на лучшую производительность модели.

  • R-квадрат (R2 Score) - также известен как коэффициент детерминации. Измеряет долю дисперсии зависимой переменной, которую можно предсказать по независимой переменной (или нескольким независимым переменным). Оценка R2, равная 1, указывает на идеальное соответствие, а оценка 0 предполагает, что модель не лучше, чем прогнозирование среднего значения целевой переменной. Отрицательные значения указывают на плохую производительность модели.

Эти метрики дают представление о том, насколько хорошо обученная модель работает на тестовом наборе.

В этом контексте вам нужно поэкспериментировать с объемом данных (задержкой ввода) и количеством эпох. Например, я использую данные за 900 дней и 1 эпоху для EURUSD за 2-часовой период времени (объяснения будут даны позже). Однако я также достигаю хороших результатов, используя значительно меньше данных и больше эпох.


Ордера

Теперь, когда у нас есть необходимый функционал, нам просто нужно измерить время, необходимое скрипту для возврата данных. Это позволяет нам отметить на графике начальную точку прогноза (поскольку прогноз начинается несколько раньше, например, в моем случае это занимает около 15-20 минут). Отправной точкой для открытия ордеров станет момент, когда это время пройдет. Кроме того, после активного использования этого бота я заметил, что наиболее точные прогнозы делаются в первой четверти всего времени работы. Для повышения точности я буду ограничивать и отмечать на графике ордера и выполнять их в течение этого конкретного периода. Это легко реализовать с помощью time.now(). Мы можем зафиксировать время в этот момент и преобразовать его в секунды, поскольку наш прогноз и шкала по оси X указаны в секундах. Учитывая все это, мы отметим, где начинается наш прогноз, и ордера будут срабатывать исходя из этой начальной точки.

Это типичный ордер Python, использующий MetaTrader 5 (MT5). Функция open_trade_sell2 представляет собой общую структуру для размещения ордера на продажу в платформе MetaTrader 5. Он использует метод mt5.order_send для отправки торгового запроса на исполнение ордера. Параметры включают в себя торговое действие (buy или sell), символ, размер лота, случайное целое число (возможно, для идентификации магического числа), тейк-профит (tp), стоп-лосс (sl) и отклонение.

def open_trade_sell2(action, symbol, lot,random_integer, tp, sl, deviation):
        '''https://www.mql5.com/en/docs/integration/python_metatrader5/mt5ordersend_py
        '''
        # prepare the buy request structure
        symbol_info = get_info(symbol)
        
        if action == 'buy':
            trade_type = mt5.ORDER_TYPE_BUY
            price = mt5.symbol_info_tick(symbol).ask
        elif action =='sell':
            trade_type = mt5.ORDER_TYPE_SELL
            price = mt5.symbol_info_tick(symbol).bid
        point = mt5.symbol_info(symbol).point
        print("el precio mt5 es:", price)

        buy_request = {
            "action": mt5.TRADE_ACTION_DEAL,
            "symbol": symbol,
            "volume": lot,
            "type": trade_type,
            "price": price,
            "sl":sl,
            "tp":tp,
            "deviation": deviation,
            "magic": random_integer,
            "comment": "python open",
            "type_time": mt5.ORDER_TIME_GTC, # good till cancelled
            "type_filling": mt5.ORDER_FILLING_IOC,
        }
        # send a trading request
        result = mt5.order_send(buy_request)        
        return result, buy_request

Функция является частью скрипта, который взаимодействует с платформой MetaTrader 5 для автоматизации торговых стратегий. Ключевые шаги включают определение типа сделки, получение текущей цены bid или ask, подготовку структуры торгового запроса и, наконец, отправку торгового запроса.


Закрытие ордеров

def close_position(position,symbol):
    """This function closes the position it receives as an argument."""
   
    request = {
        'action': mt5.TRADE_ACTION_DEAL,
        'position': position.ticket,
        'magic': position.magic,
        'symbol': symbol,
        'volume': position.volume,
        'deviation': 50,
        'type': mt5.ORDER_TYPE_BUY if position.type == 1 else mt5.ORDER_TYPE_SELL,
        'type_filling': mt5.ORDER_FILLING_FOK,
        'type_time': mt5.ORDER_TIME_GTC,
        'comment': "mi primera orden desde Python"
    }
    return mt5.order_send(request)

# Now, we define a new function that serves to close ALL open positions.
def close_all_positions(symbol):
    """This function closes ALL open positions and handles potential errors."""
   
    positions = mt5.positions_get()
    for position in positions:
        if close_position(position,symbol).retcode == mt5.TRADE_RETCODE_DONE:
            print(f"Position {position.ticket} closed correctly.")
        else:
            print(f"An error occurred while closing the position.{position.ticket}: {mt5.last_error()}")

Мы также можем закрывать ордера с помощью советника, который я модифицировал для этого случая. В этом случае файл .py создает документ и записывает время в минутах для закрытия ордеров (если вы его используете, не забудьте оставить файл в папке File в mt5).

#Specify the path for the file and open it in write mode
file_path = "C:/XXX/MetaQuotes/Terminal/XXX/MQL5/Files/python_"+symbol+"_file.txt"
try:
    with open(file_path, "w") as file:
        # Write file parameters
        file.write(str(the_value))
    print(f"File '{file_path}' created.")
except Exception as e:
    print(f"Error creating file: {e}")

Это модифицированный советник, который я получил у этого пользователя (вам нужно будет добавить правильные пути к папке File):

//+------------------------------------------------------------------+
//|                                               Timer_modified.mq5 |
//|                                                                  |
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2021, Vladimir Karputov"
#property link      "https://www.mql5.com/ru/market/product/43516"
#property version   "1.000"

//--- display the window of input parameters when launching the script
#property script_show_inputs

//--- parameters for data reading
input string InpFileName="python_file.txt"; // file name
input string InpDirectoryName="Files"; // directory name
//---
#include <Trade\PositionInfo.mqh>
#include <Trade\Trade.mqh>
//#include <File.mqh>
//---
CPositionInfo  m_position;                   // object of CPositionInfo class
CTrade         m_trade;                      // object of CTrade class
//--- input parameters
input uchar    InpAfterHour   = 0; // After: Hour ... (max 255)
input uchar    InpAfterMinutes= 42; // After: Minutes ... (max 255)
input uchar    InpAfterSeconds= 59; // After: Seconds ... (max 255)
//---
int file_handles;
uchar value_uchar;
long     m_after  = 0;
bool         started=false;     // flag of counter relevance
string str1,str2,str3,str4;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+

int OnInit()
  {
  str1="C:/XXX/MetaQuotes/Terminal/XXXX/MQL5/Files/python_";
  str3="_file.txt";
  str2=Symbol();
  str4=str1+str2+str3;

   // Open file to read
   string file_path = str4;
   file_handles = FileOpen(file_path, FILE_READ|FILE_TXT);
   
   if(file_handles != INVALID_HANDLE)
     {
      PrintFormat("File %s opened correctly", file_path);
      
      // Read uchar value of the file
      if (FileReadInteger(file_handles, INT_VALUE))
        {
         PrintFormat("Uchar value read from file: %u", value_uchar);
         m_after  = 0;
         m_after=value_uchar;
        }
      else
        {
         Print("Error when reading the uchar value from file");
        }

      // Close file after read
      FileClose(file_handles);
     }
   else
     {
      m_after  = 0;
      m_after=InpAfterHour*60*60+InpAfterMinutes*60+InpAfterSeconds;
      PrintFormat("Error when opening file %s, error code = %d", file_path, GetLastError());
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   for(int i=PositionsTotal()-1; i>=0; i--) // returns the number of current positions
      if(m_position.SelectByIndex(i)) // selects the position by index for further access to its properties
        {
         if(TimeCurrent()-m_position.Time()>=m_after)
            m_trade.PositionClose(m_position.Ticket()); // close a position
        }
  }
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| called when a Trade event arrives                                |
//+------------------------------------------------------------------+
void OnTrade()
  {
   if(started) SimpleTradeProcessor();   
  }  
 void SimpleTradeProcessor()
  {
  str1="C:/Users/XXX/MetaQuotes/Terminal/XXX/MQL5/Files/python_";
  str3="_file.txt";
  str2=Symbol();
  str4=str1+str2+str3;
     // Open file to read it
   string file_path = str4;
   file_handles = FileOpen(file_path, FILE_READ|FILE_TXT);
   
   if(file_handles != INVALID_HANDLE)
     {
      PrintFormat("File %s is opened correctly", file_path);
      
      // Reads the uchar value from the file
      if (FileReadInteger(file_handles, INT_VALUE))
        {
         PrintFormat("value_uchar read form file: %u", value_uchar);
         m_after  = 0;
         m_after=value_uchar;
        }
      else
        {
         Print("Error while reading the value uchar from file");
        }

      // Closes the file after reading
      FileClose(file_handles);
     }
   else
     {
      m_after  = 0;
      m_after=InpAfterHour*60*60+InpAfterMinutes*60+InpAfterSeconds;
      PrintFormat("Error when opening the fileo %s, Code error = %d", file_path, GetLastError());
     }    
  }

Я представил два разных способа закрытия ордеров. В Python единственное, что я делаю, это использую time.sleep(t), где t - это количество секунд между размещением ордера и моментом его закрытия. Однако этот подход не очень практичен, поскольку требует ожидания, отнимающего время, которое можно было бы использовать для других операций скрипта. Я оставляю за вами право решить, какой метод использовать для закрытия ордеров.

Мы можем генерировать больше ордеров, сократив использование time.sleep(), например, между размещением ордера на покупку или продажу и его закрытием. Как? Мы можем использовать модифицированный советник, включив в него значение магического числа, что позволит ему закрывать только ордер с указанным магическим числом и символом. Таким образом, вместо того, чтобы приостанавливать выполнение скрипта с помощью sleep, он может оставаться активным и продолжать работать.


Визуализация

Для эффективной визуализации мы построим график для наблюдения за трендами. Мы отметим диапазоны наиболее надежных прогнозов, укажем максимальные и минимальные значения в этих диапазонах и сохраним график.

plt.axvline(x=pt_division2, color='gray', linestyle='--', label='75 %')
plt.axvline(x=center, color='grey', linestyle='--', label='50 %')
plt.axvline(x=pt_division3, color='blue', linestyle='--', label='33 %')
plt.axvline(x=pt_division1, color='gray', linestyle='--', label='25 %')
plt.axvline(x=pt_division6, color='blue', linestyle='--', label='16 %')
plt.axvline(x=pt_division10, color='yellow', linestyle='--', label='10 %')
plt.axvline(x=pt_division14, color='blue', linestyle='--', label='7 %')
plt.axvline(x=pt_division20, color='yellow', linestyle='--', label='5 %')
plt.axvline(x=entry_point, color='orange', linestyle='--', label='entrada')
plt.axvline(x=value_25, color='orange', linestyle='--', label='salida 20%')
plt.axvline(x=maximal_index, color='red', linestyle='--', label='maximal') ##### ni idea de porqué no pinta correctamente la linea
plt.axvline(x=manimal_index, color='red', linestyle='--', label='minimal')# lo mismo aquí

plt.plot(dff5.iloc[:, 0], linestyle='-', label='Predicted')

plt.xlabel('Instances')
plt.ylabel('Prediction Price')
plt.legend()
plt.title(f'Predicted {symbol} y quedan en minutos: ' + str(MyParameter))
plt.savefig('Predicted_for_'+str(symbol)+'_quedan_'+str(MyParameter)+'_minutos_desde_'+str(formatted_now2)+'_.png')


Как работает скрипт?

Сначала он загружает библиотеки, затем отмечает точный момент, чтобы отслеживать текущий этап прогнозирования. После этого он инициализирует MT5, загружает данные, обрабатывает их и предоставляет результаты. Мы используем эти результаты для создания графика и исполнения ордеров в определенные моменты, включая конкретные сроки закрытия ордеров. Кроме того, у нас есть возможность выбрать способ закрытия ордеров.

Если вы хотите повторить этот процесс для той же валютной пары, будет достаточно простого цикла, который позволит легко запускать программу непрерывно. Есть только одна загвоздка: как определить оптимальные входные данные? Решение довольно простое. Модель с R2, близким к единице, а также MSE и MAE, близкими к нулю, указывают на хорошее приближение. Однако остерегайтесь подгонки. После того, как мы определили оптимальные входные значения, вы можете не принимать во внимание MSE, MAE и R2, поскольку их расчеты занимают значительное количество времени, и в этом контексте важнее всего скорость получения результатов.


Создание исполняемого файла

Когда мы начинаем с валютной пары, первое, что нам нужно знать перед размещением ордеров, — это точны ли наши прогнозы. Учитывая, что Python постоянно развивается и у нас может возникнуть необходимость в обновлении библиотек, не помешало бы всё запаковать. Таким образом, у нас будет консолидированный пакет, готовый для тестирования и экспериментов.

Для этого мы воспользуемся Tkinter и преобразуем его в исполняемый файл с помощью auto-py-to-exe. Таким образом, мы не столкнемся с дальнейшими проблемами при определении входных данных и сможем более осторожно работать с файлом .py, который отвечает за исполнение ордеров.

Я продемонстрирую, как создать исполняемый файл для целей тестирования, оставив вам возможность настроить файл для исполнения ордеров.

В качестве исполняемого файла я буду использовать Tkinter, который предоставляет графический интерфейс для ввода значений. Помните, что путь к файлу должен быть записан с помощью "/" или "\". Интерфейс получит значения точности модели и отобразит график.

Ниже приведен код графического интерфейса (gui_console)

import tkinter as tk
from program_object import execute_program_with
from program_object_without import execute_program_without
import sys

class Aplication:
    def __init__(self, root):
        self.root = root
        self.root.title("DeepLearning Forecast")
        
        # labels and entries
        label1 = tk.Label(root, text="Input account mt5:")
        label1.pack(pady=5)

        self.value1_entry = tk.Entry(root)
        self.value1_entry.pack(pady=5)

        label2 = tk.Label(root, text="Input password:")
        label2.pack(pady=5)

        self.value2_entry = tk.Entry(root)
        self.value2_entry.pack(pady=5)

        label3 = tk.Label(root, text="Input server of mt5:")
        label3.pack(pady=5)

        self.value3_entry = tk.Entry(root)
        self.value3_entry.pack(pady=5)

        label4 = tk.Label(root, text="Input delay in days (for ticks):")
        label4.pack(pady=5)

        self.value4_entry = tk.Entry(root)
        self.value4_entry.pack(pady=5)

        label5 = tk.Label(root, text="Input timeframe 1 (1h),2,4,1d,1w:")
        label5.pack(pady=5)

        self.value5_entry = tk.Entry(root)
        self.value5_entry.pack(pady=5)

        label6 = tk.Label(root, text="Input epochs:")
        label6.pack(pady=5)

        self.value6_entry = tk.Entry(root)
        self.value6_entry.pack(pady=5)

        label7 = tk.Label(root, text="Input symbol:")
        label7.pack(pady=5)

        self.value7_entry = tk.Entry(root)
        self.value7_entry.pack(pady=5)


        label8 = tk.Label(root, text="Input path for mt5:")
        label8.pack(pady=5)

        self.value8_entry = tk.Entry(root)
        self.value8_entry.pack(pady=5)

        # Radio button to select program to execute
        self.opcion_var = tk.StringVar(value="program_object")
        radio_btn_object = tk.Radiobutton(root, text="With r2 Score, MAE & MSE", variable=self.opcion_var, value="program_object")
        radio_btn_object.pack(pady=5)
        radio_btn_object_without = tk.Radiobutton(root, text="Without", variable=self.opcion_var, value="program_object_without")
        radio_btn_object_without.pack(pady=5)

        # Botón start
        boton_execute = tk.Button(root, text="Run Program", command=self.execute_programa)
        boton_execute.pack(pady=10)

        # Botón close
        boton_quit = tk.Button(root, text="Exit", command=root.destroy)
        boton_quit.pack(pady=10)

    def write(self, text):
        # this method y called when sys.stdout.write is used
        self.salida_text.insert(tk.END, text)
    def flush(self):
        pass
    def execute_program(self):
        # Obteined value of the selected option
        selected_program = self.opcion_var.get()

        # Obteined value of inputs
        value1 = self.value1_entry.get()
        value2 = self.value2_entry.get()
        value3 = self.value3_entry.get()
        value4 = self.value4_entry.get()
        value5 = self.value5_entry.get()
        value6 = self.value6_entry.get()
        value7 = self.value7_entry.get()
        value8 = self.value8_entry.get()

                # Redirects stdout & stderr to the console
        sys.stdout = sys.__stdout__
        sys.stderr = sys.__stderr__

        # Calls the function to execute a selected program and pass values as arguments
        if selected_program == "program_object":
            execute_program_with(value1, value2, value3, value4, value5, value6, value7, value8)
        elif selected_program == "program_object_without":
            execute_program_without(value1, value2, value3, value4, value5, value6, value7, value8)
        # Restores stdout to a predeterminate value to no have conflicts
        sys.stdout = self
        sys.stderr = self

if __name__ == "__main__":
    root = tk.Tk()
    app = Aplication(root)
    root.mainloop()

А это вызываемый код (куда вам нужно вставить свой код):

import sys
import MetaTrader5 as mt5
from MetaTrader5 import *
import matplotlib.pyplot as plt
import seaborn as sns  # Import seaborn for residual plot



def main(value1, value2, value3, value4, value5, value6, value7,value8):
    # Now you can use variables value1, value2, value3, value4, value5, value6, value7 en tu programa
    print("Numero de cuenta mt5 en mt5: ", value1)
    print("Password en mt5: ", value2)
    print("Servidor en mt5: ", value3)
    print("Delay dias ticks: ", value4)
    print("Time frame: ", value5)
    print("Epochs: ", value6)
    print("Symbol: ", value7)
    print("Path a mt5: ", value8)
    

def execute_program_with(value1, value2, value3, value4, value5, value6, value7, value8):
    main(value1, value2, value3, value4, value5, value6, value7, value8)

Он заканчивается следующим образом:

if __name__ == "__main__":
    value1, value2, value3, value4, value5, value6, value7 = sys.argv[1:]
    main(value1, value2, value3, value4, value5, value6, value7)

Один код с оценками прогнозов модели и графиком, а другой просто отображает график.

Чтобы преобразовать это в исполняемый файл, мы будем использовать auto-py-to-exe. Сначала установим библиотеку.

pip install auto-py-to-exe

Запустим ее, просто набрав в терминале auto-py-to-exe

Не забудьте добавить gui_consola, а также файлы programa_objetivo.py и programa_objetivo_sin.py в местоположение скрипта. Кликните convert py to exe.

auto-py-to-exe

Возможно, вам придется использовать среду conda, для этого просто введите

conda activate XXX

где XXX - имя среды.

У вас могут возникнуть проблемы с Conda. В этом случае удалите Anaconda из панели управления (перейдите в 'Uninstall a program') и установите Python непосредственно с официального сайта. Вы можете скачать его здесь. Обязательно выберите свою операционную систему.

При установке Python не забудьте добавить путь к системным переменным. Кроме того, отключите ограничения длины.

Чтобы завершить настройку, установите matplotlib, seaborn и sklearn, выполнив следующую команду в терминале или командной строке:

pip install matplotlib
pip install seaborn
pip install scikit-learn


Графический интерфейс

Термин "Tk GUI" относится к графическому интерфейсу пользователя (GUI), созданному с использованием набора инструментов Tkinter на Python. Tkinter — встроенный модуль стандартной библиотеки Python, который упрощает разработку графических пользовательских интерфейсов.

Он выглядит так:

Графический интерфейс


Не забудьте добавить путь для mt5 и заменить "\" на "/", как здесь:

C:/Program Files/XXX MT5/terminal64.exe


Графический интерфейс

Это более или менее то, что вы увидите в графическом интерфейсе, когда обернете его в исполняемый файл и запустите.

001


Результаты для тестера выглядят так:

tester001


Значения внутри красного прямоугольника довольно важны и, очевидно, далеки от идеала. Необходимо добиться значения R-квадрата около 1, а MSE и MAE - около 0. Я должен попытаться использовать больше данных и/или больше эпох, а также изменить в скрипте значение для L2 (в конце концов мы получим хорошую подгонку и успеем создать ордера, это похоже на игру).

Используемые метрики:

R2 (R-квадрат), MSE (среднеквадратическая ошибка) и MAE (средняя абсолютная ошибка) — широко используемые показатели в статистике и машинном обучении для оценки производительности моделей:

  1. R2 (R-квадрат):

    • R2 — это статистическая мера, которая представляет собой долю дисперсии зависимой переменной, которую можно предсказать на основе независимой переменной (переменных).
    • Это шкала от 0 до 1, где 0 указывает на то, что модель не объясняет изменчивость зависимой переменной, а 1 указывает на идеальное объяснение.
  2. MSE (среднеквадратическая ошибка):

    • MSE — это метрика, которая вычисляет среднеквадратическую разницу между фактическими и прогнозируемыми значениями.
    • Она наказывает за более крупные ошибки более строго, чем за мелкие, что делает ее чувствительной к выбросам.
    • Математически это сумма квадратов разностей, деленная на количество наблюдений.
  3. MAE (средняя абсолютная ошибка):

    • MAE — это показатель, который рассчитывает среднюю абсолютную разницу между фактическими и прогнозируемыми значениями.
    • В отличие от MSE, он не выравнивает различия, что делает его менее чувствительным к выбросам.
    • Это сумма абсолютных разностей, деленная на количество наблюдений.

Эти метрики обычно используются в регрессионном анализе для оценки того, насколько хорошо модель способна предсказать результирующую переменную. В целом, более высокое значение R2 и более низкие значения MSE или MAE указывают на более эффективную модель.


Переобучение

Переобучение в глубоком обучении для торговли предполагает обучение модели, которая хорошо работает на исторических данных, но не может эффективно обобщать новые, до сих не встречавшиеся данные. Вот некоторые опасности, связанные с переобучением в контексте глубокого обучения трейдингу:

  1. Ограниченная генерализация: переобученная модель может улавливать шум или определенные закономерности в исторических данных, которые не отражают истинную динамику рынка. В результате модели может быть трудно обобщить новые рыночные условия или данные.
  2. Плохая производительность на реальных данных: поскольку переоснащенная модель по сути запоминает исторические данные, она может работать плохо при работе с реальными рыночными данными. Модель может давать неточные прогнозы или неспособна адаптироваться к меняющимся рыночным трендам.
  3. Изменения рыночного режима: финансовые рынки динамичны, и различные рыночные режимы (бычий, медвежий, боковой) могут обладать уникальными характеристиками. Переобученная модель, обученная на конкретном рыночном режиме, может потерпеть неудачу, когда рынок претерпевает смену режима, поскольку ей не хватает адаптивности.
  4. Ложные сигналы: переобучение может привести к тому, что модель будет улавливать шум или выбросы в исторических данных, что приведет к ложным сигналам. Эти ложные сигналы могут ввести торговые стратегии в заблуждение, что приведет к финансовым потерям в реальных торговых сценариях.
  5. Ошибка отслеживания данных: переобучение может усугубляться отслеживанием или утечкой данных. Если модель случайно подвергается воздействию информации из тестового набора во время обучения, она может изучить закономерности, которые плохо обобщаются на до сих пор не встречавшихся данных.
  6. Чувствительность к гиперпараметрам: переобученные модели могут быть чрезмерно чувствительны к гиперпараметрам, что затрудняет их точную настройку для обеспечения надежной работы в различных рыночных условиях.

Чтобы минимизировать эти опасности, специалисты по глубокому обучению трейдингу часто используют такие стратегии, как перекрестная проверка, методы регуляризации и включение функций, которые отражают более широкое понимание динамики рынка. Кроме того, необходимо соблюдать осторожность при интерпретации эффективности модели на исторических данных и проверке ее эффективности на наборах данных, выходящих за рамки выборки. Регулярный мониторинг и повторное обучение моделей также необходимы для адаптации к меняющимся рыночным условиям.

Перекрестная проверка в глубоком обучении:

Перекрестная проверка — это статистический метод, используемый для оценки производительности модели машинного обучения. В контексте глубокого обучения перекрестная проверка предполагает разделение набора данных на несколько подмножеств или сверток (folds). Модель обучается на некоторых из этих сверток и тестируется на оставшейся. Этот процесс повторяется несколько раз, при этом на каждой итерации для обучения и тестирования используются разные свертки. Затем показатели производительности усредняются по всем итерациям, чтобы обеспечить более надежную оценку производительности модели.

Распространенные типы перекрестной проверки включают k-кратную перекрестную проверку, при которой набор данных делится на k свертков, и каждая свертка используется в качестве тестового набора ровно один раз, и перекрестную проверку с исключением по одному (leave-one-out), когда каждая точка данных рассматриваться как одна свертка.

Перекрестная проверка помогает оценить, насколько хорошо модель обобщает невидимые данные, и снижает риск переобучения или недостаточного обучения.

Регуляризатор ядра (kernel_regularizer=l2):

В контексте глубокого обучения и нейронных сетей термин регуляризатор ядра относится к методу регуляризации, применяемому к весам (или ядрам) слоев нейронной сети. Параметр kernel_regularizer=l2 специально указывает на использование регуляризации L2 для весов.

Регуляризация L2 добавляет к функции потерь штрафной член (penalty term), пропорциональный квадрату величины весов. Цель состоит в том, чтобы не допустить того, чтобы какой-либо отдельный вес стал слишком большим и доминировал в процессе обучения. Это помогает предотвратить переобучение, не давая модели слишком точно подгонять обучающие данные.

Таким образом, kernel_regularizer=l2 — это метод регуляризации, используемый для управления сложностью нейронной сети путем штрафования больших весов. Это один из инструментов для решения проблем переобучения в моделях глубокого обучения.


График

График сохраняется там, где вы запускаете скрипт.

Число, указанное в заголовке, — это время в минутах от первой оранжевой линии до конца графика.

Вы можете изменить то, что вам нужно на графике (например, показывать только диапазон между оранжевыми линиями с помощью этого кода линии):

plt.plot(dff5.iloc[punto_entrada, valor_25], linestyle='-', label='Predicted')

Просто прочитайте код и попытайтесь его понять.

График

Перед нами сгенерированный график. Красные линии указывают, где следует размещать ордера на покупку/продажу и закрытие. Оранжевые линии представляют диапазон, в котором модель считается более надежной и точной, особенно в первых 20% интервала. В данном случае я использовал 4-часовой таймфрейм, поэтому оптимальным интервалом является первый час, показывающий максимальные и минимальные точки для входа и выхода. Как только скрипт достигнет конца, он зациклится, и если мы добавим ордера, он продолжит работать непрерывно. Остальные отмеченные линии помогают позиционироваться на графике при выставлении ордеров вручную.

Цена, указанная в прогнозах, ориентировочная. Нам важно знать лишь то, где находятся минимум и максимум в интервале.


Как всё это использовать

Что я делаю:

Сначала я экспериментирую с символом, используя графический интерфейс и тестируя его с помощью R2, MAE и MSE. Я перебираю разные значения эпох и дней задержки. Во-вторых, как только я определю многообещающие значения для прогнозирования, тщательно избегая переоценки, я перехожу к скрипту. Я автоматизирую скрипт для автономного выполнения ордеров, арендуя VPS для непрерывного исполнения ордеров в течение недели. Примечательно, что при возникновении значительных ошибок также есть параметр, который можно настроить. Например, в случае с криптовалютами можно выбрать L2, равный 0,01, вместо 0,001, используемого для форекса. Методом проб и ошибок можно определить оптимальные значения количества данных (дней задержки) и эпох.


Важно учитывать, что расчет MSE, R2 и MAE занимает примерно одну эпоху. Поэтому желательно изначально провести эксперименты. После достижения хорошей аппроксимации модели ее можно использовать без этих параметров. В Python это достигается либо комментированием (отключением) соответствующего кода, либо использованием символа #.


С этой программой Python я использую цикл. На случай сбоя при закрытии ордеров (я реализовал механизм отмены команд по символу), я также использую timer.mql5, так как у меня установлен интервал для ордеров, а именно один час. Это гарантирует, что ни один ордер не останется открытым более часа.


Цикл

Чтобы зациклить DeepLearningForecast, вместо while = True, мы можем сделать следующее (Loop.py):

import time
import subprocess

def execute_program():
    
    subprocess.run(['python', 'DeepLearningForecast.py'])

# Stablish and interval in seconds (for example, 60 seconds)
seconds_interval = 5


while True:
    execute_program()
    print(f"Program executed. Waiting {seconds_interval} seconds")

    # waits the interval before executing the program
    time.sleep(seconds_interval)


Телеграм-бот

В MetaTrader 5 уже есть различные способы просмотра информации, но в отношении этого бота мне иногда интересно узнать, точно ли он прогнозирует и правильно ли исполняет ордера. Чтобы отслеживать эти действия и впоследствии сравнивать прогнозы с реальным графиком, я отправляю график и соответствующую информацию Телеграм-боту. Ниже я объясню, как создать Телеграм-бота и отправлять через него данные.

Пошаговое руководство по созданию Телеграм-бота и получению ID чата и токена для отправки сообщений через запросы:

Создание Телеграм-бота:

  1. Откройте Telegram и найдите BotFather:

    • Откройте Telegram и введите BotFather в строке поиска.
    • Начните чат с BotFather.
  2. Создание нового бота:

    • Используйте команду /newbot, чтобы создать нового бота.
    • Следуйте инструкциям BotFather, чтобы присвоить боту имя, а также имя пользователя.
    • После завершения BotFather предоставит вам токен. Этот токен необходим для аутентификации вашего бота.

Получение ID чата:

  1. Запустите чат со своим ботом:

    • После создания бота запустите с ним чат в Telegram.
  2. Посетите URL-адрес API бота:

    • Откройте веб-браузер и перейдите по следующему URL-адресу, заменив BOT_TOKEN на токен вашего бота:
    https://api.telegram.org/botBOT_TOKEN/getUpdates
  1. Отправьте сообщение боту:

    • Вернитесь в Telegram и отправьте сообщение боту, с которым вы инициировали чат.
  2. Обновите страницу браузера:

    • Вернитесь на страницу браузера, где вы ввели URL-адрес API бота.
    • Обновите страницу.
  3. Найдите ID чата:

    • В ответе JSON найдите раздел, содержащий историю сообщений.
    • Найдите объект, представляющий ваше недавно отправленное сообщение.
    • Внутри этого объекта найдите поле чата. Внутри него поле id будет идентификатором чата.


В результате вы сможете получить сообщение, подобное следующему (чтобы отслеживать бот).

Сообщение в Telegram



Другие исправления

Поскольку мне не понравилось, как закрываются ордера, я модифицировал скрипт, чтобы воспользоваться трендами. Я добавил простую скользящую среднюю (SMA) и поручил ей закрывать ордера, когда цена пересекает SMA.

Итак, чтобы исправить это:

SMA


Я добавил следующее:

def obtain_sma_pandas(symbol, periodo):
    prices = mt5.copy_rates_from_pos(symbol, mt5.TIMEFRAME_M1, 0, periodo)
    df_sma = pd.DataFrame(prices)
    sma = df_sma['close'].rolling(window=periodo).mean().iloc[-1]
    return sma

а также:

    action="buy"
    price = mt5.symbol_info_tick(symbol).ask
    period_sma=14
    sma = obtain_sma_pandas(symbol, period_sma)
    while True:
        # Obtener el precio actual y la SMA
        actual_price = price
        sma = obtain_sma_pandas(symbol, period_sma)

        # Si el precio está por debajo de la SMA, cerrar la orden
        if actual_price > sma:
            close_all_position(symbol)
            continue

        # Esperar antes de verificar nuevamente
        time.sleep(10)

Вы также можете добавить в скрипт adx для поиска сделок в период тренда следующим образом:

def calculate_adx(high, low, close, period):

    tr_pos = []
    tr_neg = []

    for i in range(1, len(close)):
        trh = max(high[i] - high[i - 1], 0)
        trl = max(low[i - 1] - low[i], 0)

        if trh > trl:
            tr_pos.append(trh)
            tr_neg.append(0)
        elif trl > trh:
            tr_pos.append(0)
            tr_neg.append(trl)
        else:
            tr_pos.append(0)
            tr_neg.append(0)

    atr = pd.Series(tr_pos).ewm(span=period, adjust=False).mean() + pd.Series(tr_neg).ewm(span=period, adjust=False).mean()
    dx = (pd.Series(tr_pos).ewm(span=period, adjust=False).mean() / atr) * 100
    adx = dx.ewm(span=period, adjust=False).mean()

    return adx.iloc[-1]


ONNX

Теперь, используя идеи из статьи "Использование ONNX-моделей в MQL5" от MetaQuotes, я конвертирую модель в формат ONNX. Следуя рекомендациям, изложенным в той же статье, я интегрирую полученную модель ONNX в базовый советник (EA) для начала торговых операций. Такой подход позволяет плавно интегрировать модели машинного обучения в среду MQL5, расширяя возможности торгового алгоритма.

Перед форматированием в ONNX необходимо скачать данные. Для этого мы будем использовать загруженный мною скрипт (ticks_to_csv). Просто сохраните его в папке советника MQL5, откройте в IDE и скомпилируйте. После этого добавьте скрипт на график и дайте ему поработать некоторое время (поскольку он загружает все тики для символа, процесс может занять некоторое время). В журнале вы увидите сообщение о завершении процесса. Я использовал его для EURUSD, и он занял несколько гигабайт.

#property script_show_inputs
#property strict
//--- Requesting 100 million ticks to be sure we receive the entire tick history
input long      getticks=100000000000; // The number of required ticks
string fileName = "ticks_data.csv";
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   int     attempts=0;     // Count of attempts
   bool    success=false;  // The flag of a successful copying of ticks
   MqlTick tick_array[];   // Tick receiving array
   MqlTick lasttick;       // To receive last tick data

   SymbolInfoTick(_Symbol,lasttick);
//--- Make 3 attempts to receive ticks
   while(attempts<3)
     {

      //--- Measuring start time before receiving the ticks
      uint start=GetTickCount();
      //--- Requesting the tick history since 1970.01.01 00:00.001 (parameter from=1 ms)
      long received=CopyTicks(_Symbol,tick_array,COPY_TICKS_ALL,1,getticks);

      // Check if ticks were successfully copied
      if(received > 0)
        {
         // Open the CSV file for writing
         int fileHandle = FileOpen(fileName, FILE_WRITE | FILE_CSV);

         // Check if the file was opened successfully
         if(fileHandle != INVALID_HANDLE)
           {
            // Write the CSV header
            FileWrite(fileHandle, "Time,Bid,Ask");

            // Write tick data to the CSV file
            for(long i = 0; i < received; i++)
              {
               string csvLine = StringFormat("%s,%.5f,%.5f", TimeToString(tick_array[i].time), tick_array[i].bid, tick_array[i].ask);
               FileWrite(fileHandle, csvLine);
              }


            // Close the CSV file
            FileClose(fileHandle);

            // Print success message
            Print("Downloaded ", received, " ticks for symbol ", _Symbol, " and period ", Period());
            Print("Ticks data saved to ", fileName);
           }
         else
           {
            // Print an error message if the file could not be opened
            Print("Failed to open the file for writing. Error code: ", GetLastError());
           }
        }
      else
        {
         // Print an error message if no ticks were downloaded
         Print("Failed to download ticks. Error code: ", GetLastError());
        }

      if(received!=-1)
        {
         //--- Showing information about the number of ticks and spent time
         PrintFormat("%s: received %d ticks in %d ms",_Symbol,received,GetTickCount()-start);
         //--- If the tick history is synchronized, the error code is equal to zero
         if(GetLastError()==0)
           {
            success=true;
            break;
           }
         else
            PrintFormat("%s: Ticks are not synchronized yet, %d ticks received for %d ms. Error=%d",
                        _Symbol,received,GetTickCount()-start,_LastError);
        }
      //--- Counting attempts
      attempts++;
      //--- A one-second pause to wait for the end of synchronization of the tick database
      Sleep(1000);
     }
  }

И из этих данных (CSV) мы будем читать и использовать нужный нам сегмент, преобразуя его в DataFrame (DeepLearning_ONNX.py был определен за последние 1000 дней до сегодняшнего дня). Вы можете использовать весь набор данных, но вам потребуется довольно много оперативной памяти.

После загрузки данных мы можем начать обучение модели ONNX с помощью файла DeepLearningForecast_ONNX_training.py. (Сохраните файл в папке внутри папки File в MQL5).

Данные импортируются следующим образом:

rates = pd.read_csv(file_path, encoding='utf-16le')

Модель создается следующим образом:

onnx_model = tf2onnx.convert.from_keras(model, output_path=output_path)


Заключение

В заключение в этой статье изложен комплексный подход к разработке скрипта Python для автоматической торговли с использованием глубокого обучения. Мы изучили интеграцию MetaTrader 5, предварительную обработку данных, обучение модели и выполнение ордеров на покупку/продажу. Использование Tkinter и auto-py-to-exe для создания исполняемого файла с удобным интерфейсом добавляет скрипту практичности.

Кроме того, были рассмотрены вопросы выбора соответствующих входных данных, оценки эффективности модели и реализации эффективных стратегий закрытия ордеров. Визуальный вывод скрипта, демонстрирующий прогнозы, надежные диапазоны и размещение ордеров, обеспечивает четкий и действенный обзор как для автоматической, так и для ручной торговли.

В дальнейшем мы научимся импортировать все тики для валютной пары. Мы используем Python для чтения данных, создания модели ONNX и (если следующая статья вызовет интерес) выполнения модели в MetaTrader 5 с помощью советника.

Однако важно подчеркнуть динамичный характер финансовых рынков, гарантирующий постоянный мониторинг, адаптацию и потенциальные улучшения модели. Изложенная структура служит прочной основой, и пользователям предлагается в дальнейшем адаптировать и совершенствовать ее с учетом меняющихся рыночных условий и своих конкретных торговых целей.


Отказ от ответственности

Отказ от ответственности: прошлые результаты не гарантируют будущих результатов. Торговля криптовалютами и другими финансовыми инструментами сопряжена с риском. Ни одна стратегия не может гарантировать прибыль. Всегда проводите тщательное исследование и обращайтесь за советом к финансовым специалистам, прежде чем принимать инвестиционные решения.


Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/13975

Прикрепленные файлы |
Timer.mq5 (4.57 KB)
Timer_modified.mq5 (9.73 KB)
Loop.py (0.4 KB)
gui_console.py (3.85 KB)
program_object.py (23.96 KB)
ticks_to_csv.mq5 (3.27 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (4)
Javier Santiago Gaston De Iriarte Cabrera
Javier Santiago Gaston De Iriarte Cabrera | 20 янв. 2024 в 15:07

Здравствуйте, извините, я допустил ошибку с файлом DeepLearningForecast_ONNX_training.py.

Вам придется использовать этот

Javier Santiago Gaston De Iriarte Cabrera
Javier Santiago Gaston De Iriarte Cabrera | 20 янв. 2024 в 17:54
Javier Santiago Gaston De Iriarte Cabrera #:

Здравствуйте, извините, я допустил ошибку в файле DeepLearningForecast_ONNX_training.py.

Вам придется использовать этот

Извините, в этом тоже есть ошибки, я сделаю этот py завтра и оставлю его здесь.


Я также делаю продолжение, вы получите правильный py также в продолжении.

Javier Santiago Gaston De Iriarte Cabrera
Javier Santiago Gaston De Iriarte Cabrera | 20 янв. 2024 в 22:20
Javier Santiago Gaston De Iriarte Cabrera #:

извините, в этой тоже есть ошибки, завтра сделаю пи и оставлю здесь.


Я также делаю продолжение, вы получите правильный py также в продолжении.

Вот .py, который я буду использовать в следующей статье.

Javier Santiago Gaston De Iriarte Cabrera
Javier Santiago Gaston De Iriarte Cabrera | 22 янв. 2024 в 04:15
Javier Santiago Gaston De Iriarte Cabrera #:

Вот .py, который я буду использовать в следующей статье.

Извините за нехватку времени.

Я забыл опустить несколько NaN

Вот финальный файл (но его можно взять из следующей статьи)

Нейросети — это просто (Часть 90): Частотная интерполяция временных рядов (FITS) Нейросети — это просто (Часть 90): Частотная интерполяция временных рядов (FITS)
При изучении метода FEDformer мы приоткрыли дверь в частотную область представления временного ряда. В новой статье мы продолжим начатую тему. И рассмотрим метод, позволяющий не только проводить анализ, но и прогнозировать последующие состояния в частной области.
Модифицированный советник Grid-Hedge в MQL5 (Часть II): Создание простого сеточного советника Модифицированный советник Grid-Hedge в MQL5 (Часть II): Создание простого сеточного советника
В статье рассматривается классическая сеточная стратегия, подробно описана ее автоматизация с помощью советника на MQL5 и проанализированы первоначальные результаты тестирования на истории. Также подчеркивается необходимость в долгом удержании позиций и рассматривается возможность оптимизации ключевых параметров (таких как расстояние, тейк-профит и размеры лотов) в будущих частях. Целью этой серии статей является повышение эффективности торговой стратегии и ее адаптируемости к различным рыночным условиям.
Алгоритм кометного следа (Comet Tail Algorithm, CTA) Алгоритм кометного следа (Comet Tail Algorithm, CTA)
В данной статье мы рассмотрим новый авторский алгоритм оптимизации CTA (Comet Tail Algorithm), который черпает вдохновение из уникальных космических объектов - комет и их впечатляющих хвостов, формирующихся при приближении к Солнцу. Данный алгоритм основан на концепции движения комет и их хвостов, и предназначен для поиска оптимальных решений в задачах оптимизации.
Риск-менеджер для алгоритмической торговли Риск-менеджер для алгоритмической торговли
Целями данной статьи являются: доказать обязательность применения риск-менеджера, адаптация принципов контролируемого риска при торговле алгоритмически в отдельном классе, чтобы каждый смог самостоятельно убедиться в эффективности подхода нормирования риска при внутридневной торговле и инвестировании на финансовых рынках. В данной статье мы подробно раскроем написание класса риск-менеджера для алгоритмической торговли в продолжение к предыдущей статье по написанию риск-менеджера для ручной торговли.