Описание структуры скрипта Python

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

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

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

Модуль os содержит функции для работы с операционной системой. Использование данной библиотеки позволяет создавать кросс-платформенные приложения, так как работа функций этого модуля не зависит от установленной операционной системы. Приведем лишь некоторые функции библиотеки os:

  • os.name — возвращает имя операционной системы. В результате выполнения функции возможны следующие варианты: 'posix', 'nt', 'mac', 'os2', 'ce', 'java'.
  • os.environ — функция для работы с переменными окружения, позволяющая изменять, добавлять и удалять переменные окружения.
  • os.path — содержит целый ряд функций для работы с путями файлов и директорий.

Модуль Pandas представляет собой библиотеку для обработки и анализа данных. Библиотека предоставляет специальные структуры данных и операции для обработки числовых таблиц и временных рядов. Она позволяет проводить анализ и моделирование данных без использования специализированных для статистической обработки языков программирования, таких как R и Octave.

Пакет предназначен для очистки и первичной оценки данных по общим статистическим показателям. Он позволяет вычислять среднее значение, квантили и т. д. В то же время пакет нельзя назвать статистическим в полном смысле, однако создаваемые им наборы данных типов DataFrame и Series применяются в качестве входных в большинстве модулей анализа данных и машинного обучения SciPy, Scikit-Learn и других.

Объект DataFrame создан в библиотеке Pandas. Он предназначен для работы с индексированными массивами двумерных данных.

Кроме того, библиотека предоставляет:

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

Библиотека дает возможность создания иерархического индексирования, что позволяет работать с данными высокой размерности в структурах меньшей размерности. Функции для работы с временными рядами позволяют формировать временные периоды и изменять интервалы. Библиотека оптимизирована для высокой производительности, наиболее важные части кода написаны на Cython и C.

Еще одна библиотека для работы с многомерными массивами — NumPy — представляет собой библиотеки с открытым исходным кодом. Основными возможностями данного модуля являются поддержка многомерных массивов (включая матрицы) и высокоуровневых математических функций, предназначенных для работы с многомерными массивами.

В библиотеке NumPy реализованы вычислительные алгоритмы в виде функций и операторов, которые оптимизированы для работы с многомерными массивами. Библиотека представляет возможность проведения векторных операций над данными. При этом все функции написаны на C и оптимизированы для максимальной производительности. В результате любой алгоритм, который может быть выражен в виде последовательности операций над массивами (матрицами) и реализованный с использованием NumPy, работает так же быстро, как эквивалентный код, выполняемый в MATLAB.

При этом NumPy можно рассматривать в качестве альтернативы использования MATLAB. Оба языка интерпретируемые и позволяют выполнять операции над массивами.

NumPy часто используется в качестве базы для работы с многомерными массивами в других библиотеках. В том числе представленный выше Pandas также использует библиотеку NumPy для низкоуровневых операций с массивами.

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

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

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

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

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

Перед использованием библиотек нужно установить их в используемом окружении Python. Для этого в командной строке или Windows PowerShell с правами администратора системы нужно выполнить ряд команд:

  • установка NumPy

pip install numpy

  • установка Pandas

pip install pandas

  • установка Matplotlib

pip install matplotlib

  • установка TensorFlow

pip install tensorflow

  • установка Keras

pip install keras

  • установка библиотеки MetaTrader 5

pip install MetaTrader5

Переходя непосредственно к структуре нашего скрипта, создадим шаблон template.py. Скрипт будет состоять из нескольких блоков. Сначала нужно подключить необходимые библиотеки к нашему скрипту.

# импорт библиотек
import os
import pandas as pd
import numpy as np
import tensorflow as tf 
from tensorflow import keras 
import matplotlib as mp
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
import MetaTrader5 as mt5

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

# устанавливаем параметры графиков результатов
mp.rcParams.update({'font.family':'serif',
                    'font.serif':'Clear Sans',
                    'axes.labelsize':'medium',
                    'legend.fontsize':'small',
                    'figure.figsize':[6.0,4.0],
                    'xtick.labelsize':'small',
                    'ytick.labelsize':'small',
                    'axes.titlesize''x-large',
                    'axes.titlecolor''#333333',
                    'axes.labelcolor''#333333',
                    'axes.edgecolor''#333333'
                   })

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

Следовательно, на следующем шаге мы загрузим исходные данные из файла в таблицу. Обратите внимание на следующий момент: поскольку MetaTrader 5 ограничивает доступ к файлам из своих программ размерами песочницы, нужно указывать полный путь доступа к файлу исходных данных. Он будет находиться в каталоге MQL5\Files вашего терминала или его подкаталогах, если они были указаны при сохранении файла данных.

