PythonMetaTrader5 1.0.8__py3-none-any.whl → 2.0__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.
@@ -1,57 +1,77 @@
1
- # internal order send
2
1
  import time
3
2
  from MetaTrader5 import *
4
- import colorama
3
+ from loguru import logger
4
+ import pyautogui
5
5
 
6
- colorama.init()
7
-
8
- def SpacerStart():
9
- print(colorama.Style.RESET_ALL + "//////////////////////////////")
10
-
11
- def SpacerEnd():
12
- print(colorama.Style.RESET_ALL + "//////////////////////////////")
13
- print()
14
6
 
15
7
  class Broker:
16
- RetCodes = {10027: "Enable Algo Trading in MetaTrader5 app", 10018: "Market closed", 10016: "Wrong SL"}
8
+ RetCodes = {
9
+ 10027: "Enable Algo Trading in MetaTrader5 app",
10
+ 10018: "Market closed",
11
+ 10016: "Wrong SL",
12
+ }
13
+
17
14
  log = None
18
15
  password = None
19
16
  server = None
17
+
20
18
  def __init__(self, log, password, server):
19
+ self.log = log
20
+ self.password = str(password)
21
+ self.server = str(server)
22
+
21
23
  initialize()
22
- if login(log, str(password), server=str(server)):
23
- print(colorama.Fore.GREEN + "Login succes")
24
+ if login(self.log, self.password, server=self.server):
25
+ logger.success("Login succes")
24
26
  acc_info = account_info()
25
- print(colorama.Style.RESET_ALL + str(acc_info))
26
- self.log = log
27
- self.password = str(password)
28
- self.server = str(server)
29
-
27
+ logger.info(str(acc_info))
30
28
  else:
31
- print(colorama.Fore.RED + "Login failed: ", str(self.log))
29
+ logger.error(f"Login failed: {self.log}")
32
30
 
33
- def login(self, ToEffect = False):
31
+ def login(self, ToEffect=False):
34
32
  while True:
35
- print(colorama.Fore.YELLOW + "Login on: ", str(self.log))
36
- print(self.log)
33
+ logger.info(f"Login on: {self.log}")
37
34
  if login(self.log, self.password, server=self.server):
38
- print(colorama.Fore.GREEN + "OK")
35
+ logger.success("OK")
39
36
  time.sleep(0.5)
40
37
  return True
41
- if ToEffect==False:
42
- print(colorama.Fore.RED + "ERROR")
38
+ if ToEffect is False:
39
+ logger.error("ERROR")
43
40
  return False
44
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 --------------------
45
65
 
46
66
  def _RawOrderClose(self, order_type, symbol, volume, price, comment=None, ticket=None):
47
67
  order = {
48
68
  "action": TRADE_ACTION_DEAL,
49
69
  "symbol": symbol,
50
- "volume": volume,
70
+ "volume": float(volume),
51
71
  "type": order_type,
52
- "price": price,
72
+ "price": float(price),
53
73
  "deviation": 10,
54
- "type_filling": ORDER_FILLING_IOC
74
+ "type_filling": ORDER_FILLING_IOC,
55
75
  }
56
76
  if comment is not None:
57
77
  order["comment"] = comment
@@ -59,9 +79,17 @@ class Broker:
59
79
  order["position"] = ticket
60
80
  return order_send(order)
61
81
 
62
-
63
- 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):
64
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
+
65
93
  order = {
66
94
  "action": TRADE_ACTION_DEAL,
67
95
  "symbol": symbol,
@@ -69,362 +97,439 @@ class Broker:
69
97
  "type": order_type,
70
98
  "price": price,
71
99
  "deviation": 10,
72
- "type_filling": ORDER_FILLING_IOC
100
+ "type_filling": ORDER_FILLING_FOK,
73
101
  }
74
102
  if comment is not None:
75
103
  order["comment"] = comment
76
104
  if ticket is not None:
77
105
  order["position"] = ticket
78
-
79
106
  if sl is not None:
80
- sl = float(sl)
81
- order["sl"] = sl
82
-
107
+ order["sl"] = float(sl)
108
+ if tp is not None:
109
+ order["tp"] = float(tp)
83
110
  return order_send(order)
