Metatrader 5 (MT5) – one of the most popular platforms for trading on financial markets with its own programming language for developing automating trading strategies (mql5).
Python – Includes incredibly powerful tools for machine learning and predictive analytics.
It seems like a link between these two would be a match made in heaven, enabling the use of machine learning in trading strategies. However, this link isn’t as straight forward as I hoped. A common solution is to export data from Metatrader manually and then analyse it python before setting up trading strategies. Unfortunately, this approach prevents the use of metatrader’s best features: automation.
I decided to create a DLL written in C++ to link Metatrader 5 and Python. Although I believe the DLL could be used by a number of programs to link with Python.
Initially I attempted to create the DLL using Boost.python. This strategy worked well, but unfortunately I had some issues compiling the library for 64 bit systems, to which my Metatrader was limited. Therefore, I decided to rather use Python’s C API. An article by arnavguddu on embedding Python in C++ was particularly useful (https://www.codeproject.com/Articles/820116/Embedding-Python-program-in-a-C-Cplusplus-code). I made use of his code for pyhelper.hpp as it made the initialization and finalization of the Python environment much easier.
Next I created my RunPythonCode below to call a python function in a saved file by the name of the file and function (both stored as a character array). Two variables are passed to the python function, namely a 2D Numpy array of up to 20 fields (mql5_arr), and an array of arguments (args).
#include <Python.h> #include "pyhelper.h" #include "arrayobject.h" CPyInstance hInstance; CPyObject pName, pModule, pFunc, pValue, pInputs, pargs, pArray; long RunPythonCode(double *mql5_arr[][20], int array_rows, int array_cols, char FileName[], char FuncName[], int *args[]) { if (PyArray_API == NULL) { import_array(); } pName = PyUnicode_FromString(FileName); pModule = PyImport_Import(pName); if (pModule) { pFunc = PyObject_GetAttrString(pModule, FuncName); if (pFunc && PyCallable_Check(pFunc)) { // Build the 2D array in C++ int SIZE = array_rows; npy_intp dims[2]{ array_rows, array_cols }; const int ND = 2; // Convert it to a NumPy array. pArray = PyArray_SimpleNewFromData(ND, dims, NPY_DOUBLE, reinterpret_cast<void*>(mql5_arr)); if (!pArray) { printf("Error converting to python array.\n"); return -2; } //Add python array to tuple pInputs = PyTuple_New(2); //Number of inputs to function PyTuple_SetItem(pInputs, 0, pArray); //Add value to inputs //Add args to Inputs int foo = sizeof(args) / sizeof(int); npy_intp argsdims[1]{ foo }; pargs = PyArray_SimpleNewFromData(1, argsdims, NPY_INT, reinterpret_cast<void*>(args)); PyTuple_SetItem(pInputs, 1, pargs); //Add value to inputs // Execute function pFunc with variables pInputs pValue = PyObject_CallObject(pFunc, pInputs); long pyResult = PyLong_AsLong(pValue); return pyResult; } else { printf("ERROR: Python function in module\n"); } } else { printf_s("ERROR: Module not imported\n"); return -3; } return 1; }
The next bit of code is the only function of the Dll. The strings for the python file and function name are received as wchar_t* and then converted to char arrays using wcstombs_s.
#include <wchar.h> _DLLAPI long __stdcall CallPython(double *mql5_arr[][20], int array_rows, int array_cols, wchar_t* FileName, wchar_t* FuncName, int *args[]) //First column should be the targets { int n_file = wcslen(FileName) + 1; int n_func = wcslen(FuncName)+1; size_t i_file,i_func; char *CharFileName = new char[n_file]; char *CharFuncName = new char[n_func]; wcstombs_s(&i_file, CharFileName, n_file, FileName, n_file); wcstombs_s(&i_func, CharFuncName, n_func, FuncName, n_func); long Result = RunPythonCode(mql5_arr, array_rows, array_cols, CharFileName, CharFuncName, args); delete(CharFileName); delete(CharFuncName); return Result; }
The Dll can be imported into mql5 using:
#import "D:\\Documents\\My_Projects\\Markets\\PythonDll\\x64\\Release\\PythonDll.dll" long CallPython(double &a[][11], int array_rows, int array_cols, string &FileName, string &FuncName, int &args[]); #import
An example of the use of the Dll in which a function called WriteData is called from a python script called PythonCode.py is:
void CallPython() { Print("Writing data to file..."); double AllInputs[][11]; GetMarketInputs(AllInputs,NumRows); // Add data to matrix int args[] = {1,1}; // Additional args to python if needed string FileString = "PythonCode"; string FuncString = "WriteData"; Print("Result = " ,CallPython(AllInputs,NumRows,NumCols,FileString,FuncString,args)); // NumCols = 11 for this example return(INIT_SUCCEEDED); }
The python script associated with this example is:
def WriteData(a, args): DataFileName = 'D:\Documents\My_Projects\Markets\AllData.csv' print('Python write function called!') file = open(DataFileName, 'w') writer = csv.writer(file,lineterminator = '\n') for row in a: writer.writerow(row) file.close() return 1;
For additional information or to download the files, please see the repository at:
https://github.com/sdswart/pythondll