MetaTraderPy
Check-in [535f405d9e]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:First import to fossil.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:535f405d9e3fa327e98cd6c6992420bc363b84f7
User & Date: polymeris 2012-10-03 18:57:41
Context
2012-10-03
23:24
Python 2.6 version backport. Leaf check-in: 970c143bec user: polymeris tags: python26
23:19
Create new branch named "python26" Leaf check-in: 0028a971ea user: polymeris tags: python26
18:57
First import to fossil. Leaf check-in: 535f405d9e user: polymeris tags: trunk
18:20
initial empty check-in check-in: 8fd1325caa user: polymeris tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Added TestPythonAdvisor.mq4.







>
>
>
1
2
3
#define PYTHON_MODULE "test"
#include <python.mqh>

Added include/py27.mqh.























































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
//+------------------------------------------------------------------+
//|                                                         py26.mqh |
//|                                                     Bernd Kreuss |
//|                                             mailto:7ibt@arcor.de |
//|                                                                  |
//|                                                                  |
//|               Python Integration For Metatrader 4                |
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "Bernd Kreuss"
#property link      "mailto:7ibt@arcor.de"

#define STDOUT "__python.out.txt" // filename for stdout and stderr redirect

#import "py27.dll" // dll version 1.3.0.x (for Python 2.6.x)

/**
* Initialize the Python environmant. This will start a new
* thread which will initialize Python and enter a sleep() loop.
* Dont call this directly, call PyInit() instead. (see below)
*/
void PyInitialize();

/**
* Return True if PyInitialize() has already been called
*/
bool PyIsInitialized();

/**
* Decrease the reference counter of a Python object by one and free the
* object if the counter reaches zero. You may only call this for objects
* that you OWN yourself, not for BORROWED references.
*/
void PyDecRef(int p_object);

/**
* Increase the reference counter by one
*/
void PyIncRef(int p_object);

/**
* Execute an arbitrary piece of python code
*/
void PyExecute(string python_source);

/**
* Evaluate a python expression and return your NEW OWN
* reference to the result. For example if foo.bar.baz is a list
* and you want a reference to it so you can directy loop through
* its members then just call PyEvaluate("foo.bar.baz") and use 
* the return value as a handle for the PyLookupList() function.
* You can also use this handle for PyListAppend() because it is
* *not* a copy but rather a reference to the original list.
* 
* After you are done with using the handle returned by PyEvaluate()
* you MUST call PyDecRef() to signal Python that you are now done 
* with this object, this will restore the reference counter to
* the value it had before or free it completely if the object
* was created by the code you evaled (return value of a function 
* or the result of a calculation which python will no longer 
* need after you read it)
*/
int PyEvaluate(string python_source); // new reference, PyDecRef() after using!

/**
* return a BORROWED reference to the __main__ dict
*/
int PyMainDict();

/**
* return a BORROWED reference to the item in the dict
* specified by name.
*/
int PyLookupDict(int p_dict, string name);

/**
* return a BORROWED reference to the item in the list
* specified by index.
*/
int PyLookupList(int p_list, int index);

/**
* return the value of the object as int
* (if it is a numeric object)
*/
int PyGetInt(int ptr_int);

/**
* return the value of the object as a double
* (if it is a numeric object)
*/
double PyGetDouble(int p_double);

/**
* return the value of the object as a string
* (only if it actually is a string object)
*/
string PyGetString(int p_string);

/**
* create a new integer obect. You will OWN a reference, 
* so take care of the reference counter
*/  
int PyNewInt(int value);

/**
* create a new double (actually a python float) object. 
* You will OWN a reference, so take care of the reference counter
*/  
int PyNewDouble(double value);

/**
* create a new string obect. You will OWN a reference, 
* so take care of the reference counter
*/  
int PyNewString(string value);

/**
* append the item to the list. This function will NOT steal
* the reference, it will create its own, so ownership
* will not change, so if it was your OWN and NOT a
* borrowed reference you must decref after you are done
*/
void PyListAppend(int p_list, int p_item);

/**
* return the size of the list object
*/
int PyListSize(int p_list);

#import

/**
* below are some higher level abstractions, here you dont have to
* care about reference counting when using the functions, you will
* not be exposed to handles of python objects
*/


