Главная PythonDLL Агенты MQL5Doc JavaDoc GitHub English

MetaTrader 5 (MQL5) + Python 3 DLL

Использование MetaTrader с Python 3 на финансовых фондовых биржах, Forex, CFD и Futures. Из MetaTrader можно получать котировки в Python, но нет полноценной связи между ними. Пост одного из разработчиков.

Эта обертка создавалась с учетом изменений в Python 3.7 Скачать ZIP с GitHub.

Python сейчас является стандартом для библиотек машинного обучения (TensorFlow, PyTorch и т.п.). Сам Python довольно медленный и поэтому все библиотеки машинного обучения на C/C++ используют его только для взаимодействия с пользователем. Не всегда можно без проблем подключить библиотеки машинного обучения на прямую к коду C/C++.

Главная идея и отличие этой обертки от остальных: обмен данными между MQL и Python через заранее созданные функции. Это самый быстрый и надежный метод обмена данными. Нет затрат времени на синтаксический разбор и компиляцию кода Python, который появляется при использовании eval().

Имеется класс (актуальный код тут):

class MQL():
    def getLong(self, magic: int, value: int, array: tuple) -> tuple or list:
        raise NotImplementedError

    def getULong(self, magic: int, value: int, array: tuple) -> tuple or list:
        raise NotImplementedError

    def getDouble(self, magic: int, value: float, array: tuple) -> tuple or list:
        raise NotImplementedError

    def getString(self, magic: int, value: str, array: bytes) -> str:
        raise NotImplementedError


__mql__ = MQL()

Название класса неважно, потому что отражение функций идет через переменную __mql__. Также у функции pyEval(..., override_class) аргумент override_class=true, когда изменяется переменная __mql__.

Пример:
PythonDLL_Example.mq5 и PythonDLL_Example.py

Можно определить среду выполнения кода Python:

if globals().get('__PythonDLL__'):
    print('run in MetaTrader')
elif __name__ == '__main__':
    print('run as script')

Проблемы и их решения

При тестировании требуется “Разрешить импорт DLL” на глобальном уровне.

Поиск необходимой python3.dll происходит в следующем порядке:

  1. В папках с terminal64.exe или metatester64.exe при тестировании на Агентах.
  2. В папках, указанных в переменной PATH. Проще изменить переменную PATH и заодно почистить ее от “мусора”.

Если ли python3.dll не найдена:

Cannot load 'Roffild\PythonDLL\x64\Release\PythonDLL.dll' [126]
Cannot call 'pyInitialize', 'Roffild\PythonDLL\x64\Release\PythonDLL.dll' is not loaded
unresolved import function call

Python это не только python3.dll, но и его окружение, которое нужно настраивать. Самое популярное и простое решение это установить Anaconda. Есть еще Miniconda (минимум 200МБ), если четко знаешь, какие пакеты понадобятся при выполнении скрипта.

Python создавался как отдельное приложение и при встраемости есть проблемы, которые вряд ли когда-нибудь будут исправлены:

При активной консоли можно заметить вывод Py_FatalError(), пока она не исчезнет:

Fatal Python error: Py_Initialize: unable to load the file system codec

Ошибки компиляции и выполнения кода Python не отображаются автоматически на активной консоли. Но при использовании класса CPythonDLL ошибки отображаются в логе терминала.

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

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

При использовании модуля multiprocessing необходимо указывать абсолютный путь к файлу в __file__ и sys.argv. Пример находится здесь.

Sub-interpreters

Обертка создавалась с суб-интерпретаторами, но пришлось использовать простой GIL. Суб-интерпретаторы не совместимы с популярными библиотеками для Питона. issue37186, issue10915. Но можно собрать эту обертку с PYTHONDLL_SUBINTERPRETERS.

При запуске в терминале нескольких независимых экспертов, индикаторов и скриптов, использующих эту обертку, для каждого потока создается свой изолированный интерпретатор с помощью Py_NewInterpreter(). Но может возникнуть задержка при переключении потока, потому что в Python нет полноценного многопоточного выполнения, а есть global interpreter lock (GIL), который блокирует другие потоки при выполнении кода Python.

При тестировании всегда есть только один интерпретатор.

Для разработчиков

Официальная статья по созданию DLL.

Дополнение к статье:

Тип int в Python бесконечный:

int("9999999999999999999999999999999999999999999999", 10).bit_length() == 153

Типы в MQL:

long = 8 байт (64 бита) = -9 223 372 036 854 775 808 ... 9 223 372 036 854 775 807
ulong = 8 байт (64 бита) = 0 ... 18 446 744 073 709 551 615

CrashDumps:

%LOCALAPPDATA%\CrashDumps\

По всем вопросам обращаться в эту тему.