84
111
 
85
-
86
112
  def _RawOrderLimit(self, order_type, symbol, volume, price, limit, comment=None, ticket=None):
87
- limit = float(limit)
88
- volume = float(volume)
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
+
89
118
  order = {
90
119
  "action": TRADE_ACTION_PENDING,
91
120
  "symbol": symbol,
92
- "volume": volume,
121
+ "volume": float(volume),
93
122
  "type": order_type,
94
- "price": limit,
123
+ "price": float(limit),
95
124
  "deviation": 10,
96
- "type_filling": ORDER_FILLING_IOC
125
+ "type_filling": ORDER_FILLING_IOC,
97
126
  }
98
127
  if comment is not None:
99
128
  order["comment"] = comment
100
129
  if ticket is not None:
101
130
  order["position"] = ticket
102
-
103
131
  return order_send(order)
104
132
 
105
- # Close all specific orders
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
+
106
354
  def Close(self, symbol, *, comment=None, ticket=None):
107
355
  close = False
108
356
  if ticket is not None:
109
357
  positions = positions_get(ticket=ticket)
110
358
  else:
111
359
  positions = positions_get(symbol=symbol)
112
- tried = 0
113
- done = 0
360
+
114
361
  lenpositions = len(positions)
115
362
  if positions:
116
363
  while True:
117
364
  for pos in positions:
118
- # process only simple buy, sell, without limit or stop
119
365
  if pos.type == ORDER_TYPE_BUY or pos.type == ORDER_TYPE_SELL:
120
- tried += 1
121
- for tries in range(10):
366
+ for _ in range(10):
122
367
  info = symbol_info_tick(symbol)
123
368
  if info is None:
124
369
  return None
125
370
  if pos.type == ORDER_TYPE_BUY:
126
- r = self._RawOrderClose(ORDER_TYPE_SELL, symbol, pos.volume, info.bid, comment, pos.ticket)
371
+ r = self._RawOrderClose(
372
+ ORDER_TYPE_SELL, symbol, pos.volume, info.bid, comment, pos.ticket
373
+ )
127
374
  close = True
128
375
  else:
129
- r = self._RawOrderClose(ORDER_TYPE_BUY, symbol, pos.volume, info.ask, comment, pos.ticket)
376
+ r = self._RawOrderClose(
377
+ ORDER_TYPE_BUY, symbol, pos.volume, info.ask, comment, pos.ticket
378
+ )
130
379
  close = True
131
- # check results
132
380
  if r is None:
133
381
  return None
134
382
  if r.retcode != TRADE_RETCODE_REQUOTE and r.retcode != TRADE_RETCODE_PRICE_OFF:
135
383
  if r.retcode == TRADE_RETCODE_DONE:
136
- SpacerStart()
137
- print(colorama.Fore.BLUE + str(self.log), ": closing all ", symbol)
138
- SpacerEnd()
139
- done += 1
384
+ logger.info(f"{self.log}: closing all {symbol}")
140
385
  lenpositions -= 1
141
386
  break
142
387
  if r.retcode in self.RetCodes:
143
- print(colorama.Fore.RED + "ERROR CAN NOT CLOSE", str(self.log), symbol)
144
- print(self.RetCodes[r.retcode])
145
- print(colorama.Style.RESET_ALL)
388
+ logger.error(f"ERROR CAN NOT CLOSE {self.log} {symbol}")
389
+ logger.error(self.RetCodes[r.retcode])
146
390
  time.sleep(0.5)
147
- if not(lenpositions) or r.retcode == 10027:
391
+ if not lenpositions:
148
392
  break
393
+ if r.retcode == 10027:
394
+ logger.info(f"Enabling algotrading {self.log} {symbol}")
395
+ time.sleep(1)
396
+ pyautogui.hotkey("ctrl", "e")
149
397
 
150
398
  if not close:
151
- SpacerStart()
152
- print(str(self.log), ": there is no orders to close")
153
- SpacerEnd()
399
+ logger.info(f"{self.log}: there is no orders to close")
154
400
  return False
155
401
 