/**
* initialize the Python environment. This should be called
* from your init() function. It is safe to call it a second
* time, subsequent calls will just be ignored.
*/
void PyInit(){
   if (!PyIsInitialized()){
      PyInitialize();
      PyExecute("import os, sys");
      PyExecute("os.chdir(\"" + TerminalPath() + "/experts\")");
      PyExecute("sys.path.insert(0, os.getcwd())");
      
      // redirect stdout and stderr
      PyExecute("__outfile__ = open(\"" + STDOUT + "\", \"w\", 0)");
      PyExecute("sys.stdout = __outfile__");
      PyExecute("sys.stderr = __outfile__");
      PyExecute("print \"Python \" + sys.version");
      PyExecute("print \"output of stdout and stderr:\"");
   }
   PyOutEmpty();
}

/**
* empty the stdout file (where the redirected print output and errors go)
*/
void PyOutEmpty(){
   PyExecute("__outfile__.seek(0, os.SEEK_SET)");
   PyExecute("__outfile__.truncate(0)");
   PyExecute("print \"Python \" + sys.version");
   PyExecute("print \"output of stdout and stderr:\"");
}

/**
* Evaluate a python expression that will evaluate to an integer 
* and return its value
*/
int PyEvalInt(string python_source){
   int p_res = PyEvaluate(python_source);
   int res = PyGetInt(p_res);
   PyDecRef(p_res);
   return(res);
}

/**
* Evaluate a python expression that will evaluate to a double 
* and return its value
*/
double PyEvalDouble(string python_source){
   int p_res = PyEvaluate(python_source);
   double res = PyGetDouble(p_res);
   PyDecRef(p_res);
   return(res);
}

/**
* Evaluate a python expression that will evaluate to a string 
* and return its value
*/
string PyEvalString(string python_source){
   int p_res = PyEvaluate(python_source);
   string res = PyGetString(p_res);
   PyDecRef(p_res);
   return(res);
}

/**
* append the array of int to the python list given by its name.
* the list must already exist. The same could be achieved
* by putting PyExecute() calls with generated python code
* into a loop but this would invoke parser and compiler for
* every new list item, directly accessing the python objects
* like it is done here is far more effective.
*/
int PyListAppendInt(string list_name, int array[]){
   int list,item,len,i;
   list = PyEvaluate(list_name);
   len = ArraySize(array);
   for (i=0; i<len; i++){
      item = PyNewInt(array[i]);
      PyListAppend(list, item);
      PyDecRef(item);
   }
   len = PyListSize(list);
   PyDecRef(list);
   return(len);
}

/**
* append the array of double to the python list given by its name.
* the list must already exist.
*/
int PyListAppendDouble(string list_name, double array[]){
   int list,item,len,i;
   list = PyEvaluate(list_name);
   len = ArraySize(array);
   for (i=0; i<len; i++){
      item = PyNewDouble(array[i]);
      PyListAppend(list, item);
      PyDecRef(item);
   }
   len = PyListSize(list);
   PyDecRef(list);
   return(len);
}

/**
* append the array of string to the python list given by its name.
* the list must already exist.
*/
int PyListAppendString(string list_name, string array[]){
   int list,item,len,i;
   list = PyEvaluate(list_name);
   len = ArraySize(array);
   for (i=0; i<len; i++){
      item = PyNewString(array[i]);
      PyListAppend(list, item);
      PyDecRef(item);
   }
   len = PyListSize(list);
   PyDecRef(list);
   return(len);
}



