Home PythonDLL Agents MQL5Doc JavaDoc GitHub Русский

MetaTrader 5 (MQL5) + Python 3 DLL

Use MetaTrader with Python 3 on financial stock exchanges, Forex, CFD and Futures. From MetaTrader, you can get quotes in Python, but no complete connection between them. This post is one of the developers (RU).

This wrapper was created with changes in Python 3.7 Download ZIP from GitHub.

Python is now the standard for machine learning libraries (TensorFlow, PyTorch, etc.). Python itself is quite slow and therefore all machine learning library on C/C++ using it only for user interaction. It is not always possible to connect machine learning libraries directly to the C/C++ code without problems.

The main idea and the difference of this wrapper: a data exchange between the MQL and Python via pre-created function. This is the fastest and most reliable data exchange method. There is no time spent on parsing and compiling Python code that appears when using eval().

There is a class (the actual code here):

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()

The class name does not matter, because the reflection of the functions is via the variable __mql__. Also, the pyEval(..., override_class) function has the argument override_class=true when the variable __mql__ changes.

Example:
PythonDLL_Example.mq5 and PythonDLL_Example.py

You can define the Python code execution environment:

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

Problems and solutions

When testing is required “Allow DLL imports” at the global level.

The search for the necessary python3.dll occurs in the following order:

  1. In the folder with terminal64.exe or metatester64.exe when testing on Agents.
  2. In the folders specified in the variable PATH. Simply change the variable PATH, and at the same time to clean it from a “trash”.

If python3.dll is not found:

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 is not only python3.dll, but also its environment that needs to be configured. The most popular and simple solution is to install Anaconda. There is also Miniconda (minimum 200MB), if you clearly know which packages will be needed when running the script.

Python was created as a separate application, and with embeddability there are problems that are unlikely to ever be fixed:

With the active console, you can notice the output of Py_FatalError() until it disappears:

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

Compile errors and Python code execution errors are not automatically displayed on the active console. But when using the class CPythonDLL, errors are displayed in the terminal log.

There is always only one instance of Python to execute code. If several experts, indicators and scripts simultaneously use this Python wrapper without synchronization in one MetaTrader, then the result is not guaranteed. There is no such problem when testing.

Memory allocation is a long operation, but it doesn’t depend on the amount of memory requested. It will be faster to pre-allocate one megabyte per line and use this buffer several times rather than requesting the required amount of memory each time.

When using the multiprocessing module, you must specify the absolute path to the file in __file__ and sys.argv. An example is here.

Sub-interpreters

The wrapper was created with sub-interpreters, but I had to use a simple GIL. Sub-interpreters are not compatible with the popular libraries for Python. issue37186, issue10915. But you can build this wrapper with PYTHONDLL_SUBINTERPRETERS.

When you run in the terminal several independent experts, indicators and scripts that use this wrapper for each thread gets its own isolated interpreter using Py_NewInterpreter(). But there may be a delay in switching the thread, because Python does not have full multi-threaded execution, but a global interpreter lock (GIL) that blocks other threads when executing Python code.

When testing, there is always only one interpreter.

For developers

Official article on creating a DLL.

Addition to the article:

The int type in Python is infinite:

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

Types in MQL:

long = 8 bytes (64 bits) = -9 223 372 036 854 775 808 ... 9 223 372 036 854 775 807
ulong = 8 bytes (64 bits) = 0 ... 18 446 744 073 709 551 615

CrashDumps:

%LOCALAPPDATA%\CrashDumps\

On all questions to address in this topic.