Вместо того, чтобы однозначно прописывать путь к песочнице терминала в коде нашей программы, мы просто запросим его из MetaTrader 5 средствами предлагаемого API. Для этого мы сначала подключаемся к установленному терминалу и проверяем результат выполнения данной операции.

# Подключаемся к терминалу MetaTrader 5
if not mt5.initialize():
    print("initialize() failed, error code =",mt5.last_error())
    quit()

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

# Запрашиваем путь в песочницу
path=os.path.join(mt5.terminal_info().data_path,r'MQL5\Files')
mt5.shutdown()

В следующем небольшом блоке загрузки данных можно увидеть использование функций сразу трех вышеуказанных библиотек. С помощью функции os.path.join мы сцепляем путь к рабочему каталогу с именем файла обучающей выборки. Функцией read_table из библиотеки Pandas мы считываем и преобразуем содержимое CSV-файла в таблицу. Затем полученную таблицу преобразуем в двумерный массив с помощью функции библиотеки NumPy.

# Загрузка обучающей выборки
filename = os.path.join(path,'study_data.csv')
data = np.asarray( pd.read_table(filename,
                   sep=',',
                   header=None,
                   skipinitialspace=True,
                   encoding='utf-8',
                   float_precision='high',
                   dtype=np.float64,
                   low_memory=False))

Непосредственно считывание содержимого CSV-файла и преобразование строк в таблицу осуществляется функцией read_table из библиотеки Pandas. Данная функция имеет довольно много параметров для точной настройки методов преобразования строчных данных в требуемый числовой тип данных. Полное их описание можно найти в документации к библиотеке. Мы же опишем лишь используемые:

  • filename — имя считываемого файла с указанием полного или относительного пути;
  • sep — указываем используемый в файле разделитель данных;
  • header — номера строк для использования в качестве имен столбцов и начала данных, при отсутствии заголовков указываем значении None;
  • skipinitialspace — логический параметр указывает, пропускать ли пробелы после разделителя;
  • encoding — указываем тип используемой кодировки;
  • float_precision — определяет, какой преобразователь должен использоваться для значений с плавающей запятой;
  • dtype — указывает конечный тип данных;
  • low_memory — внутренняя обработка файла по частям, что приведет к меньшему использованию памяти при синтаксическом анализе.

В результате этих операций все данные обучающей выборки были загружены в объект двумерного массива типа numpy.ndarray из библиотеки NumPy. Среди загруженных данных есть элементы исходных данных и целевые значения. Но для обучения нейронной сети нам нужно отдельно подавать на ее вход исходные данные, а после прямого прохода сравнить полученный результат с целевыми значениями. Получается, что исходные данные и цели для нейронной сети разделены во времени и месту использования.

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

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

# Разделение обучающей выборки на исходные данные и цели
inputs=data.shape[1]-2
targerts=2
train_data=data[:,0:inputs]
train_target=data[:,inputs:]

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

# Создание модели нейронной сети
model = keras.Sequential([....])

Модель Sequential представляет собой линейный стек слоев. Можно создать модель Sequential, передав список слоев конструктору модели, а также можно добавлять слои с помощью метода add.

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

Есть несколько способов указать размерность исходных данных:

  • Передать аргумент input_shape первому слою.
  • Некоторые 2D-слои поддерживают спецификацию размерности входных данных через аргумент input_dim. Некоторые 3D-слои поддерживают аргументы input_dim и input_length.
  • Использовать специальный тип нейронного слоя для исходных данных Input с параметром shape, в котором указывается размер слоя.

# Создание модели нейронной сети
model = keras.Sequential([keras.Input(shape=inputs),
                         # Наполнить модель описанием нейронных слоев
                         ])

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

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

  • optimizer — оптимизатор, может быть задан строковым идентификатором существующего оптимизатора или как экземпляр класса Optimizer;
  • loss — функция потерь, может быть задана строковым идентификатором существующей функции потерь, или собственная функция;
  • metrics — список показателей, которые модель должна оценивать во время обучения и тестирования, к примеру, для задачи классификации можно использовать ‘accuracy’;
  • loss_weights — необязательный список или словарь, определяющий скалярные коэффициенты для взвешивания вкладов в потери различных выходных данных модели;
  • weighted_metrics — список показателей, которые будут оценены и взвешены во время обучения и тестирования.

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