/* Some words:
 *************


* One Interpreter 
  ===============

  All expert advisors and indicators share the same 
Python interpreter with the same global namespace, so you should 
separate them by encapsulating all in classes and instantiate and 
store them with all their state in variables named after the symbol 
(or maybe even symbol + timeframe).


* Init 
  ====
  
  Put your Python classes into Python modules, the import path 
is <metatrader>\experts, the same folder where your EA's mql code 
is located, so a simple PyExecute("import yourmodule"); in your 
init() will import the file yourmodule.py from this folder. Then 
instantiate an instance of your main class with something like 
PyExecute(Symbol() + Period() + " = yourmodule.yourclass()"); 
This way each instance of your EA can keep track of its own 
Python counterpart by accessing it via this global variable.

  Your init() function may look similar to this:
  
int init(){
   // initialize Python
   PyInit();
   
   // import my module
   PyExecute("import mymodule");
   
   // instantiate some objects 
   PyExecute("myFoo_" + Symbol() + Period() + " = mymodule.Foo()");
   PyExecute("myBar_" + Symbol() + Period() + " = mymodule.Bar()");
   
   return(0);
}


* Deinit 
  ======
  
  Use the deinit() function of the EA or Indicator to destroy 
these instances, be sure to terminate all threads they may have 
started, make sure you can terminate them fast within less than a 
second because Metatrader has a timeout here, wait inside python 
in a tight loop with time.sleep() until they are terminated before 
returning to prevent Metatrader from proceding with its deinit
while your threads are stll not all ended!

  Your deinit() function may look like this:
  
int deinit(){
   // tell my objects they should commit suicide by
   // calling their self destruction method
   PyExecute("myFoo_" + Symbol() + Period() + ".stopAndDestroy()");
   PyExecute("myBar_" + Symbol() + Period() + ".stopAndDestroy()");
   
   return(0);
}


* Global unload hook
  ==================
  
  If the last EA that used Python has been removed the Python 
interpreter itself will be terminated and unloaded. 
  
  You can register cleanup functions (do it per imported module, not 
per instance!) with the atexit module, it will be called after the 
last EA's deinit(), again as above make it wait for all cleaning 
action to be finished before returning, these are the last clock 
cycles that will be spent inside Python because at this time there 
is only one system thread left and if this function returns the 
python interpreter will be frozen and then immediately unloaded.

*/


Added include/python.mqh.

























































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
/// Wrapper around Bernd Kreuss' python integration.
/// Handles common data exchange issues.
/// Read the comments in metatrader.py for more information.

#property copyright "Camilo Polymeris"
#property link      "http://www.udec.cl/~cpolymeris/MetaTraderPy/"

#include <py27.mqh>
#include <WinUser32.mqh>

/// The adivisor's unique python variable name
string adv;
/// Temporary storage for python commands
string py;
/// Temporrary storage for python values.
string pyv;

/// String passed to the Advisor constructor
extern string Constructor_Arguments = "";

int init()
{
    if (!IsDllsAllowed())
    {
        MessageBox("This is a Python Expert Advisor.\n" +
            "You must enable DLL calls in the advisor's property " +
            "window for it to run. Aborting.",
            "Error running Expert Advisor",
            MB_OK | MB_ICONERROR);
        return(0);
    }
    PyInit();
    py = StringConcatenate(
        "import ", PYTHON_MODULE, "\n",
        "reload(", PYTHON_MODULE, ")\n");

    // Use a counter to generate unique ids for each python advisor
    int id = GlobalVariableGet("python_advisor_last_id") + 1;
    GlobalVariableSet("python_advisor_last_id", id);
    // the advisor is stored in a (hopefully) unique python variable, containing the name, symbol,
    // period and the above id
    adv = StringConcatenate("advisor_", PYTHON_MODULE, "_", Symbol(), "_", Period(), "_", id);
    adv = substituteString(adv, ".", '_');

    py = StringConcatenate(py, "print \"Init ", adv,"\"");

    // parameters to the advisor's private _setup method
    string advisorSetupArguments = StringConcatenate(
        "\"", Symbol(), "\",",
        Point, ",", 
        Digits, ",", 
        boolToPy(IsTesting()));
    PyExecute("print \"a\"\n");
    string accountSetupArguments = StringConcatenate(
        "\"", AccountName(), "\", ",
        boolToPy(IsDemo()),
        ",\"", AccountCurrency(), "\",",
        MarketInfo(Symbol(), MODE_LOTSIZE), ",",
        MarketInfo(Symbol(), MODE_MINLOT), ",",
        MarketInfo(Symbol(), MODE_MAXLOT), ",",
        MarketInfo(Symbol(), MODE_LOTSTEP), ",",
        MarketInfo(Symbol(), MODE_STOPLEVEL));
    PyExecute("print \"b\"\n");
    PyExecute(StringConcatenate("print ", accountSetupArguments));

    // initialize and setup the advisor
    py = StringConcatenate(py, "\n",
        adv, " = ", PYTHON_MODULE, ".Advisor(", Constructor_Arguments, ")\n",
        adv, "._setup(", advisorSetupArguments, ")\n",
        adv, "._account._setup(", accountSetupArguments, ")\n");
    PyExecute(py);
    
    return(0);
}