156
-
157
- # Buy order
158
- def Buy(self, symbol, volume, price=None, *, comment=None, ticket=None):
159
- # with direct call
160
- if price is not None:
161
- return self._RawOrder(ORDER_TYPE_BUY, symbol, volume, price, comment, ticket)
162
- # no price, we try several times with current price
163
- while True:
164
- info = symbol_info_tick(symbol)
165
- r = r = self._RawOrder(ORDER_TYPE_BUY, symbol, volume, info.ask, comment, ticket)
166
- if r.retcode == 10009:
167
- SpacerStart()
168
- print(colorama.Fore.GREEN + str(self.log), ": long ", symbol)
169
- SpacerEnd()
170
- break
171
-
172
- else:
173
- if r.retcode in self.RetCodes:
174
- print(colorama.Fore.RED + "ERROR CAN NOT OPEN", str(self.log), ": long ", symbol)
175
- print(self.RetCodes[r.retcode])
176
- print(colorama.Style.RESET_ALL)
177
- if r.retcode == 10027:
178
- break
179
- time.sleep(0.5)
180
-
181
-
182
- else:
183
- print(colorama.Fore.RED + "ERROR CAN NOT OPEN TRYING AGAIN", str(self.log), ": long ", symbol)
184
- print("RetCode: ", r.retcode)
185
- print("Comment: ", r.comment)
186
- print(colorama.Style.RESET_ALL)
187
- time.sleep(0.5)
188
-
189
-
190
- def BuySL(self, symbol, volume, sl, price=None, *, comment=None, ticket=None):
191
- # with direct call
192
- if price is not None:
193
- return self._RawOrder(ORDER_TYPE_BUY, symbol, volume, price, sl, comment, ticket)
194
- # no price, we try several times with current price
195
- while True:
196
- info = symbol_info_tick(symbol)
197
- r = self._RawOrder(ORDER_TYPE_BUY, symbol, volume, info.ask, sl, comment, ticket)
198
- if r.retcode == 10009:
199
- SpacerStart()
200
- print(colorama.Fore.GREEN + str(self.log), ": long ", symbol, "sl: ", sl)
201
- SpacerEnd()
202
- break
203
-
204
- else:
205
- if r.retcode in self.RetCodes:
206
- print(colorama.Fore.RED + "ERROR CAN NOT OPEN", str(self.log), ": long ", symbol)
207
- print(self.RetCodes[r.retcode])
208
- print(colorama.Style.RESET_ALL)
209
- if r.retcode == 10027 or r.retcode == 10016:
210
- break
211
- time.sleep(0.5)
212
-
213
-
214
- else:
215
- print(colorama.Fore.RED + "ERROR CAN NOT OPEN TRYING AGAIN", str(self.log), ": long ", symbol)
216
- print("RetCode: ", r.retcode)
217
- print("Comment: ", r.comment)
218
- print(colorama.Style.RESET_ALL)
219
- time.sleep(0.5)
220
- return r
221
-
222
-
223
- # Sell order
224
- def Sell(self, symbol, volume, price=None, *, comment=None, ticket=None):
225
- # with direct call
226
- if price is not None:
227
- return self._RawOrder(ORDER_TYPE_SELL, symbol, volume, price, comment, ticket)
228
- # no price, we try several times with current price
229
- while True:
230
- info = symbol_info_tick(symbol)
231
- r = self._RawOrder(ORDER_TYPE_SELL, symbol, volume, info.bid, comment, ticket)
232
- if r.retcode == 10009:
233
- SpacerStart()
234
- print(colorama.Fore.RED + str(self.log), ": short ", symbol)
235
- SpacerEnd()
236
- break
237
-
238
- else:
239
- if r.retcode in self.RetCodes:
240
- print(colorama.Fore.RED + "ERROR CAN NOT OPEN", str(self.log), " short ", symbol)
241
- print(self.RetCodes[r.retcode])
242
- print(colorama.Style.RESET_ALL)
243
- if r.retcode == 10027:
244
- break
245
- time.sleep(0.5)
246
-
247
-
248
- else:
249
- print(colorama.Fore.RED + "ERROR CAN NOT OPEN TRYING AGAIN", str(self.log), ": short ", symbol)
250
- print("RetCode: ", r.retcode)
251
- print("Comment: ", r.comment)
252
- print(colorama.Style.RESET_ALL)
253
- time.sleep(0.5)
254
-
255
-
256
- def SellSL(self, symbol, volume, sl, price=None, *, comment=None, ticket=None):
257
-
258
- # with direct call
259
- if price is not None:
260
- return self._RawOrder(ORDER_TYPE_SELL, symbol, volume, price, float(sl), comment, ticket)
261
- # no price, we try several times with current price
262
- while True:
263
- info = symbol_info_tick(symbol)
264
- r = self._RawOrder(ORDER_TYPE_SELL, symbol, volume, info.bid, float(sl), comment, ticket)
265
- if r.retcode == 10009:
266
- SpacerStart()
267
- print(colorama.Fore.RED + str(self.log), ": short ", symbol, " sl ", sl)
268
- SpacerEnd()
269
- break
270
-
271
- else:
272
- if r.retcode in self.RetCodes:
273
- print(colorama.Fore.RED + "ERROR CAN NOT OPEN", str(self.log), " short ", symbol, " sl ", sl)
274
- print(self.RetCodes[r.retcode])
275
- print(colorama.Style.RESET_ALL)
276
- if r.retcode == 10027 or r.retcode == 10016:
277
- break
278
- time.sleep(0.5)
279
-
280
-
281
- else:
282
- print(colorama.Fore.RED + "ERROR CAN NOT OPEN TRYING AGAIN", str(self.log), ": short ", symbol, " sl ", sl)
283
- print("RetCode: ", r.retcode)
284
- print("Comment: ", r.comment)
285
- print(colorama.Style.RESET_ALL)
286
- time.sleep(0.5)
287
-
288
- return r
289
-
402
+ # -------------------- Pending orders --------------------
290
403
 