model.compile(optimizer='Adam'
              loss='mean_squared_error'
              metrics=['accuracy'])

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

  • x — массив исходных данных;
  • y — массив целевых результатов;
  • batch_size — необязательный параметр, указывает количество наборов пар «исходные данные — целевые значения» до обновления матрицы весов;
  • epochs — количество эпох обучения;
  • verbose — необязательный параметр, указывает уровень детализации логирования обучения: 0 — без сообщений, 1 — индикатор выполнения, 2 — одна строка на эпоху, auto — автоматический выбор;
  • callbacks — список обратных вызовов для применения во время обучения;
  • validation_split — выделение части обучающей выборки для валидации, указывается в долях от 1,0;
  • validation_data — отдельная выборка для валидации процесса обучения;
  • shuffle — логическое значение, которое указывает на необходимость перемешивания данных обучающей выборки перед следующей эпохой;
  • class_weight — необязательные индексы классов сопоставления словаря в значение веса, используемое для взвешивания функции потерь (только во время обучения);
  • sample_weight — необязательный массив весов NumPy для обучающей выборки, используемый для взвешивания функции потерь (только во время обучения);
  • initial_epoch — эпоха начала тренировки, может быть полезен для возобновления предыдущего тренировочного цикла;
  • steps_per_epoch — общее количество пакетов перед объявлением одной эпохи завершенной и началом следующей, по умолчанию равен размеру обучающей выборки;
  • validation_steps — общее количество пакетов из валидационной выборки перед остановкой при выполнении проверки в конце каждой эпохи, по умолчанию равен размеру валидационной выборки;
  • validation_batch_size — количество образцов на партию валидации;
  • validation_freq —  целое число, указывает количество периодов обучения перед выполнением нового прогона валидации.

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

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

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

callback = tf.keras.callbacks.EarlyStopping(monitor='loss'patience=5)
history = model.fit(train_datatrain_target,
                      epochs=500batch_size=1000,
                      callbacks=[callback],
                      verbose=2,
                      validation_split=0.2,
                      shuffle=true)

После завершения обучения сохраним обученную модель в файл на локальном диске. Для этого воспользуемся методами библиотек Keras и os.

# Сохранение обученной модели
model.save(os.path.join(path,'model.h5'))

Для наглядности и понимания процесса обучения выведем динамику изменения метрик в процессе обучения и валидации на графики. Здесь мы воспользуемся методами библиотеки Matplotlib.

# Отрисовка результатов обучения модели
plt.plot(history.history['loss'], label='Train')
plt.plot(history.history['val_loss'], label='Validation')
plt.ylabel('$MSE$ $Loss$')
plt.xlabel('$Epochs$')
plt.title('Dynamic of Models train')
plt.legend(loc='upper right')
 
plt.figure()
plt.plot(history.history['accuracy'], label='Train')
plt.plot(history.history['val_accuracy'], label='Validation')
plt.ylabel('$Accuracy$')
plt.xlabel('$Epochs$')
plt.title('Dynamic of Models train')
plt.legend(loc='lower right')

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

# Загрузка тестовой выборки
test_filename = os.path.join(path,'test_data.csv')
test = np.asarraypd.read_table(test_filename,
                   sep=',',
                   header=None,
                   skipinitialspace=true,
                   encoding='utf-8',
                   float_precision='high',
                   dtype=np.float64,
                   low_memory=false))

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

# Разделение тестовой выборки на исходные данные и цели
test_data=test[:,0:inputs]
test_target=test[:,inputs:]

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

  • x — массив исходных данных тестовой выборки;
  • y — массив целей тестовой выборки;
  • batch_size — размер пакета тестирования;
  • verbose — режим детализации логирования процесса (0 — без логирования, 1 — индикация выполнения);
  • sample_weight — необязательный параметр, используемый для взвешивания функции потерь;
  • steps — общее количество шагов для объявления процесса тестирования завершенным;
  • callbacks — список обратных вызовов, используемых в процессе обучения;
  • return_dict  — логическая переменная, которая определяет формат вывода результатов работы метода (True — в виде словаря «метрика - значение», False — в виде списка).

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

# Проверка результатов модели на тестовой выборке
test_losstest_acc = model.evaluate(test_datatest_target

В заключение выведем результаты тестирования в журнал и отобразим построенные ранее графики.

# Вывод результатов тестирования в журнал
print('Model in test')
print('Test accuracy:'test_acc)
print('Test loss:'test_loss)
 
# Вывод созданных графиков
plt.show()

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