void deinit()
{
    // delete the advisor. See py27.mqh for notes on multithreading
    PyExecute(StringConcatenate("del ", adv));
}

void start()
{
    py = "";
    RefreshRates();
    // inter-tick work includes updating the indicators and other values, as well as handling
    // orders, see below
    updateAccount();
    updateIndicators();
    int time = TimeCurrent();
    // call the advisor's private main loop _tick method. This in turn calls the public tick()
    py = StringConcatenate(
        py, "\n",
        adv, "._tick(",
        Ask, ",", Bid, ",", time, ")");
    PyExecute(py);
    // Handle this tick's orders.
    // the handle... procedures expect py to be clean. Using a global variable is not a very elegant
    // way of doing this, but it is more efficient, and works.
    py = "";
    PyExecute("print 'h'");
    handleTickCloseOrders();
    PyExecute("print 'i'");
    handleTickOpenOrders();
    PyExecute("print 'j'");
    PyExecute(StringConcatenate("print \"\"\"", py, "\"\"\""));
    PyExecute(py);
    PyExecute("print 'k'");
}

/// Helper function. Substitute single chars of 'from' in 'text' with the char 'to'.
string substituteString(string text, string from, int to)
{
    string ret = text;
    int ix = 0;
    while (TRUE)
    {
        ix = StringFind(ret, from, 0);
        if (ix < 0)
        {
            return (ret);
        }
        ret = StringSetChar(ret, ix, to);
    }
}

/// Helper function. Construct a python call from an indicator variable name (istr) and a index into
/// it's _args array (See metatrader.Indicator). Then evaluate to an MQL integer.
int iArg(string istr, string astr)
{
    pyv = StringConcatenate(istr, "._args[\"", astr, "\"]");
    return (PyEvalInt(pyv));
}

/// Helper function. Construct a python call from an indicator variable name (istr) and a index into
/// it's _args array (See metatrader.Indicator). Then evaluate to an MQL double.
int dArg(string istr, string astr)
{
    pyv = StringConcatenate(istr, "._args[\"", astr, "\"]");
    return (PyEvalDouble(pyv));
}

string boolToPy(bool b)
{
    if (b) return ("True");
    return ("False");
}

void updateAccount()
{
    string accountUpdateArgs = StringConcatenate(
        AccountBalance(), ",",
        AccountCredit(), ",",
        AccountEquity(), ",",
        AccountMargin(), ",",
        AccountFreeMargin(), ",",
        AccountLeverage());
    py = StringConcatenate(
        py, "\n",
        adv, "._account._update(", accountUpdateArgs, ")");
}

void updateIndicators()
{
    pyv = StringConcatenate("len(", adv, "._watchedIndicators)");
    int len = PyEvalInt(pyv);
    for (int i = len - 1; i >= 0; i--)
    {
        double value = getIndicatorValue(StringConcatenate(adv, "._watchedIndicators[", i, "]"));
        py = StringConcatenate(
            py, "\n",
            adv, "._watchedIndicators[", i, "]",
            "._update(", value, ")");
    }
}

