PythonMetaTrader5 1.0.9__py3-none-any.whl → 2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- PythonMetaTrader5/__init__.py +369 -297
- pythonmetatrader5-2.1.dist-info/METADATA +29 -0
- pythonmetatrader5-2.1.dist-info/RECORD +5 -0
- {PythonMetaTrader5-1.0.9.dist-info → pythonmetatrader5-2.1.dist-info}/WHEEL +1 -1
- PythonMetaTrader5-1.0.9.dist-info/METADATA +0 -111
- PythonMetaTrader5-1.0.9.dist-info/RECORD +0 -5
- {PythonMetaTrader5-1.0.9.dist-info → pythonmetatrader5-2.1.dist-info}/top_level.txt +0 -0
PythonMetaTrader5/__init__.py
CHANGED
|
@@ -1,58 +1,77 @@
|
|
|
1
|
-
# internal order send
|
|
2
1
|
import time
|
|
3
2
|
from MetaTrader5 import *
|
|
4
|
-
import
|
|
3
|
+
from loguru import logger
|
|
5
4
|
import pyautogui
|
|
6
5
|
|
|
7
|
-
colorama.init()
|
|
8
|
-
|
|
9
|
-
def SpacerStart():
|
|
10
|
-
print(colorama.Style.RESET_ALL + "//////////////////////////////")
|
|
11
|
-
|
|
12
|
-
def SpacerEnd():
|
|
13
|
-
print(colorama.Style.RESET_ALL + "//////////////////////////////")
|
|
14
|
-
print()
|
|
15
6
|
|
|
16
7
|
class Broker:
|
|
17
|
-
RetCodes = {
|
|
8
|
+
RetCodes = {
|
|
9
|
+
10027: "Enable Algo Trading in MetaTrader5 app",
|
|
10
|
+
10018: "Market closed",
|
|
11
|
+
10016: "Wrong SL",
|
|
12
|
+
}
|
|
13
|
+
|
|
18
14
|
log = None
|
|
19
15
|
password = None
|
|
20
16
|
server = None
|
|
17
|
+
|
|
21
18
|
def __init__(self, log, password, server):
|
|
19
|
+
self.log = log
|
|
20
|
+
self.password = str(password)
|
|
21
|
+
self.server = str(server)
|
|
22
|
+
|
|
22
23
|
initialize()
|
|
23
|
-
if login(log,
|
|
24
|
-
|
|
24
|
+
if login(self.log, self.password, server=self.server):
|
|
25
|
+
logger.success("Login succes")
|
|
25
26
|
acc_info = account_info()
|
|
26
|
-
|
|
27
|
-
self.log = log
|
|
28
|
-
self.password = str(password)
|
|
29
|
-
self.server = str(server)
|
|
30
|
-
|
|
27
|
+
logger.info(str(acc_info))
|
|
31
28
|
else:
|
|
32
|
-
|
|
29
|
+
logger.error(f"Login failed: {self.log}")
|
|
33
30
|
|
|
34
|
-
def login(self, ToEffect
|
|
31
|
+
def login(self, ToEffect=False):
|
|
35
32
|
while True:
|
|
36
|
-
|
|
37
|
-
print(self.log)
|
|
33
|
+
logger.info(f"Login on: {self.log}")
|
|
38
34
|
if login(self.log, self.password, server=self.server):
|
|
39
|
-
|
|
35
|
+
logger.success("OK")
|
|
40
36
|
time.sleep(0.5)
|
|
41
37
|
return True
|
|
42
|
-
if ToEffect
|
|
43
|
-
|
|
38
|
+
if ToEffect is False:
|
|
39
|
+
logger.error("ERROR")
|
|
44
40
|
return False
|
|
45
41
|
|
|
42
|
+
# -------------------- Symbol meta & normalization --------------------
|
|
43
|
+
|
|
44
|
+
def _symbol_meta(self, symbol):
|
|
45
|
+
"""
|
|
46
|
+
Returns (point, digits).
|
|
47
|
+
point -> minimal price step (MT5)
|
|
48
|
+
digits -> number of decimal places
|
|
49
|
+
"""
|
|
50
|
+
info = symbol_info(symbol)
|
|
51
|
+
if info is None:
|
|
52
|
+
logger.error(f"{self.log}: symbol_info({symbol}) returned None")
|
|
53
|
+
return None, None
|
|
54
|
+
point = float(info.point)
|
|
55
|
+
digits = int(info.digits)
|
|
56
|
+
return point, digits
|
|
57
|
+
|
|
58
|
+
def _normalize(self, price, digits):
|
|
59
|
+
if price is None:
|
|
60
|
+
return None
|
|
61
|
+
step = 10 ** (-digits)
|
|
62
|
+
return round(float(price) / step) * step
|
|
63
|
+
|
|
64
|
+
# -------------------- Low-level senders --------------------
|
|
46
65
|
|
|
47
66
|
def _RawOrderClose(self, order_type, symbol, volume, price, comment=None, ticket=None):
|
|
48
67
|
order = {
|
|
49
68
|
"action": TRADE_ACTION_DEAL,
|
|
50
69
|
"symbol": symbol,
|
|
51
|
-
"volume": volume,
|
|
70
|
+
"volume": float(volume),
|
|
52
71
|
"type": order_type,
|
|
53
|
-
"price": price,
|
|
72
|
+
"price": float(price),
|
|
54
73
|
"deviation": 10,
|
|
55
|
-
"type_filling": ORDER_FILLING_IOC
|
|
74
|
+
"type_filling": ORDER_FILLING_IOC,
|
|
56
75
|
}
|
|
57
76
|
if comment is not None:
|
|
58
77
|
order["comment"] = comment
|
|
@@ -60,9 +79,17 @@ class Broker:
|
|
|
60
79
|
order["position"] = ticket
|
|
61
80
|
return order_send(order)
|
|
62
81
|
|
|
63
|
-
|
|
64
|
-
def _RawOrder(self, order_type, symbol, volume, price, sl=None, comment=None, ticket=None):
|
|
82
|
+
def _RawOrder(self, order_type, symbol, volume, price, sl=None, tp=None, comment=None, ticket=None):
|
|
65
83
|
volume = float(volume)
|
|
84
|
+
|
|
85
|
+
# normalize prices to instrument digits before sending
|
|
86
|
+
info = symbol_info(symbol)
|
|
87
|
+
if info:
|
|
88
|
+
digits = int(info.digits)
|
|
89
|
+
price = self._normalize(price, digits)
|
|
90
|
+
sl = self._normalize(sl, digits) if sl is not None else None
|
|
91
|
+
tp = self._normalize(tp, digits) if tp is not None else None
|
|
92
|
+
|
|
66
93
|
order = {
|
|
67
94
|
"action": TRADE_ACTION_DEAL,
|
|
68
95
|
"symbol": symbol,
|
|
@@ -70,394 +97,439 @@ class Broker:
|
|
|
70
97
|
"type": order_type,
|
|
71
98
|
"price": price,
|
|
72
99
|
"deviation": 10,
|
|
73
|
-
"type_filling":
|
|
100
|
+
"type_filling": ORDER_FILLING_FOK,
|
|
74
101
|
}
|
|
75
102
|
if comment is not None:
|
|
76
103
|
order["comment"] = comment
|
|
77
104
|
if ticket is not None:
|
|
78
105
|
order["position"] = ticket
|
|
79
|
-
|
|
80
106
|
if sl is not None:
|
|
81
|
-
sl = float(sl)
|
|
82
|
-
|
|
83
|
-
|
|
107
|
+
order["sl"] = float(sl)
|
|
108
|
+
if tp is not None:
|
|
109
|
+
order["tp"] = float(tp)
|
|
84
110
|
return order_send(order)
|
|
85
111
|
|
|
86
|
-
|
|
87
112
|
def _RawOrderLimit(self, order_type, symbol, volume, price, limit, comment=None, ticket=None):
|
|
88
|
-
|
|
89
|
-
|
|
113
|
+
# MT5 uses 'price' for the pending level; keep signature consistent
|
|
114
|
+
info = symbol_info(symbol)
|
|
115
|
+
digits = int(info.digits) if info else 5
|
|
116
|
+
limit = self._normalize(limit, digits)
|
|
117
|
+
|
|
90
118
|
order = {
|
|
91
119
|
"action": TRADE_ACTION_PENDING,
|
|
92
120
|
"symbol": symbol,
|
|
93
|
-
"volume": volume,
|
|
121
|
+
"volume": float(volume),
|
|
94
122
|
"type": order_type,
|
|
95
|
-
"price": limit,
|
|
123
|
+
"price": float(limit),
|
|
96
124
|
"deviation": 10,
|
|
97
|
-
"type_filling": ORDER_FILLING_IOC
|
|
125
|
+
"type_filling": ORDER_FILLING_IOC,
|
|
98
126
|
}
|
|
99
127
|
if comment is not None:
|
|
100
128
|
order["comment"] = comment
|
|
101
129
|
if ticket is not None:
|
|
102
130
|
order["position"] = ticket
|
|
103
|
-
|
|
104
131
|
return order_send(order)
|
|
105
132
|
|
|
106
|
-
#
|
|
133
|
+
# -------------------- Positions I/O --------------------
|
|
134
|
+
|
|
135
|
+
def _pos_to_dict(self, p):
|
|
136
|
+
"""Serialize MT5 position to a dict (for as_dict=True)."""
|
|
137
|
+
if p is None:
|
|
138
|
+
return None
|
|
139
|
+
return {
|
|
140
|
+
"ticket": p.ticket,
|
|
141
|
+
"symbol": p.symbol,
|
|
142
|
+
"type": p.type, # ORDER_TYPE_BUY / ORDER_TYPE_SELL
|
|
143
|
+
"volume": float(p.volume),
|
|
144
|
+
"price_open": float(p.price_open),
|
|
145
|
+
"sl": float(p.sl) if p.sl else None,
|
|
146
|
+
"tp": float(p.tp) if p.tp else None,
|
|
147
|
+
"price_current": float(p.price_current),
|
|
148
|
+
"profit": float(p.profit),
|
|
149
|
+
"time": int(p.time), # epoch
|
|
150
|
+
"magic": int(p.magic),
|
|
151
|
+
"comment": p.comment,
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
# -------------------- Positions by symbol --------------------
|
|
155
|
+
|
|
156
|
+
def GetPositionsBySymbol(self, symbol: str, as_dict: bool = False):
|
|
157
|
+
"""
|
|
158
|
+
Return all open positions for the given symbol.
|
|
159
|
+
If as_dict=True -> list of dictionaries.
|
|
160
|
+
"""
|
|
161
|
+
try:
|
|
162
|
+
positions = positions_get(symbol=symbol)
|
|
163
|
+
except Exception as e:
|
|
164
|
+
logger.error(f"{self.log}: GetPositionsBySymbol exception: {e}")
|
|
165
|
+
return []
|
|
166
|
+
|
|
167
|
+
positions = list(positions) if positions else []
|
|
168
|
+
if as_dict:
|
|
169
|
+
return [self._pos_to_dict(p) for p in positions]
|
|
170
|
+
return positions
|
|
171
|
+
|
|
172
|
+
def GetPositions(self, symbol=None, *, ticket=None, as_dict: bool = False):
|
|
173
|
+
"""
|
|
174
|
+
Return positions with the same filtering semantics as Close():
|
|
175
|
+
- if ticket is provided -> only that position
|
|
176
|
+
- elif symbol is provided -> all positions for that symbol
|
|
177
|
+
- else -> all open positions
|
|
178
|
+
"""
|
|
179
|
+
try:
|
|
180
|
+
if ticket is not None:
|
|
181
|
+
pos = positions_get(ticket=ticket)
|
|
182
|
+
elif symbol is not None:
|
|
183
|
+
pos = positions_get(symbol=symbol)
|
|
184
|
+
else:
|
|
185
|
+
pos = positions_get()
|
|
186
|
+
except Exception as e:
|
|
187
|
+
logger.error(f"{self.log}: GetPositions exception: {e}")
|
|
188
|
+
return []
|
|
189
|
+
|
|
190
|
+
pos = list(pos) if pos else []
|
|
191
|
+
if as_dict:
|
|
192
|
+
return [self._pos_to_dict(p) for p in pos]
|
|
193
|
+
return pos
|
|
194
|
+
|
|
195
|
+
def GetPosition(self, symbol=None, *, ticket=None, as_dict: bool = False):
|
|
196
|
+
"""
|
|
197
|
+
Return the first matching position or None.
|
|
198
|
+
Convenient when you expect at most one.
|
|
199
|
+
"""
|
|
200
|
+
pos_list = self.GetPositions(symbol=symbol, ticket=ticket, as_dict=False)
|
|
201
|
+
if not pos_list:
|
|
202
|
+
return None if not as_dict else None
|
|
203
|
+
return self._pos_to_dict(pos_list[0]) if as_dict else pos_list[0]
|
|
204
|
+
|
|
205
|
+
# -------------------- SL/TP computation: absolute or offset IN POINTS --------------------
|
|
206
|
+
|
|
207
|
+
def _compute_sl_tp(self, order_type, symbol, sl, tp, sl_type: str, tp_type: str):
|
|
208
|
+
"""
|
|
209
|
+
Return (sl_val, tp_val) as absolute prices.
|
|
210
|
+
- 'absolute' -> values are prices (e.g., 1.12345), only normalized
|
|
211
|
+
- 'offset' -> values are POINTS away from the current price:
|
|
212
|
+
BUY : SL = ask - |sl|*point, TP = ask + |tp|*point
|
|
213
|
+
SELL: SL = bid + |sl|*point, TP = bid - |tp|*point
|
|
214
|
+
"""
|
|
215
|
+
sl_val = float(sl) if sl is not None else None
|
|
216
|
+
tp_val = float(tp) if tp is not None else None
|
|
217
|
+
sl_type = (sl_type or "absolute").lower()
|
|
218
|
+
tp_type = (tp_type or "absolute").lower()
|
|
219
|
+
|
|
220
|
+
info_tick = symbol_info_tick(symbol)
|
|
221
|
+
if info_tick is None:
|
|
222
|
+
logger.error(f"{self.log}: symbol_info_tick({symbol}) returned None (SL/TP compute)")
|
|
223
|
+
return None, None
|
|
224
|
+
|
|
225
|
+
point, digits = self._symbol_meta(symbol)
|
|
226
|
+
if point is None:
|
|
227
|
+
return None, None
|
|
228
|
+
|
|
229
|
+
is_buy = (order_type == ORDER_TYPE_BUY)
|
|
230
|
+
ref_price = info_tick.ask if is_buy else info_tick.bid
|
|
231
|
+
|
|
232
|
+
if sl_val is not None:
|
|
233
|
+
if sl_type == "absolute":
|
|
234
|
+
pass
|
|
235
|
+
elif sl_type == "offset":
|
|
236
|
+
sl_val = ref_price - abs(sl_val) * point if is_buy else ref_price + abs(sl_val) * point
|
|
237
|
+
else:
|
|
238
|
+
logger.error(f"{self.log}: invalid sl_type '{sl_type}' (use 'absolute'|'offset')")
|
|
239
|
+
return None, None
|
|
240
|
+
|
|
241
|
+
if tp_val is not None:
|
|
242
|
+
if tp_type == "absolute":
|
|
243
|
+
pass
|
|
244
|
+
elif tp_type == "offset":
|
|
245
|
+
tp_val = ref_price + abs(tp_val) * point if is_buy else ref_price - abs(tp_val) * point
|
|
246
|
+
else:
|
|
247
|
+
logger.error(f"{self.log}: invalid tp_type '{tp_type}' (use 'absolute'|'offset')")
|
|
248
|
+
return None, None
|
|
249
|
+
|
|
250
|
+
sl_val = self._normalize(sl_val, digits) if sl_val is not None else None
|
|
251
|
+
tp_val = self._normalize(tp_val, digits) if tp_val is not None else None
|
|
252
|
+
return sl_val, tp_val
|
|
253
|
+
|
|
254
|
+
# -------------------- Single market-entry function --------------------
|
|
255
|
+
|
|
256
|
+
def OpenPosition(
|
|
257
|
+
self,
|
|
258
|
+
symbol,
|
|
259
|
+
side,
|
|
260
|
+
volume,
|
|
261
|
+
*,
|
|
262
|
+
sl=None,
|
|
263
|
+
tp=None,
|
|
264
|
+
price=None,
|
|
265
|
+
comment=None,
|
|
266
|
+
ticket=None,
|
|
267
|
+
sl_type: str = "absolute", # 'absolute' (price) or 'offset' (POINTS)
|
|
268
|
+
tp_type: str = "absolute", # 'absolute' (price) or 'offset' (POINTS)
|
|
269
|
+
):
|
|
270
|
+
"""
|
|
271
|
+
Open a market position.
|
|
272
|
+
side: "buy"/"sell" (also accepts "long"/"short")
|
|
273
|
+
SL/TP:
|
|
274
|
+
- sl_type/tp_type == 'absolute': pass absolute prices
|
|
275
|
+
- sl_type/tp_type == 'offset' : pass POINTS from current price
|
|
276
|
+
price: if None -> use current ask/bid and retry on known transient errors
|
|
277
|
+
"""
|
|
278
|
+
side = str(side).lower()
|
|
279
|
+
if side not in ("buy", "sell", "long", "short"):
|
|
280
|
+
logger.error(f"{self.log}: invalid side '{side}' (use 'buy'/'sell')")
|
|
281
|
+
return None
|
|
282
|
+
|
|
283
|
+
order_type = ORDER_TYPE_BUY if side in ("buy", "long") else ORDER_TYPE_SELL
|
|
284
|
+
|
|
285
|
+
sl_val, tp_val = self._compute_sl_tp(order_type, symbol, sl, tp, sl_type, tp_type)
|
|
286
|
+
|
|
287
|
+
# direct-price mode
|
|
288
|
+
if price is not None:
|
|
289
|
+
return self._RawOrder(order_type, symbol, volume, price, sl_val, tp_val, comment, ticket)
|
|
290
|
+
|
|
291
|
+
# current-tick mode with retries
|
|
292
|
+
while True:
|
|
293
|
+
info = symbol_info_tick(symbol)
|
|
294
|
+
if info is None:
|
|
295
|
+
logger.error(f"{self.log}: symbol_info_tick({symbol}) returned None")
|
|
296
|
+
return None
|
|
297
|
+
|
|
298
|
+
curr_price = info.ask if order_type == ORDER_TYPE_BUY else info.bid
|
|
299
|
+
r = self._RawOrder(order_type, symbol, volume, curr_price, sl_val, tp_val, comment, ticket)
|
|
300
|
+
|
|
301
|
+
if r is None:
|
|
302
|
+
logger.error(f"{self.log}: order_send returned None")
|
|
303
|
+
return None
|
|
304
|
+
|
|
305
|
+
if r.retcode == TRADE_RETCODE_DONE or r.retcode == 10009:
|
|
306
|
+
action = "long" if order_type == ORDER_TYPE_BUY else "short"
|
|
307
|
+
details = []
|
|
308
|
+
if sl_val is not None:
|
|
309
|
+
details.append(f"SL {sl_val}")
|
|
310
|
+
if tp_val is not None:
|
|
311
|
+
details.append(f"TP {tp_val}")
|
|
312
|
+
suffix = f" ({', '.join(details)})" if details else ""
|
|
313
|
+
logger.success(f"{self.log}: {action} {symbol}{suffix}")
|
|
314
|
+
return r
|
|
315
|
+
|
|
316
|
+
if r.retcode in self.RetCodes:
|
|
317
|
+
action = "long" if order_type == ORDER_TYPE_BUY else "short"
|
|
318
|
+
logger.error(f"ERROR CAN NOT OPEN {self.log}: {action} {symbol}")
|
|
319
|
+
logger.error(self.RetCodes[r.retcode])
|
|
320
|
+
if r.retcode == 10027:
|
|
321
|
+
logger.info(f"Enabling algotrading {self.log} {symbol}")
|
|
322
|
+
time.sleep(1)
|
|
323
|
+
pyautogui.hotkey("ctrl", "e")
|
|
324
|
+
if r.retcode == 10016:
|
|
325
|
+
return r
|
|
326
|
+
time.sleep(0.5)
|
|
327
|
+
else:
|
|
328
|
+
action = "long" if order_type == ORDER_TYPE_BUY else "short"
|
|
329
|
+
logger.error(f"ERROR CAN NOT OPEN TRYING AGAIN {self.log}: {action} {symbol}")
|
|
330
|
+
logger.info(f"RetCode: {r.retcode}")
|
|
331
|
+
logger.info(f"Comment: {r.comment}")
|
|
332
|
+
time.sleep(0.5)
|
|
333
|
+
|
|
334
|
+
# -------------------- Backward-compat wrappers --------------------
|
|
335
|
+
|
|
336
|
+
def Buy(self, symbol, volume, price=None, *, comment=None, ticket=None):
|
|
337
|
+
return self.OpenPosition(symbol, "buy", volume, price=price, comment=comment, ticket=ticket)
|
|
338
|
+
|
|
339
|
+
def BuySL(self, symbol, volume, sl, price=None, *, comment=None, ticket=None, sl_type: str = "absolute"):
|
|
340
|
+
return self.OpenPosition(
|
|
341
|
+
symbol, "buy", volume, sl=sl, price=price, comment=comment, ticket=ticket, sl_type=sl_type
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
def Sell(self, symbol, volume, price=None, *, comment=None, ticket=None):
|
|
345
|
+
return self.OpenPosition(symbol, "sell", volume, price=price, comment=comment, ticket=ticket)
|
|
346
|
+
|
|
347
|
+
def SellSL(self, symbol, volume, sl, price=None, *, comment=None, ticket=None, sl_type: str = "absolute"):
|
|
348
|
+
return self.OpenPosition(
|
|
349
|
+
symbol, "sell", volume, sl=sl, price=price, comment=comment, ticket=ticket, sl_type=sl_type
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
# -------------------- Closing logic --------------------
|
|
353
|
+
|
|
107
354
|
def Close(self, symbol, *, comment=None, ticket=None):
|
|
108
355
|
close = False
|
|
109
356
|
if ticket is not None:
|
|
110
357
|
positions = positions_get(ticket=ticket)
|
|
111
358
|
else:
|
|
112
359
|
positions = positions_get(symbol=symbol)
|
|
113
|
-
|
|
114
|
-
done = 0
|
|
360
|
+
|
|
115
361
|
lenpositions = len(positions)
|
|
116
362
|
if positions:
|
|
117
363
|
while True:
|
|
118
364
|
for pos in positions:
|
|
119
|
-
# process only simple buy, sell, without limit or stop
|
|
120
365
|
if pos.type == ORDER_TYPE_BUY or pos.type == ORDER_TYPE_SELL:
|
|
121
|
-
|
|
122
|
-
for tries in range(10):
|
|
366
|
+
for _ in range(10):
|
|
123
367
|
info = symbol_info_tick(symbol)
|
|
124
368
|
if info is None:
|
|
125
369
|
return None
|
|
126
370
|
if pos.type == ORDER_TYPE_BUY:
|
|
127
|
-
r = self._RawOrderClose(
|
|
371
|
+
r = self._RawOrderClose(
|
|
372
|
+
ORDER_TYPE_SELL, symbol, pos.volume, info.bid, comment, pos.ticket
|
|
373
|
+
)
|
|
128
374
|
close = True
|
|
129
375
|
else:
|
|
130
|
-
r = self._RawOrderClose(
|
|
376
|
+
r = self._RawOrderClose(
|
|
377
|
+
ORDER_TYPE_BUY, symbol, pos.volume, info.ask, comment, pos.ticket
|
|
378
|
+
)
|
|
131
379
|
close = True
|
|
132
|
-
# check results
|
|
133
380
|
if r is None:
|
|
134
381
|
return None
|
|
135
382
|
if r.retcode != TRADE_RETCODE_REQUOTE and r.retcode != TRADE_RETCODE_PRICE_OFF:
|
|
136
383
|
if r.retcode == TRADE_RETCODE_DONE:
|
|
137
|
-
|
|
138
|
-
print(colorama.Fore.BLUE + str(self.log), ": closing all ", symbol)
|
|
139
|
-
SpacerEnd()
|
|
140
|
-
done += 1
|
|
384
|
+
logger.info(f"{self.log}: closing all {symbol}")
|
|
141
385
|
lenpositions -= 1
|
|
142
386
|
break
|
|
143
387
|
if r.retcode in self.RetCodes:
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
print(colorama.Style.RESET_ALL)
|
|
388
|
+
logger.error(f"ERROR CAN NOT CLOSE {self.log} {symbol}")
|
|
389
|
+
logger.error(self.RetCodes[r.retcode])
|
|
147
390
|
time.sleep(0.5)
|
|
148
|
-
if not
|
|
391
|
+
if not lenpositions:
|
|
149
392
|
break
|
|
150
393
|
if r.retcode == 10027:
|
|
151
|
-
|
|
152
|
-
print(colorama.Style.RESET_ALL)
|
|
394
|
+
logger.info(f"Enabling algotrading {self.log} {symbol}")
|
|
153
395
|
time.sleep(1)
|
|
154
|
-
pyautogui.hotkey(
|
|
396
|
+
pyautogui.hotkey("ctrl", "e")
|
|
155
397
|
|
|
156
398
|
if not close:
|
|
157
|
-
|
|
158
|
-
print(str(self.log), ": there is no orders to close")
|
|
159
|
-
SpacerEnd()
|
|
399
|
+
logger.info(f"{self.log}: there is no orders to close")
|
|
160
400
|
return False
|
|
161
401
|
|
|
162
|
-
|
|
163
|
-
# Buy order
|
|
164
|
-
def Buy(self, symbol, volume, price=None, *, comment=None, ticket=None):
|
|
165
|
-
# with direct call
|
|
166
|
-
if price is not None:
|
|
167
|
-
return self._RawOrder(ORDER_TYPE_BUY, symbol, volume, price, comment, ticket)
|
|
168
|
-
# no price, we try several times with current price
|
|
169
|
-
while True:
|
|
170
|
-
info = symbol_info_tick(symbol)
|
|
171
|
-
r = r = self._RawOrder(ORDER_TYPE_BUY, symbol, volume, info.ask, comment, ticket)
|
|
172
|
-
if r.retcode == 10009:
|
|
173
|
-
SpacerStart()
|
|
174
|
-
print(colorama.Fore.GREEN + str(self.log), ": long ", symbol)
|
|
175
|
-
SpacerEnd()
|
|
176
|
-
break
|
|
177
|
-
|
|
178
|
-
else:
|
|
179
|
-
if r.retcode in self.RetCodes:
|
|
180
|
-
print(colorama.Fore.RED + "ERROR CAN NOT OPEN", str(self.log), ": long ", symbol)
|
|
181
|
-
print(self.RetCodes[r.retcode])
|
|
182
|
-
print(colorama.Style.RESET_ALL)
|
|
183
|
-
if r.retcode == 10027:
|
|
184
|
-
print(colorama.Fore.GREEN + "Enabling algotrading", str(self.log), symbol)
|
|
185
|
-
print(colorama.Style.RESET_ALL)
|
|
186
|
-
time.sleep(1)
|
|
187
|
-
pyautogui.hotkey('ctrl', 'e')
|
|
188
|
-
time.sleep(0.5)
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
else:
|
|
192
|
-
print(colorama.Fore.RED + "ERROR CAN NOT OPEN TRYING AGAIN", str(self.log), ": long ", symbol)
|
|
193
|
-
print("RetCode: ", r.retcode)
|
|
194
|
-
print("Comment: ", r.comment)
|
|
195
|
-
print(colorama.Style.RESET_ALL)
|
|
196
|
-
time.sleep(0.5)
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
def BuySL(self, symbol, volume, sl, price=None, *, comment=None, ticket=None):
|
|
200
|
-
# with direct call
|
|
201
|
-
if price is not None:
|
|
202
|
-
return self._RawOrder(ORDER_TYPE_BUY, symbol, volume, price, sl, comment, ticket)
|
|
203
|
-
# no price, we try several times with current price
|
|
204
|
-
while True:
|
|
205
|
-
info = symbol_info_tick(symbol)
|
|
206
|
-
r = self._RawOrder(ORDER_TYPE_BUY, symbol, volume, info.ask, sl, comment, ticket)
|
|
207
|
-
if r.retcode == 10009:
|
|
208
|
-
SpacerStart()
|
|
209
|
-
print(colorama.Fore.GREEN + str(self.log), ": long ", symbol, "sl: ", sl)
|
|
210
|
-
SpacerEnd()
|
|
211
|
-
break
|
|
212
|
-
|
|
213
|
-
else:
|
|
214
|
-
if r.retcode in self.RetCodes:
|
|
215
|
-
print(colorama.Fore.RED + "ERROR CAN NOT OPEN", str(self.log), ": long ", symbol)
|
|
216
|
-
print(self.RetCodes[r.retcode])
|
|
217
|
-
print(colorama.Style.RESET_ALL)
|
|
218
|
-
if r.retcode == 10016:
|
|
219
|
-
break
|
|
220
|
-
if r.retcode == 10027:
|
|
221
|
-
print(colorama.Fore.GREEN + "Enabling algotrading", str(self.log), symbol)
|
|
222
|
-
print(colorama.Style.RESET_ALL)
|
|
223
|
-
time.sleep(1)
|
|
224
|
-
pyautogui.hotkey('ctrl', 'e')
|
|
225
|
-
time.sleep(0.5)
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
else:
|
|
229
|
-
print(colorama.Fore.RED + "ERROR CAN NOT OPEN TRYING AGAIN", str(self.log), ": long ", symbol)
|
|
230
|
-
print("RetCode: ", r.retcode)
|
|
231
|
-
print("Comment: ", r.comment)
|
|
232
|
-
print(colorama.Style.RESET_ALL)
|
|
233
|
-
time.sleep(0.5)
|
|
234
|
-
return r
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
# Sell order
|
|
238
|
-
def Sell(self, symbol, volume, price=None, *, comment=None, ticket=None):
|
|
239
|
-
# with direct call
|
|
240
|
-
if price is not None:
|
|
241
|
-
return self._RawOrder(ORDER_TYPE_SELL, symbol, volume, price, comment, ticket)
|
|
242
|
-
# no price, we try several times with current price
|
|
243
|
-
while True:
|
|
244
|
-
info = symbol_info_tick(symbol)
|
|
245
|
-
r = self._RawOrder(ORDER_TYPE_SELL, symbol, volume, info.bid, comment, ticket)
|
|
246
|
-
if r.retcode == 10009:
|
|
247
|
-
SpacerStart()
|
|
248
|
-
print(colorama.Fore.RED + str(self.log), ": short ", symbol)
|
|
249
|
-
SpacerEnd()
|
|
250
|
-
break
|
|
251
|
-
|
|
252
|
-
else:
|
|
253
|
-
if r.retcode in self.RetCodes:
|
|
254
|
-
print(colorama.Fore.RED + "ERROR CAN NOT OPEN", str(self.log), " short ", symbol)
|
|
255
|
-
print(self.RetCodes[r.retcode])
|
|
256
|
-
print(colorama.Style.RESET_ALL)
|
|
257
|
-
if r.retcode == 10027:
|
|
258
|
-
print(colorama.Fore.GREEN + "Enabling algotrading", str(self.log), symbol)
|
|
259
|
-
print(colorama.Style.RESET_ALL)
|
|
260
|
-
time.sleep(1)
|
|
261
|
-
pyautogui.hotkey('ctrl', 'e')
|
|
262
|
-
time.sleep(0.5)
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
else:
|
|
266
|
-
print(colorama.Fore.RED + "ERROR CAN NOT OPEN TRYING AGAIN", str(self.log), ": short ", symbol)
|
|
267
|
-
print("RetCode: ", r.retcode)
|
|
268
|
-
print("Comment: ", r.comment)
|
|
269
|
-
print(colorama.Style.RESET_ALL)
|
|
270
|
-
time.sleep(0.5)
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
def SellSL(self, symbol, volume, sl, price=None, *, comment=None, ticket=None):
|
|
274
|
-
|
|
275
|
-
# with direct call
|
|
276
|
-
if price is not None:
|
|
277
|
-
return self._RawOrder(ORDER_TYPE_SELL, symbol, volume, price, float(sl), comment, ticket)
|
|
278
|
-
# no price, we try several times with current price
|
|
279
|
-
while True:
|
|
280
|
-
info = symbol_info_tick(symbol)
|
|
281
|
-
r = self._RawOrder(ORDER_TYPE_SELL, symbol, volume, info.bid, float(sl), comment, ticket)
|
|
282
|
-
if r.retcode == 10009:
|
|
283
|
-
SpacerStart()
|
|
284
|
-
print(colorama.Fore.RED + str(self.log), ": short ", symbol, " sl ", sl)
|
|
285
|
-
SpacerEnd()
|
|
286
|
-
break
|
|
287
|
-
|
|
288
|
-
else:
|
|
289
|
-
if r.retcode in self.RetCodes:
|
|
290
|
-
print(colorama.Fore.RED + "ERROR CAN NOT OPEN", str(self.log), " short ", symbol, " sl ", sl)
|
|
291
|
-
print(self.RetCodes[r.retcode])
|
|
292
|
-
print(colorama.Style.RESET_ALL)
|
|
293
|
-
if r.retcode == 10016:
|
|
294
|
-
break
|
|
295
|
-
if r.retcode == 10027:
|
|
296
|
-
print(colorama.Fore.GREEN + "Enabling algotrading", str(self.log), symbol)
|
|
297
|
-
print(colorama.Style.RESET_ALL)
|
|
298
|
-
time.sleep(1)
|
|
299
|
-
pyautogui.hotkey('ctrl', 'e')
|
|
300
|
-
time.sleep(0.5)
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
else:
|
|
304
|
-
print(colorama.Fore.RED + "ERROR CAN NOT OPEN TRYING AGAIN", str(self.log), ": short ", symbol, " sl ", sl)
|
|
305
|
-
print("RetCode: ", r.retcode)
|
|
306
|
-
print("Comment: ", r.comment)
|
|
307
|
-
print(colorama.Style.RESET_ALL)
|
|
308
|
-
time.sleep(0.5)
|
|
309
|
-
|
|
310
|
-
return r
|
|
311
|
-
|
|
402
|
+
# -------------------- Pending orders --------------------
|
|
312
403
|
|
|
313
404
|
def SellLimit(self, symbol, volume, stoplimit, price=None, comment=None, ticket=None):
|
|
314
405
|
OpenedInstantly = False
|
|
315
|
-
|
|
316
406
|
if price is not None:
|
|
317
|
-
|
|
318
|
-
|
|
407
|
+
logger.error("Price have to be none")
|
|
408
|
+
return
|
|
409
|
+
|
|
319
410
|
while True:
|
|
320
411
|
info = symbol_info_tick(symbol)
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
r = self._RawOrder(ORDER_TYPE_SELL, symbol, float(volume), info.bid, comment, ticket)
|
|
324
|
-
|
|
412
|
+
if float(info.bid) >= float(stoplimit):
|
|
413
|
+
r = self._RawOrder(ORDER_TYPE_SELL, symbol, float(volume), info.bid, comment=comment, ticket=ticket)
|
|
325
414
|
OpenedInstantly = True
|
|
326
415
|
else:
|
|
327
|
-
r = self._RawOrderLimit(
|
|
328
|
-
|
|
329
|
-
|
|
416
|
+
r = self._RawOrderLimit(
|
|
417
|
+
ORDER_TYPE_SELL_LIMIT, symbol, float(volume), info.bid, float(stoplimit), comment, ticket
|
|
418
|
+
)
|
|
330
419
|
OpenedInstantly = False
|
|
331
420
|
|
|
332
|
-
if r.retcode
|
|
421
|
+
if r.retcode == 10009:
|
|
333
422
|
if OpenedInstantly:
|
|
334
|
-
|
|
335
|
-
print(colorama.Fore.RED + str(self.log), ": opened short on: ", symbol,
|
|
336
|
-
"beacues of ask is > or = limit price")
|
|
337
|
-
SpacerEnd()
|
|
423
|
+
logger.success(f"{self.log}: opened short on: {symbol} beacues of ask is > or = limit price")
|
|
338
424
|
else:
|
|
339
|
-
|
|
340
|
-
print(colorama.Fore.RED + str(self.log), ": opened short limit on: ", symbol, " price: ", stoplimit)
|
|
341
|
-
SpacerEnd()
|
|
425
|
+
logger.success(f"{self.log}: opened short limit on: {symbol} price: {stoplimit}")
|
|
342
426
|
break
|
|
343
|
-
|
|
344
427
|
else:
|
|
345
428
|
if r.retcode in self.RetCodes:
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
print(colorama.Style.RESET_ALL)
|
|
429
|
+
logger.error(f"ERROR CAN NOT OPEN {self.log} sell limit on: {symbol} price: {stoplimit}")
|
|
430
|
+
logger.error(self.RetCodes[r.retcode])
|
|
349
431
|
if r.retcode == 10027:
|
|
350
|
-
|
|
351
|
-
print(colorama.Style.RESET_ALL)
|
|
432
|
+
logger.info(f"Enabling algotrading {self.log} {symbol}")
|
|
352
433
|
time.sleep(1)
|
|
353
|
-
pyautogui.hotkey(
|
|
434
|
+
pyautogui.hotkey("ctrl", "e")
|
|
354
435
|
time.sleep(0.5)
|
|
355
|
-
|
|
356
436
|
else:
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
437
|
+
logger.error(
|
|
438
|
+
f"ERROR CAN NOT OPEN TRYING AGAIN {self.log} sell limit on: {symbol} price: {stoplimit}"
|
|
439
|
+
)
|
|
440
|
+
logger.info(f"RetCode: {r.retcode}")
|
|
441
|
+
logger.info(f"Comment: {r.comment}")
|
|
361
442
|
time.sleep(0.5)
|
|
362
443
|
return r
|
|
363
444
|
|
|
364
|
-
|
|
365
445
|
def BuyLimit(self, symbol, volume, stoplimit, price=None, comment=None, ticket=None):
|
|
366
446
|
OpenedInstantly = False
|
|
367
447
|
if price is not None:
|
|
368
|
-
|
|
369
|
-
|
|
448
|
+
logger.error("Price have to be none")
|
|
449
|
+
return
|
|
450
|
+
|
|
370
451
|
while True:
|
|
371
452
|
info = symbol_info_tick(symbol)
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
r = self._RawOrder(ORDER_TYPE_BUY, symbol, float(volume), info.ask, comment, ticket)
|
|
453
|
+
if float(info.ask) <= float(stoplimit):
|
|
454
|
+
r = self._RawOrder(ORDER_TYPE_BUY, symbol, float(volume), info.ask, comment=comment, ticket=ticket)
|
|
375
455
|
OpenedInstantly = True
|
|
376
456
|
else:
|
|
377
|
-
r = self._RawOrderLimit(
|
|
457
|
+
r = self._RawOrderLimit(
|
|
458
|
+
ORDER_TYPE_BUY_LIMIT, symbol, float(volume), info.ask, float(stoplimit), comment, ticket
|
|
459
|
+
)
|
|
378
460
|
OpenedInstantly = False
|
|
379
461
|
|
|
380
|
-
if r.retcode
|
|
462
|
+
if r.retcode == 10009:
|
|
381
463
|
if OpenedInstantly:
|
|
382
|
-
|
|
383
|
-
print(colorama.Fore.GREEN + str(self.log), ": opened long on: ", symbol, "beacues of ask is < or = limit price")
|
|
384
|
-
SpacerEnd()
|
|
464
|
+
logger.success(f"{self.log}: opened long on: {symbol} beacues of ask is < or = limit price")
|
|
385
465
|
else:
|
|
386
|
-
|
|
387
|
-
print(colorama.Fore.GREEN + str(self.log), ": opened long limit on: ", symbol, " price: ", stoplimit)
|
|
388
|
-
SpacerEnd()
|
|
466
|
+
logger.success(f"{self.log}: opened long limit on: {symbol} price: {stoplimit}")
|
|
389
467
|
break
|
|
390
|
-
|
|
391
468
|
else:
|
|
392
469
|
if r.retcode in self.RetCodes:
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
print(colorama.Style.RESET_ALL)
|
|
470
|
+
logger.error(f"ERROR CAN NOT OPEN {self.log} long limit on: {symbol} price: {stoplimit}")
|
|
471
|
+
logger.error(self.RetCodes[r.retcode])
|
|
396
472
|
if r.retcode == 10027:
|
|
397
|
-
|
|
398
|
-
print(colorama.Style.RESET_ALL)
|
|
473
|
+
logger.info(f"Enabling algotrading {self.log} {symbol}")
|
|
399
474
|
time.sleep(1)
|
|
400
|
-
pyautogui.hotkey(
|
|
475
|
+
pyautogui.hotkey("ctrl", "e")
|
|
401
476
|
time.sleep(0.5)
|
|
402
|
-
|
|
403
|
-
|
|
404
477
|
else:
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
478
|
+
logger.error(
|
|
479
|
+
f"ERROR CAN NOT OPEN TRYING AGAIN {self.log} long limit on: {symbol} price: {stoplimit}"
|
|
480
|
+
)
|
|
481
|
+
logger.info(f"RetCode: {r.retcode}")
|
|
482
|
+
logger.info(f"Comment: {r.comment}")
|
|
409
483
|
time.sleep(0.5)
|
|
410
484
|
return r
|
|
411
485
|
|
|
486
|
+
# -------------------- Cancel pending orders --------------------
|
|
412
487
|
|
|
413
488
|
def _RawCancelOrder(self, order_number):
|
|
414
|
-
# Create the request
|
|
415
489
|
request = {
|
|
416
490
|
"action": TRADE_ACTION_REMOVE,
|
|
417
491
|
"order": order_number,
|
|
418
|
-
"comment": "Order Removed"
|
|
492
|
+
"comment": "Order Removed",
|
|
419
493
|
}
|
|
420
|
-
|
|
421
|
-
order_result = order_send(request)
|
|
422
|
-
return order_result
|
|
423
|
-
|
|
494
|
+
return order_send(request)
|
|
424
495
|
|
|
425
496
|
def CancelOrders(self, symbol):
|
|
426
|
-
cancel = False
|
|
427
497
|
orders = orders_get()
|
|
428
|
-
limit_orders = [
|
|
429
|
-
|
|
498
|
+
limit_orders = [
|
|
499
|
+
order
|
|
500
|
+
for order in orders
|
|
501
|
+
if order.type == ORDER_TYPE_BUY_LIMIT or order.type == ORDER_TYPE_SELL_LIMIT
|
|
502
|
+
]
|
|
430
503
|
lenpositions = len(limit_orders)
|
|
504
|
+
|
|
431
505
|
while True:
|
|
506
|
+
r = None
|
|
432
507
|
for order in limit_orders:
|
|
433
508
|
if order.symbol == symbol:
|
|
434
|
-
cancel = True
|
|
435
509
|
r = self._RawCancelOrder(order.ticket)
|
|
436
510
|
|
|
437
511
|
if r.retcode != TRADE_RETCODE_REQUOTE and r.retcode != TRADE_RETCODE_PRICE_OFF:
|
|
438
512
|
if r.retcode == TRADE_RETCODE_DONE:
|
|
439
|
-
|
|
440
|
-
print(colorama.Fore.BLUE + str(self.log), ": order have been just cancelled on: ", symbol)
|
|
441
|
-
SpacerEnd()
|
|
513
|
+
logger.info(f"{self.log}: order have been just cancelled on: {symbol}")
|
|
442
514
|
lenpositions -= 1
|
|
443
515
|
break
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
time.sleep(0.5)
|
|
516
|
+
if r.retcode in self.RetCodes:
|
|
517
|
+
logger.error(f"ERROR CAN NOT CLOSE {self.log} {symbol}")
|
|
518
|
+
logger.error(self.RetCodes[r.retcode])
|
|
519
|
+
time.sleep(0.5)
|
|
449
520
|
|
|
450
521
|
if not lenpositions:
|
|
451
522
|
break
|
|
452
|
-
if r.retcode == 10027:
|
|
453
|
-
|
|
454
|
-
print(colorama.Style.RESET_ALL)
|
|
523
|
+
if r is not None and r.retcode == 10027:
|
|
524
|
+
logger.info(f"Enabling algotrading {self.log} {symbol}")
|
|
455
525
|
time.sleep(1)
|
|
456
|
-
pyautogui.hotkey(
|
|
526
|
+
pyautogui.hotkey("ctrl", "e")
|
|
457
527
|
|
|
458
528
|
if not len(limit_orders):
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
529
|
+
logger.info(f"{self.log}: there is no orders to cancel")
|
|
530
|
+
|
|
531
|
+
# -------------------- Disconnect --------------------
|
|
532
|
+
|
|
462
533
|
def Disconnect(self):
|
|
463
534
|
shutdown()
|
|
535
|
+
logger.info("Disconnected from MT5")
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: PythonMetaTrader5
|
|
3
|
+
Version: 2.1
|
|
4
|
+
Summary: Connect MT5 with Python
|
|
5
|
+
Author-email: Wiktor Jelen <wiktorjn@gmail.com>
|
|
6
|
+
License: CC BY-NC 4.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/Akinzou/MetaTrader5-Python
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/Akinzou/MetaTrader5-Python/issues
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.6
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
Requires-Dist: MetaTrader5
|
|
15
|
+
Requires-Dist: loguru
|
|
16
|
+
Requires-Dist: pyautogui
|
|
17
|
+
|
|
18
|
+
# PythonMetaTrader5 [](https://github.com/Akinzou/MetaTrader5-Python/actions/workflows/python-publish.yml)    [](https://pepy.tech/projects/pythonmetatrader5)
|
|
19
|
+
|
|
20
|
+
[](https://discord.gg/BARYa55KS8)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
MetaTrader 5
|
|
24
|
+
This Python library enables you to connect with the MetaTrader 5 trading platform and interact with its features using Python. It allows executing trading operations, fetching market data, receiving notifications, and more.
|
|
25
|
+
|
|
26
|
+
Soon -> huge update
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
PythonMetaTrader5/__init__.py,sha256=FQYk4BheVnCjEDqIKFw_i5bR6tTOquydzNtxbKkO9F8,21318
|
|
2
|
+
pythonmetatrader5-2.1.dist-info/METADATA,sha256=7lJr9jvAYIfS3NLSsWputOFnqimh-sxgMgmYSGIj3io,1565
|
|
3
|
+
pythonmetatrader5-2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
4
|
+
pythonmetatrader5-2.1.dist-info/top_level.txt,sha256=r8xFh3BXAc7RVgR1j6wCYfyk_mm_DaDoGUYFk2IqBLE,18
|
|
5
|
+
pythonmetatrader5-2.1.dist-info/RECORD,,
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: PythonMetaTrader5
|
|
3
|
-
Version: 1.0.9
|
|
4
|
-
Summary: Connect MT5 with Python
|
|
5
|
-
Home-page: https://github.com/Akinzou/MetaTrader5-Python
|
|
6
|
-
Author: Wiktor Jelen
|
|
7
|
-
Author-email: wiktorjn@gmail.com
|
|
8
|
-
Project-URL: Bug Tracker, https://github.com/Akinzou/MetaTrader5-Python/issues
|
|
9
|
-
Classifier: Programming Language :: Python :: 3
|
|
10
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
-
Classifier: Operating System :: OS Independent
|
|
12
|
-
Requires-Python: >=3.6
|
|
13
|
-
Description-Content-Type: text/markdown
|
|
14
|
-
|
|
15
|
-
# Python Library for MetaTrader 5 Connection
|
|
16
|
-
|
|
17
|
-
![MetaTrader 5]
|
|
18
|
-
This Python library enables you to connect with the MetaTrader 5 trading platform and interact with its features using Python. It allows executing trading operations, fetching market data, receiving notifications, and more.
|
|
19
|
-
|
|
20
|
-
## Requirements
|
|
21
|
-
|
|
22
|
-
To use this library, you need:
|
|
23
|
-
|
|
24
|
-
Python 3.x
|
|
25
|
-
MetaTrader 5 trading platform installed
|
|
26
|
-
MetaTrader 5 Python API (MetaTrader5 module)
|
|
27
|
-
colorama
|
|
28
|
-
|
|
29
|
-
## Installation
|
|
30
|
-
|
|
31
|
-
To install the library, use `pip`:
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
pip install MetaTrader5
|
|
35
|
-
pip install PythonMetaTrader5
|
|
36
|
-
```
|
|
37
|
-
# Getting Started
|
|
38
|
-
|
|
39
|
-
Import the library in your Python script:
|
|
40
|
-
|
|
41
|
-
```
|
|
42
|
-
from MetaTrader5 import *
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
Initialize the MetaTrader 5 library by calling the initialize() function.
|
|
46
|
-
|
|
47
|
-
Create an instance of the Broker class by providing your MetaTrader 5 login credentials and server:
|
|
48
|
-
|
|
49
|
-
```
|
|
50
|
-
broker = Broker(log=your_mt5_login, password="your_mt5_password", server="your_mt5_server")
|
|
51
|
-
```
|
|
52
|
-
### After successful login, you can perform trading operations such as placing orders and managing positions.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
## Available Functions:
|
|
56
|
-
### Placing Orders:
|
|
57
|
-
|
|
58
|
-
``Buy(symbol, volume, price=None, *, comment=None, ticket=None):`` Places a Buy market order or limit order if price is specified.
|
|
59
|
-
|
|
60
|
-
``BuySL(symbol, volume, sl, price=None, *, comment=None, ticket=None):`` Places a Buy market order or limit order with a Stop Loss (SL) price if price is specified.
|
|
61
|
-
|
|
62
|
-
``Sell(symbol, volume, price=None, *, comment=None, ticket=None):`` Places a Sell market order or limit order if price is specified.
|
|
63
|
-
|
|
64
|
-
``SellSL(symbol, volume, sl, price=None, *, comment=None, ticket=None):`` Places a Sell market order or limit order with a Stop Loss (SL) price if price is specified.
|
|
65
|
-
|
|
66
|
-
``BuyLimit(symbol, volume, stoplimit, price=None, comment=None, ticket=None):`` Places a Buy limit order with the specified stoplimit price.
|
|
67
|
-
|
|
68
|
-
``SellLimit(symbol, volume, stoplimit, price=None, comment=None, ticket=None):`` Places a Sell limit order with the specified stoplimit price.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
### Managing Positions:
|
|
72
|
-
``Close(symbol, *, comment=None, ticket=None):`` Closes all positions for the specified symbol or a specific position indicated by ticket.
|
|
73
|
-
|
|
74
|
-
``CancelOrders(symbol):`` Cancels all pending orders for the specified symbol.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
### Miscellaneous:
|
|
78
|
-
``Disconnect():`` Closes the connection to the MetaTrader 5 trading platform.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
## Example Usage:
|
|
82
|
-
```
|
|
83
|
-
from MetaTrader5 import *
|
|
84
|
-
|
|
85
|
-
# Initialize the MetaTrader 5 library
|
|
86
|
-
initialize()
|
|
87
|
-
|
|
88
|
-
# Create a broker instance and log in
|
|
89
|
-
broker = Broker(log=your_mt5_login, password="your_mt5_password", server="your_mt5_server")
|
|
90
|
-
|
|
91
|
-
# Place a Buy market order for 0.1 lot of EURUSD
|
|
92
|
-
broker.Buy("EURUSD", 0.1)
|
|
93
|
-
|
|
94
|
-
# Place a limit order for gold (if price is lower than 1900 it will be execute immediately
|
|
95
|
-
broker.BuyLimit("XAUUSD", 0.01, 1900)
|
|
96
|
-
|
|
97
|
-
# Close all positions for EURUSD
|
|
98
|
-
broker.Close("EURUSD")
|
|
99
|
-
|
|
100
|
-
# Cancel all pending orders for EURUSD
|
|
101
|
-
broker.CancelOrders("EURUSD")
|
|
102
|
-
|
|
103
|
-
# Disconnect from the MetaTrader 5 platform
|
|
104
|
-
broker.Disconnect()
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
## Note:
|
|
109
|
-
It is important to have the MetaTrader 5 trading platform running and logged in with the provided credentials before using this library.
|
|
110
|
-
This library is provided "as is" without any warranty. Use it at your own risk.
|
|
111
|
-
Before using this library in a live trading environment, thoroughly test it on a demo account to ensure its correctness and reliability.
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
PythonMetaTrader5/__init__.py,sha256=HvV8KhWHGUf7yhYxuSfS-tUMJIM_jsTsq_8wVbtNJDY,19015
|
|
2
|
-
PythonMetaTrader5-1.0.9.dist-info/METADATA,sha256=oEbrX8zr7-ETOO9NXwTn8669yl98e5yztGpCOfoSq3Y,3925
|
|
3
|
-
PythonMetaTrader5-1.0.9.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
|
4
|
-
PythonMetaTrader5-1.0.9.dist-info/top_level.txt,sha256=r8xFh3BXAc7RVgR1j6wCYfyk_mm_DaDoGUYFk2IqBLE,18
|
|
5
|
-
PythonMetaTrader5-1.0.9.dist-info/RECORD,,
|
|
File without changes
|