Skip to content

Commit

Permalink
PythonDLL
Browse files Browse the repository at this point in the history
  • Loading branch information
Roffild committed Mar 18, 2019
1 parent 2d5e816 commit 1c30921
Show file tree
Hide file tree
Showing 23 changed files with 1,454 additions and 2 deletions.
90 changes: 90 additions & 0 deletions Experts/Roffild/Examples/PythonDLL_Example.mq5
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* https://github.com/Roffild/RoffildLibrary
*/
#property copyright "Roffild"
#property link "https://github.com/Roffild/RoffildLibrary"

extern string PythonHome = "";

#include <Roffild/PythonDLL.mqh>
#include <Roffild/ToIndicator.mqh>

#resource "PythonDLL_Example.py" as string _PyCode_
CPythonDLL python;

CToIndicator indicMedian;

int OnInit()
{
if (PythonHome == "") {
Print("ERROR: PythonHome == \"\"");
return INIT_FAILED;
}
if (python.initialize(PythonHome) == false) {
Print("ERROR: Py_NewInterpreter() is not created.");
return INIT_FAILED;
}
const string errinit = python.getErrorText();
if (errinit != "") {
Print(errinit);
return INIT_FAILED;
}
if (python.eval(_PyCode_, true) == false) {
const string errmycode = python.getErrorText();
if (errmycode != "") {
Print(errmycode);
return INIT_FAILED;
}
}
uchar array[];
StringToCharArray("Version_info: ", array);
Print(python.getString(1, "Version: ", array));
Print(python.getString(2, "", array));
Print(python.getString(3, "", array));
Print("Error in Python:");
python.getString(99, "", array);

if (indicMedian.init("indicMedian") == INVALID_HANDLE) {
Print("ERROR: indicMedian.init");
return INIT_FAILED;
}
indicMedian.addPlot(DRAW_COLOR_LINE, "Median");
indicMedian.plotIndexSetInteger(0, PLOT_LINE_WIDTH, 5);
indicMedian.plotIndexSetInteger(0, PLOT_COLOR_INDEXES, 2);
indicMedian.plotIndexSetInteger(0, PLOT_LINE_COLOR, 0, clrAquamarine);
indicMedian.plotIndexSetInteger(0, PLOT_LINE_COLOR, 1, clrPink);

indicMedian.show();
return INIT_SUCCEEDED;
}

void OnTick()
{
MqlRates rt[];
CopyRates(Symbol(), Period(), 0, 2, rt);
ArraySetAsSeries(rt, true);
double pack[4];
pack[0] = rt[0].open;
pack[1] = rt[0].high;
pack[2] = rt[0].low;
pack[3] = rt[0].close;
double result[100];
python.getDouble(0, 0, pack, result);

const double median = result[0];
const double median1 = (rt[1].high + rt[1].low) / 2.0;
indicMedian.buffer(median, 0, (uchar)(median > median1));
indicMedian.flush();
}
40 changes: 40 additions & 0 deletions Experts/Roffild/Examples/PythonDLL_Example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# https://github.com/Roffild/RoffildLibrary
# ==============================================================================

import sys
import os

class PythonDLL_Example():
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:
return [(array[1] + array[2]) / 2.0]

def getString(self, magic: int, value: str, array: bytes) -> str:
if magic == 1:
return value + str(sys.version)
if magic == 2:
return str(array) + " " + str(sys.version_info)
if magic == 3:
return "sys.path:\n" + "\n".join(sys.path) + \
"os.environ[\"PATH\"]:\n" + os.environ["PATH"].replace(";", "\n")
raise Exception("This is not a bug! This is a feature :D")


__mql__ = PythonDLL_Example()
241 changes: 241 additions & 0 deletions Include/Roffild/PythonDLL.mqh
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* https://github.com/Roffild/RoffildLibrary
*/
#include "../../Libraries/Roffild/PythonDLL/public.h"