/// Return the the values supplied by MT for the indicator pointed to by the python variable named
/// in istr.
double getIndicatorValue(string istr)
{
    pyv = StringConcatenate(istr, "._indicator");
    int indicator = PyEvalInt(pyv);
    string sym = Symbol();
    int tf = iArg(istr, "timeframe");
    int sh = iArg(istr, "shift");
    // a different call is needed for each built-in indicator. Custom indicators are not supported
    switch (indicator)
    {
        case 0:  return (iAC         (sym, tf, sh));
        case 1:  return (iAD         (sym, tf, sh));
        case 2:  return (iADX        (sym, tf, iArg(istr, "period"), iArg(istr, "applied_price"), iArg(istr, "mode"), sh));
        case 3:  return (iAlligator  (sym, tf, iArg(istr, "jaw_period"), iArg(istr, "jaw_shift"), iArg(istr, "teeth_period"), iArg(istr, "teeth_shift"), iArg(istr, "lips_period"), iArg(istr, "lips_shift"), iArg(istr, "ma_method"), iArg(istr, "applied_price"), iArg(istr, "mode"), sh));
        case 4:  return (iAO         (sym, tf, sh));
        case 5:  return (iATR        (sym, tf, iArg(istr, "period"), sh));
        case 6:  return (iBands      (sym, tf, iArg(istr, "period"), iArg(istr, "deviation"), iArg(istr, "bands_shift"), iArg(istr, "applied_price"), iArg(istr, "mode"), sh));
        case 7:  return (iBearsPower (sym, tf, iArg(istr, "period"), iArg(istr, "applied_price"), sh));
        case 8:  return (iBullsPower (sym, tf, iArg(istr, "period"), iArg(istr, "applied_price"), sh));
        case 9:  return (iBWMFI      (sym, tf, sh));
        case 10: return (iCCI        (sym, tf, iArg(istr, "period"), iArg(istr, "applied_price"), sh));
        case 11: return (iDeMarker   (sym, tf, iArg(istr, "period"), sh));
        case 12: return (iEnvelopes  (sym, tf, iArg(istr, "ma_period"), iArg(istr, "ma_method"), iArg(istr, "ma_shift"), iArg(istr, "applied_price"), dArg(istr, "deviation"), iArg(istr, "mode"), sh));
        case 13: return (iForce      (sym, tf, iArg(istr, "period"), iArg(istr, "ma_method"), iArg(istr, "applied_price"), sh));
        case 14: return (iFractals   (sym, tf, iArg(istr, "mode"), sh));
        case 15: return (iGator      (sym, tf, iArg(istr, "jaw_period"), iArg(istr, "jaw_shift"), iArg(istr, "teeth_period"), iArg(istr, "teeth_shift"), iArg(istr, "lips_period"), iArg(istr, "lips_shift"), iArg(istr, "ma_method"), iArg(istr, "applied_price"), iArg(istr, "mode"), sh));
        case 16: return (iIchimoku   (sym, tf, iArg(istr, "tenkan_sen"), iArg(istr, "kijun_sen"), iArg(istr, "senkou_span_b"), iArg(istr, "mode"), sh));
        case 17: return (iMA         (sym, tf, iArg(istr, "period"), iArg(istr, "ma_shift"), iArg(istr, "ma_method"), iArg(istr, "applied_price"), sh));
        case 18: return (iMACD       (sym, tf, iArg(istr, "fast_ema_period"), iArg(istr, "slow_ema_period"), iArg(istr, "signal_period"), iArg(istr, "applied_price"), iArg(istr, "mode"), sh));
        case 19: return (iMFI        (sym, tf, iArg(istr, "period"), sh));
        case 20: return (iMomentum   (sym, tf, iArg(istr, "period"), iArg(istr, "applied_price"), sh));
        case 21: return (iOBV        (sym, tf, iArg(istr, "applied_price"), sh));
        case 22: return (iOsMA       (sym, tf, iArg(istr, "fast_ema_period"), iArg(istr, "slow_ema_period"), iArg(istr, "signal_period"), iArg(istr, "applied_price"), sh));
        case 23: return (iRSI        (sym, tf, iArg(istr, "period"), iArg(istr, "applied_price"), sh));
        case 24: return (iRVI        (sym, tf, iArg(istr, "period"), iArg(istr, "mode"), sh));
        case 25: return (iSAR        (sym, tf, dArg(istr, "step"), dArg(istr, "maximum"), sh));
        case 26: return (iStdDev     (sym, tf, iArg(istr, "ma_period"), iArg(istr, "ma_shift"), iArg(istr, "ma_method"), iArg(istr, "applied_price"), sh));
        case 27: return (iStochastic (sym, tf, iArg(istr, "k_period"), iArg(istr, "d_period"), iArg(istr, "slowing"), iArg(istr, "method"), iArg(istr, "price_field"), iArg(istr, "mode"), sh));
        case 28: return (iWPR        (sym, tf, iArg(istr, "period"), sh));
        default: return (0.0);
    }
}

void handleTickOpenOrders()
{
    pyv = StringConcatenate("len(", adv, "._pendingOrders)");
    int len = PyEvalInt(pyv);
    for (int i = len - 1; i >= 0; i--)
    {
        int ticket = handleOpenOrder(StringConcatenate(adv, "._pendingOrders[", i, "]"));
        if (ticket >= 0)
            py = StringConcatenate(
                py, "\n",
                adv, "._orderOpened(",
                adv, "._pendingOpenOrders[", i, "], ", ticket, ")"
                );
    }
}