291
404
  def SellLimit(self, symbol, volume, stoplimit, price=None, comment=None, ticket=None):
292
405
  OpenedInstantly = False
293
-
294
406
  if price is not None:
295
- return print("Price have to be none")
296
- # no price, we try several times with current price
407
+ logger.error("Price have to be none")
408
+ return
409
+
297
410
  while True:
298
411
  info = symbol_info_tick(symbol)
299
-
300
- if (float(info.bid) >= float(stoplimit)):
301
- r = self._RawOrder(ORDER_TYPE_SELL, symbol, float(volume), info.bid, comment, ticket)
302
-
412
+ if float(info.bid) >= float(stoplimit):
413
+ r = self._RawOrder(ORDER_TYPE_SELL, symbol, float(volume), info.bid, comment=comment, ticket=ticket)
303
414
  OpenedInstantly = True
304
415
  else:
305
- r = self._RawOrderLimit(ORDER_TYPE_SELL_LIMIT, symbol, float(volume), info.bid, float(stoplimit),
306
- comment,
307
- ticket)
416
+ r = self._RawOrderLimit(
417
+ ORDER_TYPE_SELL_LIMIT, symbol, float(volume), info.bid, float(stoplimit), comment, ticket
418
+ )
308
419
  OpenedInstantly = False
309
420
 
310
- if r.retcode == 10009:
421
+ if r.retcode == 10009:
311
422
  if OpenedInstantly:
312
- SpacerStart()
313
- print(colorama.Fore.RED + str(self.log), ": opened short on: ", symbol,
314
- "beacues of ask is > or = limit price")
315
- SpacerEnd()
423
+ logger.success(f"{self.log}: opened short on: {symbol} beacues of ask is > or = limit price")
316
424
  else:
317
- SpacerStart()
318
- print(colorama.Fore.RED + str(self.log), ": opened short limit on: ", symbol, " price: ", stoplimit)
319
- SpacerEnd()
425
+ logger.success(f"{self.log}: opened short limit on: {symbol} price: {stoplimit}")
320
426
  break
321
-
322
427
  else:
323
428
  if r.retcode in self.RetCodes:
324
- print(colorama.Fore.RED + "ERROR CAN NOT OPEN", str(self.log), " sell limit on: ", symbol, " price: ", stoplimit)
325
- print(self.RetCodes[r.retcode])
326
- 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])
327
431
  if r.retcode == 10027:
328
- break
432
+ logger.info(f"Enabling algotrading {self.log} {symbol}")
433
+ time.sleep(1)
434
+ pyautogui.hotkey("ctrl", "e")
329
435
  time.sleep(0.5)
330
-
331
436
  else:
332
- print(colorama.Fore.RED + "ERROR CAN NOT OPEN TRYING AGAIN", str(self.log), " sell limit on: ", symbol, " price: ", stoplimit)
333
- print("RetCode: ", r.retcode)
334
- print("Comment: ", r.comment)
335
- print(colorama.Style.RESET_ALL)
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}")
336
442
  time.sleep(0.5)
337
443
  return r
338
444
 
339
-
340
445
  def BuyLimit(self, symbol, volume, stoplimit, price=None, comment=None, ticket=None):
341
446
  OpenedInstantly = False
342
447
  if price is not None:
343
- return print("Price have to be none")
344
- # no price, we try several times with current price
448
+ logger.error("Price have to be none")
449
+ return
450
+
345
451
  while True:
346
452
  info = symbol_info_tick(symbol)
347
-
348
- if (float(info.ask) <= float(stoplimit)):
349
- 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)
350
455
  OpenedInstantly = True
351
456
  else:
352
- r = self._RawOrderLimit(ORDER_TYPE_BUY_LIMIT, symbol, float(volume), info.ask, float(stoplimit), comment, ticket)
457
+ r = self._RawOrderLimit(
458
+ ORDER_TYPE_BUY_LIMIT, symbol, float(volume), info.ask, float(stoplimit), comment, ticket
459
+ )
353
460
  OpenedInstantly = False
354
461
 
355
- if r.retcode == 10009:
462
+ if r.retcode == 10009:
356
463
  if OpenedInstantly:
357
- SpacerStart()
358
- print(colorama.Fore.GREEN + str(self.log), ": opened long on: ", symbol, "beacues of ask is < or = limit price")
359
- SpacerEnd()
464
+ logger.success(f"{self.log}: opened long on: {symbol} beacues of ask is < or = limit price")
360
465
  else:
361
- SpacerStart()
362
- print(colorama.Fore.GREEN + str(self.log), ": opened long limit on: ", symbol, " price: ", stoplimit)
363
- SpacerEnd()
466
+ logger.success(f"{self.log}: opened long limit on: {symbol} price: {stoplimit}")
364
467
  break
365
-
366
468
  else:
367
469
  if r.retcode in self.RetCodes:
368
- print(colorama.Fore.RED + "ERROR CAN NOT OPEN", str(self.log), " long limit on: ", symbol, " price: ", stoplimit)
369
- print(self.RetCodes[r.retcode])
370
- 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])
371
472
  if r.retcode == 10027:
372
- break
473
+ logger.info(f"Enabling algotrading {self.log} {symbol}")
474
+ time.sleep(1)
475
+ pyautogui.hotkey("ctrl", "e")
373
476
  time.sleep(0.5)
374
-
375
-
376
477
  else:
377
- print(colorama.Fore.RED + "ERROR CAN NOT OPEN TRYING AGAIN", str(self.log), " long limit on: ", symbol, " price: ", stoplimit)
378
- print("RetCode: ", r.retcode)
379
- print("Comment: ", r.comment)
380
- print(colorama.Style.RESET_ALL)
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}")
381
483
  time.sleep(0.5)
382
484
  return r
383
485
 
486
+ # -------------------- Cancel pending orders --------------------
384
487
 
385
488
  def _RawCancelOrder(self, order_number):
386
- # Create the request
387
489
  request = {
388
490
  "action": TRADE_ACTION_REMOVE,
389
491
  "order": order_number,
390
- "comment": "Order Removed"
492
+ "comment": "Order Removed",
391
493
  }
392
- # Send order to MT5
393
- order_result = order_send(request)
394
- return order_result
395
-
494
+ return order_send(request)
396
495
 
397
496
  def CancelOrders(self, symbol):
398
- cancel = False
399
497
  orders = orders_get()
400
- limit_orders = [order for order in orders if
401
- order.type == ORDER_TYPE_BUY_LIMIT or order.type == ORDER_TYPE_SELL_LIMIT]
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
+ ]
402
503
  lenpositions = len(limit_orders)