/**
* Class for PythonDLL.dll
*/
class CPythonDLL
{
protected:
string bufstr;

public:
CPythonDLL()
{
StringInit(bufstr, 1000111);
}

/**
* Attempt to create a Python environment.
  * If python_home is set incorrectly, the terminal will be destroyed.
*
* @param[in] python_home path to the environment of Python.
* @param[in] console show console window.
* @return Py_NewInterpreter() != NULL
*/
bool initialize(const string python_home, const bool console = false)
{
const string packages =
python_home + "/python37.zip;"
+ python_home + "/lib;"
+ python_home + "/lib/site-packages;"
+ python_home + "/DLLs;"
+ python_home + "/;";
const string dlls =
python_home + "/;"
+ python_home + "/Library/bin;" // Anaconda
+ python_home + "/DLLs;";
return pyInitialize(packages, dlls, console);
}

/**
* Check the active environment of Python.
*
* @return status.
*/
bool isInitialized()
{
return pyIsInitialized();
}

/**
* It is not necessary to use this function,
* because it is always called when a test is completed or MetaTrader is closed.
*/
void finalize()
{
pyFinalize();
}

/**
* Compiling and executing code.
*
* @param[in] pycode
* @param[in] override_class when changing the variable __mql__, set to true.
* @return error status.
*/
bool eval(const string pycode, const bool override_class = false)
{
return pyEval(pycode, override_class);
}

/**
* Checks for errors.
* To get an error message, use getErrorText().
*
* @param[in] clear clears the error.
* @return error status.
*/
bool isError(const bool clear = true)
{
return pyIsError(clear);
}

/**
* Checks for errors, gets text and clears the error.
*/
string getErrorText()
{
const int size = pyGetErrorText(bufstr, StringBufferLen(bufstr));
if (size < 0) {
return "Error in getErrorText()";
}
if (size == 0) {
return "";
}
return bufstr;
}

/**
* Calls the __mql__.getLong() function with the data passed.
* The error is automatically displayed in the log.
*
* @param[in] magic
* @param[in] value
* @param[in] inputs
* @param[out] outputs array size will not change.
* @param[in] inputs_size
* @param[in] outputs_size
* @return the size of the array returned from Python or -1 on a runtime error.
*/
int getLong(const long magic, const long value, const long &inputs[], long &outputs[],
const int inputs_size = WHOLE_ARRAY, const int outputs_size = WHOLE_ARRAY)
{
const int size = pyMQL_getLong(
magic, value,
inputs, (inputs_size < 0 ? ArraySize(inputs) : inputs_size),
outputs, (outputs_size < 0 ? ArraySize(outputs) : outputs_size)
);
if (size < 0) {
Print(getErrorText());
}
return size;
}

/**
* Calls the __mql__.getULong() function with the data passed.
* The error is automatically displayed in the log.
*
* @param[in] magic
* @param[in] value
* @param[in] inputs
* @param[out] outputs array size will not change.
* @param[in] inputs_size
* @param[in] outputs_size
* @return the size of the array returned from Python or -1 on a runtime error.
*/
int getULong(const long magic, const ulong value, const ulong &inputs[], ulong &outputs[],
const int inputs_size = WHOLE_ARRAY, const int outputs_size = WHOLE_ARRAY)
{
const int size = pyMQL_getULong(
magic, value,
inputs, (inputs_size < 0 ? ArraySize(inputs) : inputs_size),
outputs, (outputs_size < 0 ? ArraySize(outputs) : outputs_size)
);
if (size < 0) {
Print(getErrorText());
}
return size;
}

/**
* Calls the __mql__.getDouble() function with the data passed.
* The error is automatically displayed in the log.
*
* @param[in] magic
* @param[in] value
* @param[in] inputs
* @param[out] outputs array size will not change.
* @param[in] inputs_size
* @param[in] outputs_size
* @return the size of the array returned from Python or -1 on a runtime error.
*/
int getDouble(const long magic, const double value, const double &inputs[], double &outputs[],
const int inputs_size = WHOLE_ARRAY, const int outputs_size = WHOLE_ARRAY)
{
const int size = pyMQL_getDouble(
magic, value,
inputs, (inputs_size < 0 ? ArraySize(inputs) : inputs_size),
outputs, (outputs_size < 0 ? ArraySize(outputs) : outputs_size)
);
if (size < 0) {
Print(getErrorText());
}
return size;
}

/**
* Calls the __mql__.getString() function with the data passed.
* The error is automatically displayed in the log.
*
* @param[in] magic
* @param[in] value
* @param[in] inputs
* @param[in] inputs_size
* @return the string returned from Python or "" on a runtime error.
*/
string getString(const long magic, const string value, const uchar &inputs[],
const int inputs_size = WHOLE_ARRAY)
{
const int size = pyMQL_getString(
magic, value,
inputs, (inputs_size < 0 ? ArraySize(inputs) : inputs_size),
bufstr, StringBufferLen(bufstr)
);
if (size < 0) {
Print(getErrorText());
return "";
}
return bufstr;
}

/**
* Calls the __mql__.getString() function with the data passed.
* The error is automatically displayed in the log.
*
* @param[in] magic
* @param[in] value
* @param[in] inputs
* @param[out] buffer
* @param[in] inputs_size
* @param[in] stringBufferLen use StringBufferLen(buffer)
* @return the size of the array returned from Python or -1 on a runtime error.
*/
int getString(const long magic, const string value, const uchar &inputs[], string &buffer,
const int inputs_size = WHOLE_ARRAY, const int stringBufferLen = WHOLE_ARRAY)
{
const int size = pyMQL_getString(
magic, value,
inputs, (inputs_size < 0 ? ArraySize(inputs) : inputs_size),
buffer, (stringBufferLen < 0 ? StringBufferLen(buffer) : stringBufferLen)
);
if (size < 0) {
Print(getErrorText());
}
return size;
}
};
5 changes: 5 additions & 0 deletions Libraries/Roffild/PythonDLL/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# gitignore
/python_source/
/x64/
/.vs/
*.vcxproj.user
Loading

0 comments on commit 1c30921

Please sign in to comment.