void handleTickCloseOrders()
{
    pyv = StringConcatenate("len(", adv, "._pendingCloseOrders)");
    int len = PyEvalInt(pyv);
    for (int i = len - 1; i >= 0; i--)
        string order = StringConcatenate(adv, "._pendingCloseOrders[", i, "]");
        if (handleCloseOrder(order))
            py = StringConcatenate(
                py, "\n",
                adv, "._orderClosed(",
                adv, "._pendingCloseOrders[", i, "]", ")");
}

int handleOpenOrder(string ostr)
{
    int cmd = PyEvalInt(StringConcatenate(ostr, "._command"));
    double vol = PyEvalDouble(StringConcatenate(ostr, "._volume"));
    double slp = PyEvalDouble(StringConcatenate(ostr, "._slippage"));
    double stl = PyEvalDouble(StringConcatenate(ostr, "._stopLoss"));
    double tkp = PyEvalDouble(StringConcatenate(ostr, "._takeProfit"));
    return (OrderSend(Symbol(), cmd, vol, Ask, slp, stl, tkp));
}

bool handleCloseOrder(string ostr)
{
    int ticket = PyEvalInt(StringConcatenate(ostr, "._ticket"));
    int cmd = PyEvalInt(StringConcatenate(ostr, "._command"));
    double vol = PyEvalDouble(StringConcatenate(ostr, "._volume"));
    double slp = PyEvalDouble(StringConcatenate(ostr, "._slippage"));
    return (OrderClose(ticket, vol, Ask, slp));
}

Added libraries/py27.dll.

cannot compute difference between binary files

Added metatrader/__init__.py.







































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
# -*- coding: utf-8 -*-
""" This module offers a wrapper around Bernd's Python integration for MetaTrader[1]. The intent is that you don't have to ever handle MQL code.
Currently only a subset of functionality is supported, but the most important code to implement Expert Advisors is there.
To use this, apart from Bernd's code, you need the metatrader counterpart script: python.mqh [2]

Then, subclass the Advisor class in your own python module. The new class must be called Advisor, too, e.g. test.Advisor.

This class can then easely be invoked with the following MQL4 script (a demo is included in the package [2]):

#define PYTHON_MODULE "test"
#include <python.mqh>

That's all you should have to ever bother with MQL4. Have fun!

Camilo Polymeris <mailto:cpolymeris@gmail.com>

[1] Bernd's code is available from http://sites.google.com/site/prof7bit/metatrader-python-integration
[2] Download the latest version of this code from http://chiselapp.com/user/polymeris/repository/metatraderpy
"""

from datetime import datetime

VERSION = 1.2