504
+
403
505
  while True:
506
+ r = None
404
507
  for order in limit_orders:
405
508
  if order.symbol == symbol:
406
- cancel = True
407
509
  r = self._RawCancelOrder(order.ticket)
408
510
 
409
511
  if r.retcode != TRADE_RETCODE_REQUOTE and r.retcode != TRADE_RETCODE_PRICE_OFF:
410
512
  if r.retcode == TRADE_RETCODE_DONE:
411
- SpacerStart()
412
- print(colorama.Fore.BLUE + str(self.log), ": order have been just cancelled on: ", symbol)
413
- SpacerEnd()
513
+ logger.info(f"{self.log}: order have been just cancelled on: {symbol}")
414
514
  lenpositions -= 1
415
515
  break
416
- if r.retcode in self.RetCodes:
417
- print(colorama.Fore.RED + "ERROR CAN NOT CLOSE", str(self.log), symbol)
418
- print(self.RetCodes[r.retcode])
419
- print(colorama.Style.RESET_ALL)
420
- 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)
421
520
 
422
- if not lenpositions or r.retcode == 10027:
521
+ if not lenpositions:
423
522
  break
523
+ if r is not None and r.retcode == 10027:
524
+ logger.info(f"Enabling algotrading {self.log} {symbol}")
525
+ time.sleep(1)
526
+ pyautogui.hotkey("ctrl", "e")
424
527
 
425
528
  if not len(limit_orders):
426
- SpacerStart()
427
- print(str(self.log), ": there is no orders to cancel")
428
- SpacerEnd()
529
+ logger.info(f"{self.log}: there is no orders to cancel")
530
+
531
+ # -------------------- Disconnect --------------------
532
+
429
533
  def Disconnect(self):
430
534
  shutdown()
535
+ logger.info("Disconnected from MT5")
@@ -0,0 +1,25 @@
1
+ Metadata-Version: 2.4
2
+ Name: PythonMetaTrader5
3
+ Version: 2.0
4
+ Summary: Connect MT5 with Python
5
+ Author-email: Wiktor Jelen <wiktorjn@gmail.com>
6
+ License: MIT
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
+ # Python Library for MetaTrader 5 Connection [![PyPI Downloads](https://static.pepy.tech/badge/pythonmetatrader5)](https://pepy.tech/projects/pythonmetatrader5)
19
+ [![Discord](https://img.shields.io/badge/Join_us_on-Discord-5865F2?logo=discord&logoColor=white&style=for-the-badge)](https://discord.gg/BARYa55KS8)
20
+
21
+
22
+ MetaTrader 5
23
+ 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.
24
+
25
+ Soon -> huge update
@@ -0,0 +1,5 @@
1
+ PythonMetaTrader5/__init__.py,sha256=FQYk4BheVnCjEDqIKFw_i5bR6tTOquydzNtxbKkO9F8,21318
2
+ pythonmetatrader5-2.0.dist-info/METADATA,sha256=HPvd0qanr03u89e1dbgxaooqJkMpMtSuSWrtt_jSjHs,1155
3
+ pythonmetatrader5-2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
4
+ pythonmetatrader5-2.0.dist-info/top_level.txt,sha256=r8xFh3BXAc7RVgR1j6wCYfyk_mm_DaDoGUYFk2IqBLE,18
5
+ pythonmetatrader5-2.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.41.2)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,111 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: PythonMetaTrader5
3
- Version: 1.0.8
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=C3juNy9lgYU8oSqGfoJZlzRDOINmavCVCc0GT3X4btE,17118
2
- PythonMetaTrader5-1.0.8.dist-info/METADATA,sha256=bqG717yH7WTYzLJXixGcH79JGDCxPc4VmUN7kgRyVf4,3925
3
- PythonMetaTrader5-1.0.8.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
4
- PythonMetaTrader5-1.0.8.dist-info/top_level.txt,sha256=r8xFh3BXAc7RVgR1j6wCYfyk_mm_DaDoGUYFk2IqBLE,18
5
- PythonMetaTrader5-1.0.8.dist-info/RECORD,,