Оборачиваем ONNX-модели в классы
Оглавление
Введение
В предыдущей статье мы использовали две ONNX-модели для организации классификатора голосования. При этом весь исходный текст был организван в виде одного MQ5-файла. Да, весь код разбит на функции. Но попробуйте, например, поменять местами модели. А если добавить ещё модель? Исходный текст ещё более распухнет. Попробуем объектно-ориентированный подход.
1. Какие модели мы собираемся использовать
В предыдущем классификаторе голосования мы использовали одну классификационную модель и одну регрессионную модель. В регрессионной модели вместо предсказанного движения цены (вниз, вверх, не изменяется) мы получаем предсказанную цену, на основе которой и вычисляем класс. Однако, в этом случае мы не имеем распределения вероятностей по классам, что не позволяет проводить так называемое "мягкое голосование".
Мы подготовили 3 классификационные модели. Две модели уже использовались в статье "Пример ансамбля ONNX-моделей в MQL5". Первая модель — регрессионная — переделана в классификационную. Обучение проводилось на сериях из 10 цен OHLC. Вторая модель — классификационная. Обучение проводилось на сериях из 63 цен Close.
Наконец, ещё одна модель. Классификационная модель обучалась на сериях из 30 цен Close и сериях простых скользящих средних с периодами усреднения 21 и 34. Мы не делали никаких предположений по поводу пересечения скользящих средних с графиком Close и между собой — все закономерности посчитает и запомнит сеть в виде матриц коэффициентов между слоями.
Все модели обучались на данных сервера MetaQuotes-Demo, EURUSD D1 с 2010.01.01 по 2023.01.01. Тренировочные скрипты всех трёх моделей написаны на Питоне и приложены к данной статье. Мы не будем приводить их исходные коды здесь, чтобы не отвлекать внимание читателя от основной темы нашей статьи.
2. Нужен один базовый класс для всех моделей
Три модели. Каждая отличается от другой размером входных данных, подготовкой входных данных. У всех моделей есть общность. Один и тот же интерфейс. Классы всех моделей должны наследоваться от одного и того же базового класса.
Попробуем представить базовый класс.
Базовый класс можно использовать как для моделей регрессии, так и для моделей классификации. Надо будет только реализовать в классе-наследнике соответствующий метод — PredictPrice или PredictClass.
В базовом классе задаётся с каким символом-периодом должна работать модель (на каких данных обучалась модель). В базовом классе проводится проверка, что эксперт, использующий модель, работает на нужном символе-периоде, а также создаётся ONNX-сессия для исполнения модели. В базовом классе обеспечивается работа только в начале нового бара.
3. Класс для первой модели
Наша первая модель называется model.eurusd.D1.10.class.onnx, то есть классификационная модель, тренированная на EURUSD D1 на сериях из 10 цен OHLC.
Как было сказано выше: "Три модели. Каждая отличается от другой размером входных данных, подготовкой входных данных". И мы переопределили всего два метода — Init и PredictClass. В двух других классах для двух других моделей будут переопределены эти же самые методы.
В методе Init вызывается метод базового класса CheckInit, где создаётся сессия для нашей ONNX-модели. А также явно выставляются размеры входного и выходного тензоров. Здесь больше комментариев, чем кода.
В методе PredictClass обеспечивается точно такая же подготовка входных данных, что и при обучении модели. На вход подаётся матрица нормализованных цен OHLC.
4. Проверим, как это работает
Для проверки работоспособности нашего класса был создан очень компактный эксперт.
Так как модель обучалась на ценовых данных до 2023 года, запустим тестирование с 1 января 2023 года.

И получим следующий результат:

Как видим, вполне работоспособная модель.
5. Класс для второй модели
Вторая модель называется model.eurusd.D1.30.class.onnx. Классификационная модель, тренированная на EURUSD D1 на сериях из 30 цен Close и двух простых скользящих средних с периодами усреднения 21 и 34.
Как и в предыдущем классе, в методе Init вызывается метод базового класса CheckInit, где создаётся сессия для ONNX-модели и явно выставляются размеры входного и выходного тензоров
В методе PredictClass обеспечиваются серии из 30 предыдущих Close и рассчитанные скользящие средние. Данные нормируются тем же способом, что и при обучении.
Проверим, как работает эта модель. Для этого изменим всего две строки проверочного эксперта
Параметры тестирования те же самые.

Видим, что модель работает.
6. Класс для третьей модели
Последняя модель называется model.eurusd.D1.63.class.onnx. Классификационная модель, тренированная на EURUSD D1 на сериях из 63 цен Close.
Это — самая простая модель из трёх. Поэтому код метода PredictClass получился таким компактным.
Опять изменим две строки в эксперте
И запустим тестирование с теми же самыми настройками.

Модель работает
7. Собираем все модели в одном эксперте. Жёсткое голосование
Все три модели показали свою работоспособность. Теперь попробуем объединить их усилия. Устроим голосование моделей.
Предварительные объявления и определения
Функция OnInit
Функция OnTick
Результат тестирования всё с теми же самыми настройками.

Вспомним работу всех трёх моделей по отдельности, а именно количество прибыльных и убыточных трейдов.
Первая модель
11 : 3
Вторая модель
6 : 1
Третья модель
16 : 10
Похоже, при помощи жёсткого голосования мы улучшили результат — 16 : 4. Но, конечно же, необходимо смотреть полные отчёты и графики тестирования.
8. Мягкое голосование
Мягкое голосование отличается от жёсткого тем, что учитывается не количество голосов, а считается сумма вероятностей всех трёх классов от всех трёх моделей. И уже по самой высокой вероятности выбирается класс.
Для обеспечения мягкого голосования необходимо внести некоторые изменения.
В базовом классе:
В классах наследниках:
В эксперте:
Тестируем всё с теми же настройками. Во входных параметрах выбираем Soft.

Получим результат.

Прибыльных трейдов — 15, Убыточных трейдов — 3. В денежном выражении жёсткое голосование тоже оказалось лучше, чем мягкое.
9. Единогласное голосоване
Интересно посмотреть на результат единогласного голосования, то есть при количестве голосов 3.

Очень консервативная торговля. При этом единственная убыточная сделка была закрыта при окончании тестирования (возможно, она и не убыточная на самом деле).

Важно: обращаем ваше внимание, что использованные в статье модели представлены только в целях демонстрации работы с ONNX-моделями средствами языка MQL5. Советник не предназначен для торговли на реальных счетах.
Заключение
Мы показали, как объектно-ориентированное программирование позволяет упростить написание программ. Все сложности моделей (а модели могут быть гораздо более сложными, чем представленные нами в качестве примера) прячутся в своих классах. А остальная "сложность" уместилась в 45 строках функции OnTick.
Last updated