class Advisor:
    """ An expert advisor takes care of trade for one symbol.
    This is the base class to MetaTrader expert advisors written in Python.
    Implement a tick() with no arguments method in your derived classes.
    """
    def __init__(self):
        """ You may want to implement your own __init__ method.
        If so, the arguments passed will be the advisor's symbol,
        point and relevant digits.
        """
        self._openOrders = list()
        self._pendingOrders = list()
        self._pendingCloseOrders = list()
        self._watchedIndicators = list()
        self._account = Account()
    # PUBLIC methods. You may use these in your code.
    def symbol(self):
        """ Returns the advisors symbol as string.
        This information isn't available before the first tick.
        """
        return self._symbol
    def point(self):
        """ Returns the symbols point in currency.
        This information isn't available before the first tick.
        """
        return self._point
    def digits(self):
        """ Relevant digits after the decimal point for this symbol.
        This information isn't available before the first tick.
        """
        return self._digits
    def askPrice(self):
        """ Returns the current tick's ask price.
        This information isn't available before the first tick.
        """
        return self._askPrice
    def bidPrice(self):
        """ Returns the current tick's bid price.
        This information isn't available before the first tick.
        """
        return self._bidPrice
    def orders(self):
        """ Returns a list of open orders.
        Only orders issued by this advisor are included.
        """
        return list(self._openOrders)
    def openOrder(self, order):
        """ Opens an order.
        The order is issued in between this tick() call and the next.
        See Order, BuyOrder and SellOrder classes.
        """
        self._pendingOrders.append(order)
    def closeOrder(self, order):
        """ Closes an open order.
        Alternatively, cancels the order if it hasn't been issued yet.
        """
        try:
            self._pendingOrders.remove(order)
            return
        except:
            pass
        self._pendingCloseOrders.append(order)
    def watchIndicator(self, indicator):
        """ Watch an Indicator. It will be updated between ticks.
        """
        self._watchedIndicators.append(indicator)
    def indicators(self):
        """ Returns a dictionary of watched indicators to their values
        this tick.
        """
        wi = self._watchedIndicators
        return dict([(i, i.value()) for i in wi])
    def unwatchIndicator(self, indicator):
        """ Stop watching this indicator.
        """
        indicator._value = 0.0
        try:
            self._watchedIndicators.remove(indicator)
        except:
            pass
    def isTesting(self):
        """ Returns true if this advisor is being tested.
        This information isn't available before the first tick.
        """
        return self._testing
    def account(self):
        """ Returns an instance with information about the active
        account.
        This information isn't available before the first tick.
        """
        return self._account
    def datetime(self):
        """ Returns the current tick's server time. The time is modelled
        when testing.
        """
        return self._datetime
    def minimumTakeProfit(self):
        return self.askPrice() + self.account().stopLevel() * self.point()
    def maximumStopLoss(self):
        return self.askPrice() - self.account().stopLevel() * self.point()
    # PRIVATE functions. Used internally. Don't call nor reimplement
    # unless you know what you are doing.
    # MT must handle these lists:
    # _openOrders, _pendingOrders, _pendingCloseOrders
    def _setup(self, symbol, point, digits, testing):
        self._symbol = symbol
        self._point = point
        self._digits = digits
        self._testing = testing
    def _tick(self, ask, bid, timeCur):
        self._askPrice = ask
        self._bidPrice = bid
        self._datetime = datetime.fromtimestamp(timeCur)
        self.tick()
    def _ordersOpened(self, orders):
        for o in orders:
            self._orderOpened(o)
    def _orderOpened(self, order, ticket):
        try:
            self._pendingOrders.remove(order)
        except:
            pass
        order._ticket = ticket
        self._openOrders.append(order)
    def _orderClosed(self, order):
        try:
            self._pendingCloseOrders.remove(order)
        except:
            pass
        try:
            self._openOrders.remove(order)
        except:
            pass

class Account:
    """ Information about the currently active account.
    """
    def name(self):
        """ Account's name.
        """
        return self._name
    def balance(self):
        """ Account's current amount of money in the account.
        """
        return self._balance
    def credit(self):
        """ Account's credit value.
        """
        return self._credit
    def isDemo(self):
        """ Returns true if this is a demo account.
        """
        return self._demo
    def currency(self):
        """ Account's currency name.
        """
        return self._currency
    def lotSize(self):
        return self._lotsize
    def minimumVolume(self):
        return self._minlot
    def maximumVolume(self):
        return self._maxlot
    def stopLevel(self):
        return self._stoplevel
    def volumeStep(self):
        return lotstep
    def equity(self):
        """ Account's equity value. This depends on trading server
        settings.
        """
        return self._equity
    def margin(self):
        """ Account's margin value.
        """
        return self._margin
    def freeMargin(self):
        """ Account's free margin value.
        """
        return self._freeMargin
    def leverage(self):
        """ Returns the current leverage of this account.
        """
        return self._leverage
    #PRIVATE
    def _setup(self, name, demo, curr, lotsize, minlot, maxlot, lotstep, stoplevel):
        self._name = name
        self._demo = demo
        self._currency = curr
        self._lotsize = lotsize
        self._minlot = minlot
        self._lotstep = lotstep
        self._maxlot = maxlot
        self._stoplevel = stoplevel
    def _update(self, bal, cred, eq, marg, fMarg, lever):
        self._balance = bal
        self._credit = cred
        self._equity = eq
        self._margin = marg
        self._freeMargin = fMarg
        self._leverage = lever

class Order:
    """ An order to buy or sell that can be issued to the broker.
    And later closed. See Advisor.openOrder() and Advisor.closeOrder()
    methods.
    """
    BUY, SELL = range(2)
    def __init__(self, command, volume, slippage = 2,
            stopLoss = 25, takeProfit = 50):
        """ Create an order.
        The order command parameter may be one of Order.BUY or
        Order.SELL. Volume is in lots. Slippage in points.
        """
        if not stopLoss:
            stopLoss = 0
        if not takeProfit:
            takeProfit = 0
        self._command = command
        self._volume = volume
        self._slippage = slippage
        self._stopLoss = stopLoss
        self._takeProfit = takeProfit
        
class Indicator:
    """ Technical indicators.
    To use them call Advisor.watchIndicator().
    """
    def __init__(self, indicator, timeframe, shift = 0, **kwargs):
        """ Construct an indicator.
        You must specify any parameters using MQL4 parameter names
        as keywords, e.g. 'appied_price' or 'ma_shift'.
        In the case of the Stochastic Indicator, the keywords for the
        %Kperiod and %Dperiod parameters are 'k_period' and 'd_period',
        respectively.
        If the indicator only takes timeframe and shift as parameters,
        you may omit the keywords.
        """
        self._indicator = indicator
        self._args = kwargs
        self._args["timeframe"] = timeframe
        self._args["shift"] = shift
        self._value = float()
    def value(self):
        """ Return the indicator value for this tick.
        """
        return self._value
    # PRIVATE:
    def _update(self, value):
        self._value = value
    # ENUMERATIONS:
    AC, AD, ADX, ALLIGATOR, AO, ATR, \
        BANDS, BEARS_POWER, BULLS_POWER, BWMFI, CCI, \
        DE_MARKER, ENVELOPES, FORCE, FRACTALS, GATOR, ICHIMOKU, \
        MA, MACD, MFI, MOMENTUM, OBV, OSMA, \
        RSI, RVI, SAR, STDDEV, STOCHASTIC, WPR = range(29)
    class Timeframe:
        CHART, M1, M5, M15, M30 = 0, 1, 5, 15, 30
        H1, H4, D1, W1, MN1 = 60, 240, 1440, 10080, 43200
    class MovingAverage:
        SMA, EMA, SMMA, LWMA = range(4)
    class AppliedPrice:
        CLOSE, OPEN, HIGH, LOW, MEDIAN, TYPICAL, WEIGHTED = range(7)
    class Mode:
        MAIN, SIGNAL = 0, 1
        PLUSDI, MINUSDI = 1, 2
        UPPER, LOWER = 1, 2

Added test/__init__.py.













































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
""" A demo module implementing a subclass of metatrader.Advisor.
Call this from MetaTrader using the following code:

#define PYTHON_MODULE "test"
#include <python.mqh>
"""

import metatrader
from metatrader import Indicator
from metatrader import Order

class Advisor(metatrader.Advisor):
    def __init__(self):
        # You don't have to define your own __init__ method,
        # but if you do, remember to call metatrader.Advisor.__init__
        metatrader.Advisor.__init__(self)
        timeframe = Indicator.Timeframe.M1
        # If the indicator only takes timeframe & shift as parameters,
        # you can leave the keywords out.
        ao = Indicator(Indicator.AO, timeframe, 0)
        # Else you have to specify them by name, just as they appear
        # in the MQL4 docs.
        macd = Indicator(\
            Indicator.MACD, \
            timeframe, \
            0, \
            fast_ema_period = 12, \
            slow_ema_period = 26, \
            signal_period = 9, \
            applied_price = Indicator.AppliedPrice.CLOSE,
            mode = Indicator.Mode.MAIN)
        self.watchIndicator(ao)
        self.watchIndicator(macd)
        self.counter = 0
    def tick(self):
        self.counter = self.counter + 1
        # YOUR ADVISOR CODE HERE...
        try:
            bid = self.bidPrice()
            ask = self.askPrice()
            # print messages are output to __python.out.txt
            msg = "Tick, BID = %f, ASK = %f, indicators: " % (bid , ask)
            msg += repr(self.indicators().values())
            print msg
        except:
            print "no tick information."
        print self.counter
        if (self.counter % 10 == 1):
            self.order = Order(Order.BUY, 0.1, 5,
                self.maximumStopLoss(), self.minimumTakeProfit())
            self.openOrder(self.order)
        elif (self.counter % 10 == 6):
            print self.order._ticket
            self.closeOrder(self.order)