bbstrader 0.2.92__py3-none-any.whl → 0.2.94__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.

Potentially problematic release.


This version of bbstrader might be problematic. Click here for more details.

Files changed (36) hide show
  1. bbstrader/__ini__.py +20 -20
  2. bbstrader/__main__.py +50 -50
  3. bbstrader/btengine/__init__.py +54 -54
  4. bbstrader/btengine/data.py +11 -9
  5. bbstrader/btengine/scripts.py +157 -157
  6. bbstrader/compat.py +19 -19
  7. bbstrader/config.py +137 -137
  8. bbstrader/core/data.py +22 -22
  9. bbstrader/core/utils.py +146 -146
  10. bbstrader/metatrader/__init__.py +6 -6
  11. bbstrader/metatrader/account.py +1516 -1516
  12. bbstrader/metatrader/copier.py +750 -735
  13. bbstrader/metatrader/rates.py +584 -584
  14. bbstrader/metatrader/risk.py +749 -748
  15. bbstrader/metatrader/scripts.py +81 -81
  16. bbstrader/metatrader/trade.py +1836 -1826
  17. bbstrader/metatrader/utils.py +645 -645
  18. bbstrader/models/__init__.py +10 -10
  19. bbstrader/models/factors.py +312 -312
  20. bbstrader/models/ml.py +1272 -1265
  21. bbstrader/models/optimization.py +182 -182
  22. bbstrader/models/portfolio.py +223 -223
  23. bbstrader/models/risk.py +398 -398
  24. bbstrader/trading/__init__.py +11 -11
  25. bbstrader/trading/execution.py +846 -842
  26. bbstrader/trading/script.py +155 -155
  27. bbstrader/trading/scripts.py +69 -69
  28. bbstrader/trading/strategies.py +860 -860
  29. bbstrader/tseries.py +1842 -1842
  30. {bbstrader-0.2.92.dist-info → bbstrader-0.2.94.dist-info}/LICENSE +21 -21
  31. {bbstrader-0.2.92.dist-info → bbstrader-0.2.94.dist-info}/METADATA +188 -187
  32. bbstrader-0.2.94.dist-info/RECORD +44 -0
  33. {bbstrader-0.2.92.dist-info → bbstrader-0.2.94.dist-info}/WHEEL +1 -1
  34. bbstrader-0.2.92.dist-info/RECORD +0 -44
  35. {bbstrader-0.2.92.dist-info → bbstrader-0.2.94.dist-info}/entry_points.txt +0 -0
  36. {bbstrader-0.2.92.dist-info → bbstrader-0.2.94.dist-info}/top_level.txt +0 -0
@@ -1,645 +1,645 @@
1
- from datetime import datetime
2
- from enum import Enum
3
- from typing import NamedTuple, Optional
4
-
5
- try:
6
- import MetaTrader5 as MT5
7
- except ImportError:
8
- import bbstrader.compat # noqa: F401
9
-
10
-
11
- __all__ = [
12
- "TIMEFRAMES",
13
- "TimeFrame",
14
- "TerminalInfo",
15
- "AccountInfo",
16
- "SymbolInfo",
17
- "TickInfo",
18
- "TradeRequest",
19
- "OrderCheckResult",
20
- "OrderSentResult",
21
- "TradeOrder",
22
- "TradePosition",
23
- "TradeDeal",
24
- "InvalidBroker",
25
- "GenericFail",
26
- "InvalidParams",
27
- "HistoryNotFound",
28
- "InvalidVersion",
29
- "AuthFailed",
30
- "UnsupportedMethod",
31
- "AutoTradingDisabled",
32
- "InternalFailSend",
33
- "InternalFailReceive",
34
- "InternalFailInit",
35
- "InternalFailConnect",
36
- "InternalFailTimeout",
37
- "trade_retcode_message",
38
- "raise_mt5_error",
39
- ]
40
-
41
- # TIMEFRAME is an enumeration with possible chart period values
42
- # See https://www.mql5.com/en/docs/python_metatrader5/mt5copyratesfrom_py#timeframe
43
- TIMEFRAMES = {
44
- "1m": MT5.TIMEFRAME_M1,
45
- "2m": MT5.TIMEFRAME_M2,
46
- "3m": MT5.TIMEFRAME_M3,
47
- "4m": MT5.TIMEFRAME_M4,
48
- "5m": MT5.TIMEFRAME_M5,
49
- "6m": MT5.TIMEFRAME_M6,
50
- "10m": MT5.TIMEFRAME_M10,
51
- "12m": MT5.TIMEFRAME_M12,
52
- "15m": MT5.TIMEFRAME_M15,
53
- "20m": MT5.TIMEFRAME_M20,
54
- "30m": MT5.TIMEFRAME_M30,
55
- "1h": MT5.TIMEFRAME_H1,
56
- "2h": MT5.TIMEFRAME_H2,
57
- "3h": MT5.TIMEFRAME_H3,
58
- "4h": MT5.TIMEFRAME_H4,
59
- "6h": MT5.TIMEFRAME_H6,
60
- "8h": MT5.TIMEFRAME_H8,
61
- "12h": MT5.TIMEFRAME_H12,
62
- "D1": MT5.TIMEFRAME_D1,
63
- "W1": MT5.TIMEFRAME_W1,
64
- "MN1": MT5.TIMEFRAME_MN1,
65
- }
66
-
67
-
68
- class TimeFrame(Enum):
69
- """
70
- Rrepresent a time frame object
71
- """
72
-
73
- M1 = "1m"
74
- M2 = "2m"
75
- M3 = "3m"
76
- M4 = "4m"
77
- M5 = "5m"
78
- M6 = "6m"
79
- M10 = "10m"
80
- M12 = "12m"
81
- M15 = "15m"
82
- M20 = "20m"
83
- M30 = "30m"
84
- H1 = "1h"
85
- H2 = "2h"
86
- H3 = "3h"
87
- H4 = "4h"
88
- H6 = "6h"
89
- H8 = "8h"
90
- H12 = "12h"
91
- D1 = "D1"
92
- W1 = "W1"
93
- MN1 = "MN1"
94
-
95
-
96
- class TerminalInfo(NamedTuple):
97
- """
98
- Represents general information about the trading terminal.
99
- See https://www.mql5.com/en/docs/constants/environment_state/terminalstatus
100
- """
101
-
102
- community_account: bool
103
- community_connection: bool
104
- connected: bool
105
- dlls_allowed: bool
106
- trade_allowed: bool
107
- tradeapi_disabled: bool
108
- email_enabled: bool
109
- ftp_enabled: bool
110
- notifications_enabled: bool
111
- mqid: bool
112
- build: int
113
- maxbars: int
114
- codepage: int
115
- ping_last: int
116
- community_balance: float
117
- retransmission: float
118
- company: str
119
- name: str
120
- language: str
121
- path: str
122
- data_path: str
123
- commondata_path: str
124
-
125
-
126
- class AccountInfo(NamedTuple):
127
- """
128
- Represents information about a trading account.
129
- See https://www.mql5.com/en/docs/constants/environment_state/accountinformation
130
- """
131
-
132
- login: int
133
- trade_mode: int
134
- leverage: int
135
- limit_orders: int
136
- margin_so_mode: int
137
- trade_allowed: bool
138
- trade_expert: bool
139
- margin_mode: int
140
- currency_digits: int
141
- fifo_close: bool
142
- balance: float
143
- credit: float
144
- profit: float
145
- equity: float
146
- margin: float
147
- margin_free: float
148
- margin_level: float
149
- margin_so_call: float
150
- margin_so_so: float
151
- margin_initial: float
152
- margin_maintenance: float
153
- assets: float
154
- liabilities: float
155
- commission_blocked: float
156
- name: str
157
- server: str
158
- currency: str
159
- company: str
160
-
161
-
162
- class SymbolInfo(NamedTuple):
163
- """
164
- Represents detailed information about a financial instrument.
165
- See https://www.mql5.com/en/docs/constants/environment_state/marketinfoconstants
166
- """
167
-
168
- custom: bool
169
- chart_mode: int
170
- select: bool
171
- visible: bool
172
- session_deals: int
173
- session_buy_orders: int
174
- session_sell_orders: int
175
- volume: int
176
- volumehigh: int
177
- volumelow: int
178
- time: datetime
179
- digits: int
180
- spread: int
181
- spread_float: bool
182
- ticks_bookdepth: int
183
- trade_calc_mode: int
184
- trade_mode: int
185
- start_time: int
186
- expiration_time: int
187
- trade_stops_level: int
188
- trade_freeze_level: int
189
- trade_exemode: int
190
- swap_mode: int
191
- swap_rollover3days: int
192
- margin_hedged_use_leg: bool
193
- expiration_mode: int
194
- filling_mode: int
195
- order_mode: int
196
- order_gtc_mode: int
197
- option_mode: int
198
- option_right: int
199
- bid: float
200
- bidhigh: float
201
- bidlow: float
202
- ask: float
203
- askhigh: float
204
- asklow: float
205
- last: float
206
- lasthigh: float
207
- lastlow: float
208
- volume_real: float
209
- volumehigh_real: float
210
- volumelow_real: float
211
- option_strike: float
212
- point: float
213
- trade_tick_value: float
214
- trade_tick_value_profit: float
215
- trade_tick_value_loss: float
216
- trade_tick_size: float
217
- trade_contract_size: float
218
- trade_accrued_interest: float
219
- trade_face_value: float
220
- trade_liquidity_rate: float
221
- volume_min: float
222
- volume_max: float
223
- volume_step: float
224
- volume_limit: float
225
- swap_long: float
226
- swap_short: float
227
- margin_initial: float
228
- margin_maintenance: float
229
- session_volume: float
230
- session_turnover: float
231
- session_interest: float
232
- session_buy_orders_volume: float
233
- session_sell_orders_volume: float
234
- session_open: float
235
- session_close: float
236
- session_aw: float
237
- session_price_settlement: float
238
- session_price_limit_min: float
239
- session_price_limit_max: float
240
- margin_hedged: float
241
- price_change: float
242
- price_volatility: float
243
- price_theoretical: float
244
- price_greeks_delta: float
245
- price_greeks_theta: float
246
- price_greeks_gamma: float
247
- price_greeks_vega: float
248
- price_greeks_rho: float
249
- price_greeks_omega: float
250
- price_sensitivity: float
251
- basis: str
252
- category: str
253
- currency_base: str
254
- currency_profit: str
255
- currency_margin: str
256
- bank: str
257
- description: str
258
- exchange: str
259
- formula: str
260
- isin: str
261
- name: str
262
- page: str
263
- path: str
264
-
265
-
266
- class TickInfo(NamedTuple):
267
- """
268
- Represents the last tick for the specified financial instrument.
269
- * time: Time of the last prices update
270
- * bid: Current Bid price
271
- * ask: Current Ask price
272
- * last: Price of the last deal (Last)
273
- * volume: Volume for the current Last price
274
- * time_msc: Time of a price last update in milliseconds
275
- * flags: Tick flags
276
- * volume_real: Volume for the current Last price with greater accuracy
277
- """
278
-
279
- time: datetime
280
- bid: float
281
- ask: float
282
- last: float
283
- volume: int
284
- time_msc: int
285
- flags: int
286
- volume_real: float
287
-
288
-
289
- class TradeRequest(NamedTuple):
290
- """
291
- Represents a Trade Request Structure
292
- See https://www.mql5.com/en/docs/constants/structures/mqltraderequest
293
- """
294
-
295
- action: int
296
- magic: int
297
- order: int
298
- symbol: str
299
- volume: float
300
- price: float
301
- stoplimit: float
302
- sl: float
303
- tp: float
304
- deviation: int
305
- type: int
306
- type_filling: int
307
- type_time: int
308
- expiration: int
309
- comment: str
310
- position: int
311
- position_by: int
312
-
313
-
314
- class OrderCheckResult(NamedTuple):
315
- """
316
- The Structure of Results of a Trade Request Check
317
- See https://www.mql5.com/en/docs/constants/structures/mqltradecheckresult
318
- """
319
-
320
- retcode: int
321
- balance: float
322
- equity: float
323
- profit: float
324
- margin: float
325
- margin_free: float
326
- margin_level: float
327
- comment: str
328
- request: TradeRequest
329
-
330
-
331
- class OrderSentResult(NamedTuple):
332
- """
333
- The Structure of a Trade Request Result
334
- See https://www.mql5.com/en/docs/constants/structures/mqltraderesult
335
- """
336
-
337
- retcode: int
338
- deal: int
339
- order: int
340
- volume: float
341
- price: float
342
- bid: float
343
- ask: float
344
- comment: str
345
- request_id: int
346
- retcode_external: int
347
- request: TradeRequest
348
-
349
-
350
- class TradeOrder(NamedTuple):
351
- """
352
- Represents a trade order.
353
- See https://www.mql5.com/en/docs/constants/tradingconstants/orderproperties
354
- """
355
-
356
- ticket: int
357
- time_setup: int
358
- time_setup_msc: int
359
- time_done: int
360
- time_done_msc: int
361
- time_expiration: int
362
- type: int
363
- type_time: int
364
- type_filling: int
365
- state: int
366
- magic: int
367
- position_id: int
368
- position_by_id: int
369
- reason: int
370
- volume_initial: float
371
- volume_current: float
372
- price_open: float
373
- sl: float # Stop Loss
374
- tp: float # Take Profit
375
- price_current: float
376
- price_stoplimit: float
377
- symbol: str
378
- comment: str
379
- external_id: str
380
-
381
-
382
- class TradePosition(NamedTuple):
383
- """
384
- Represents a trade position with attributes like ticket, open/close prices,
385
- volume, profit, and other trading details.
386
- See https://www.mql5.com/en/docs/constants/tradingconstants/positionproperties
387
- """
388
-
389
- ticket: int
390
- time: int
391
- time_msc: int
392
- time_update: int
393
- time_update_msc: int
394
- type: int
395
- magic: int
396
- identifier: int
397
- reason: int
398
- volume: float
399
- price_open: float
400
- sl: float # Stop Loss
401
- tp: float # Take Profit
402
- price_current: float
403
- swap: float
404
- profit: float
405
- symbol: str
406
- comment: str
407
- external_id: str
408
-
409
-
410
- class TradeDeal(NamedTuple):
411
- """
412
- Represents a trade deal execution.
413
- See https://www.mql5.com/en/docs/constants/tradingconstants/dealproperties
414
- """
415
-
416
- ticket: int
417
- order: int
418
- time: int
419
- time_msc: int
420
- type: int
421
- entry: int
422
- magic: int
423
- position_id: int
424
- reason: int
425
- volume: float
426
- price: float
427
- commission: float
428
- swap: float
429
- profit: float
430
- fee: float
431
- symbol: str
432
- comment: str
433
- external_id: str
434
-
435
-
436
- class InvalidBroker(Exception):
437
- """Exception raised for invalid broker errors."""
438
-
439
- def __init__(self, message="Invalid broker."):
440
- super().__init__(message)
441
-
442
-
443
- class MT5TerminalError(Exception):
444
- """Base exception class for trading-related errors."""
445
-
446
- def __init__(self, code, message):
447
- super().__init__(message)
448
- self.code = code
449
- self.message = message
450
-
451
- def __str__(self) -> str:
452
- if self.message is None:
453
- return f"{self.__class__.__name__}"
454
- else:
455
- return f"{self.__class__.__name__}, {self.message}"
456
-
457
-
458
- class GenericFail(MT5TerminalError):
459
- """Exception raised for generic failure."""
460
-
461
- def __init__(self, message="Generic fail"):
462
- super().__init__(MT5.RES_E_FAIL, message)
463
-
464
-
465
- class InvalidParams(MT5TerminalError):
466
- """Exception raised for invalid arguments or parameters."""
467
-
468
- def __init__(self, message="Invalid arguments or parameters."):
469
- super().__init__(MT5.RES_E_INVALID_PARAMS, message)
470
-
471
-
472
- class HistoryNotFound(MT5TerminalError):
473
- """Exception raised when no history is found."""
474
-
475
- def __init__(self, message="No history found."):
476
- super().__init__(MT5.RES_E_NOT_FOUND, message)
477
-
478
-
479
- class InvalidVersion(MT5TerminalError):
480
- """Exception raised for an invalid version."""
481
-
482
- def __init__(self, message="Invalid version."):
483
- super().__init__(MT5.RES_E_INVALID_VERSION, message)
484
-
485
-
486
- class AuthFailed(MT5TerminalError):
487
- """Exception raised for authorization failure."""
488
-
489
- def __init__(self, message="Authorization failed."):
490
- super().__init__(MT5.RES_E_AUTH_FAILED, message)
491
-
492
-
493
- class UnsupportedMethod(MT5TerminalError):
494
- """Exception raised for an unsupported method."""
495
-
496
- def __init__(self, message="Unsupported method."):
497
- super().__init__(MT5.RES_E_UNSUPPORTED, message)
498
-
499
-
500
- class AutoTradingDisabled(MT5TerminalError):
501
- """Exception raised when auto-trading is disabled."""
502
-
503
- def __init__(self, message="Auto-trading is disabled."):
504
- super().__init__(MT5.RES_E_AUTO_TRADING_DISABLED, message)
505
-
506
-
507
- class InternalFailError(MT5TerminalError):
508
- """Base exception class for internal IPC errors."""
509
-
510
- pass
511
-
512
-
513
- class InternalFailSend(InternalFailError):
514
- """Exception raised for internal IPC send failure."""
515
-
516
- def __init__(self, message="Internal IPC send failed."):
517
- super().__init__(MT5.RES_E_INTERNAL_FAIL_SEND, message)
518
-
519
-
520
- class InternalFailReceive(InternalFailError):
521
- """Exception raised for internal IPC receive failure."""
522
-
523
- def __init__(self, message="Internal IPC receive failed."):
524
- super().__init__(MT5.RES_E_INTERNAL_FAIL_RECEIVE, message)
525
-
526
-
527
- class InternalFailInit(InternalFailError):
528
- """Exception raised for internal IPC initialization failure."""
529
-
530
- def __init__(self, message="Internal IPC initialization failed."):
531
- super().__init__(MT5.RES_E_INTERNAL_FAIL_INIT, message)
532
-
533
-
534
- class InternalFailConnect(InternalFailError):
535
- """Exception raised for no IPC connection."""
536
-
537
- def __init__(self, message="No IPC connection."):
538
- super().__init__(MT5.RES_E_INTERNAL_FAIL_CONNECT, message)
539
-
540
-
541
- class InternalFailTimeout(InternalFailError):
542
- """Exception raised for an internal timeout."""
543
-
544
- def __init__(self, message="Internal timeout."):
545
- super().__init__(MT5.RES_E_INTERNAL_FAIL_TIMEOUT, message)
546
-
547
-
548
- # Dictionary to map error codes to exception classes
549
- _ERROR_CODE_TO_EXCEPTION_ = {
550
- MT5.RES_E_FAIL: GenericFail,
551
- MT5.RES_E_INVALID_PARAMS: InvalidParams,
552
- MT5.RES_E_NOT_FOUND: HistoryNotFound,
553
- MT5.RES_E_INVALID_VERSION: InvalidVersion,
554
- MT5.RES_E_AUTH_FAILED: AuthFailed,
555
- MT5.RES_E_UNSUPPORTED: UnsupportedMethod,
556
- MT5.RES_E_AUTO_TRADING_DISABLED: AutoTradingDisabled,
557
- MT5.RES_E_INTERNAL_FAIL_SEND: InternalFailSend,
558
- MT5.RES_E_INTERNAL_FAIL_RECEIVE: InternalFailReceive,
559
- MT5.RES_E_INTERNAL_FAIL_INIT: InternalFailInit,
560
- MT5.RES_E_INTERNAL_FAIL_CONNECT: InternalFailConnect,
561
- MT5.RES_E_INTERNAL_FAIL_TIMEOUT: InternalFailTimeout,
562
- }
563
-
564
-
565
- def raise_mt5_error(message: Optional[str] = None):
566
- """Raises an exception based on the given error code.
567
-
568
- Args:
569
- message: An optional custom error message.
570
-
571
- Raises:
572
- MT5TerminalError: A specific exception based on the error code.
573
- """
574
- error = _ERROR_CODE_TO_EXCEPTION_.get(MT5.last_error()[0])
575
- raise Exception(f"{error(None)} {message or MT5.last_error()[1]}")
576
-
577
-
578
- _ORDER_FILLING_TYPE_ = "https://www.mql5.com/en/docs/constants/tradingconstants/orderproperties#enum_order_type_filling"
579
- _ORDER_TYPE_ = "https://www.mql5.com/en/docs/constants/tradingconstants/orderproperties#enum_order_type"
580
- _POSITION_IDENTIFIER_ = "https://www.mql5.com/en/docs/constants/tradingconstants/positionproperties#enum_position_property_integer"
581
- _FIFO_RULE_ = "https://www.mql5.com/en/docs/constants/environment_state/accountinformation#enum_account_info_integer"
582
-
583
- _TRADE_RETCODE_MESSAGES_ = {
584
- 10004: "Requote: The price has changed, please try again",
585
- 10006: "Request rejected",
586
- 10007: "Request canceled by trader",
587
- 10008: "Order placed",
588
- 10009: "Request completed",
589
- 10010: "Only part of the request was completed",
590
- 10011: "Request processing error",
591
- 10012: "Request canceled by timeout",
592
- 10013: "Invalid request",
593
- 10014: "Invalid volume in the request",
594
- 10015: "Invalid price in the request",
595
- 10016: "Invalid stops in the request",
596
- 10017: "Trade is disabled",
597
- 10018: "Market is closed",
598
- 10019: "Insufficient funds to complete the request",
599
- 10020: "Prices changed",
600
- 10021: "No quotes to process the request",
601
- 10022: "Invalid order expiration date in the request",
602
- 10023: "Order state changed",
603
- 10024: "Too many requests, please try again later",
604
- 10025: "No changes in request",
605
- 10026: "Autotrading disabled by server",
606
- 10027: "Autotrading disabled by client terminal",
607
- 10028: "Request locked for processing",
608
- 10029: "Order or position frozen",
609
- 10030: "Invalid order filling type: see" + " " + _ORDER_FILLING_TYPE_,
610
- 10031: "No connection with the trade server",
611
- 10032: "Operation allowed only for live accounts",
612
- 10033: "The number of pending orders has reached the limit",
613
- 10034: "Order/position volume limit for the symbol reached",
614
- 10035: "Incorrect or prohibited order type: see" + " " + _ORDER_TYPE_,
615
- 10036: "Position with the specified ID has already been closed: see"
616
- + " "
617
- + _POSITION_IDENTIFIER_,
618
- 10038: "Close volume exceeds the current position volume",
619
- 10039: "A close order already exists for this position",
620
- 10040: "Maximum number of open positions reached",
621
- 10041: "Pending order activation rejected, order canceled",
622
- 10042: "Only long positions are allowed",
623
- 10043: "Only short positions are allowed",
624
- 10044: "Only position closing is allowed",
625
- 10045: "Position closing allowed only by FIFO rule: see" + " " + _FIFO_RULE_,
626
- 10046: "Opposite positions on this symbol are disabled",
627
- }
628
-
629
-
630
- def trade_retcode_message(code, display=False, add_msg=""):
631
- """
632
- Retrieves a user-friendly message corresponding to a given trade return code.
633
-
634
- Args:
635
- code (int): The trade return code to look up.
636
- display (bool, optional): Whether to print the message to the console. Defaults to False.
637
-
638
- Returns:
639
- str: The message associated with the provided trade return code. If the code is not found,
640
- it returns "Unknown trade error.".
641
- """
642
- message = _TRADE_RETCODE_MESSAGES_.get(code, "Unknown trade error")
643
- if display:
644
- print(message + add_msg)
645
- return message
1
+ from datetime import datetime
2
+ from enum import Enum
3
+ from typing import NamedTuple, Optional
4
+
5
+ try:
6
+ import MetaTrader5 as MT5
7
+ except ImportError:
8
+ import bbstrader.compat # noqa: F401
9
+
10
+
11
+ __all__ = [
12
+ "TIMEFRAMES",
13
+ "TimeFrame",
14
+ "TerminalInfo",
15
+ "AccountInfo",
16
+ "SymbolInfo",
17
+ "TickInfo",
18
+ "TradeRequest",
19
+ "OrderCheckResult",
20
+ "OrderSentResult",
21
+ "TradeOrder",
22
+ "TradePosition",
23
+ "TradeDeal",
24
+ "InvalidBroker",
25
+ "GenericFail",
26
+ "InvalidParams",
27
+ "HistoryNotFound",
28
+ "InvalidVersion",
29
+ "AuthFailed",
30
+ "UnsupportedMethod",
31
+ "AutoTradingDisabled",
32
+ "InternalFailSend",
33
+ "InternalFailReceive",
34
+ "InternalFailInit",
35
+ "InternalFailConnect",
36
+ "InternalFailTimeout",
37
+ "trade_retcode_message",
38
+ "raise_mt5_error",
39
+ ]
40
+
41
+ # TIMEFRAME is an enumeration with possible chart period values
42
+ # See https://www.mql5.com/en/docs/python_metatrader5/mt5copyratesfrom_py#timeframe
43
+ TIMEFRAMES = {
44
+ "1m": MT5.TIMEFRAME_M1,
45
+ "2m": MT5.TIMEFRAME_M2,
46
+ "3m": MT5.TIMEFRAME_M3,
47
+ "4m": MT5.TIMEFRAME_M4,
48
+ "5m": MT5.TIMEFRAME_M5,
49
+ "6m": MT5.TIMEFRAME_M6,
50
+ "10m": MT5.TIMEFRAME_M10,
51
+ "12m": MT5.TIMEFRAME_M12,
52
+ "15m": MT5.TIMEFRAME_M15,
53
+ "20m": MT5.TIMEFRAME_M20,
54
+ "30m": MT5.TIMEFRAME_M30,
55
+ "1h": MT5.TIMEFRAME_H1,
56
+ "2h": MT5.TIMEFRAME_H2,
57
+ "3h": MT5.TIMEFRAME_H3,
58
+ "4h": MT5.TIMEFRAME_H4,
59
+ "6h": MT5.TIMEFRAME_H6,
60
+ "8h": MT5.TIMEFRAME_H8,
61
+ "12h": MT5.TIMEFRAME_H12,
62
+ "D1": MT5.TIMEFRAME_D1,
63
+ "W1": MT5.TIMEFRAME_W1,
64
+ "MN1": MT5.TIMEFRAME_MN1,
65
+ }
66
+
67
+
68
+ class TimeFrame(Enum):
69
+ """
70
+ Rrepresent a time frame object
71
+ """
72
+
73
+ M1 = "1m"
74
+ M2 = "2m"
75
+ M3 = "3m"
76
+ M4 = "4m"
77
+ M5 = "5m"
78
+ M6 = "6m"
79
+ M10 = "10m"
80
+ M12 = "12m"
81
+ M15 = "15m"
82
+ M20 = "20m"
83
+ M30 = "30m"
84
+ H1 = "1h"
85
+ H2 = "2h"
86
+ H3 = "3h"
87
+ H4 = "4h"
88
+ H6 = "6h"
89
+ H8 = "8h"
90
+ H12 = "12h"
91
+ D1 = "D1"
92
+ W1 = "W1"
93
+ MN1 = "MN1"
94
+
95
+
96
+ class TerminalInfo(NamedTuple):
97
+ """
98
+ Represents general information about the trading terminal.
99
+ See https://www.mql5.com/en/docs/constants/environment_state/terminalstatus
100
+ """
101
+
102
+ community_account: bool
103
+ community_connection: bool
104
+ connected: bool
105
+ dlls_allowed: bool
106
+ trade_allowed: bool
107
+ tradeapi_disabled: bool
108
+ email_enabled: bool
109
+ ftp_enabled: bool
110
+ notifications_enabled: bool
111
+ mqid: bool
112
+ build: int
113
+ maxbars: int
114
+ codepage: int
115
+ ping_last: int
116
+ community_balance: float
117
+ retransmission: float
118
+ company: str
119
+ name: str
120
+ language: str
121
+ path: str
122
+ data_path: str
123
+ commondata_path: str
124
+
125
+
126
+ class AccountInfo(NamedTuple):
127
+ """
128
+ Represents information about a trading account.
129
+ See https://www.mql5.com/en/docs/constants/environment_state/accountinformation
130
+ """
131
+
132
+ login: int
133
+ trade_mode: int
134
+ leverage: int
135
+ limit_orders: int
136
+ margin_so_mode: int
137
+ trade_allowed: bool
138
+ trade_expert: bool
139
+ margin_mode: int
140
+ currency_digits: int
141
+ fifo_close: bool
142
+ balance: float
143
+ credit: float
144
+ profit: float
145
+ equity: float
146
+ margin: float
147
+ margin_free: float
148
+ margin_level: float
149
+ margin_so_call: float
150
+ margin_so_so: float
151
+ margin_initial: float
152
+ margin_maintenance: float
153
+ assets: float
154
+ liabilities: float
155
+ commission_blocked: float
156
+ name: str
157
+ server: str
158
+ currency: str
159
+ company: str
160
+
161
+
162
+ class SymbolInfo(NamedTuple):
163
+ """
164
+ Represents detailed information about a financial instrument.
165
+ See https://www.mql5.com/en/docs/constants/environment_state/marketinfoconstants
166
+ """
167
+
168
+ custom: bool
169
+ chart_mode: int
170
+ select: bool
171
+ visible: bool
172
+ session_deals: int
173
+ session_buy_orders: int
174
+ session_sell_orders: int
175
+ volume: int
176
+ volumehigh: int
177
+ volumelow: int
178
+ time: datetime
179
+ digits: int
180
+ spread: int
181
+ spread_float: bool
182
+ ticks_bookdepth: int
183
+ trade_calc_mode: int
184
+ trade_mode: int
185
+ start_time: int
186
+ expiration_time: int
187
+ trade_stops_level: int
188
+ trade_freeze_level: int
189
+ trade_exemode: int
190
+ swap_mode: int
191
+ swap_rollover3days: int
192
+ margin_hedged_use_leg: bool
193
+ expiration_mode: int
194
+ filling_mode: int
195
+ order_mode: int
196
+ order_gtc_mode: int
197
+ option_mode: int
198
+ option_right: int
199
+ bid: float
200
+ bidhigh: float
201
+ bidlow: float
202
+ ask: float
203
+ askhigh: float
204
+ asklow: float
205
+ last: float
206
+ lasthigh: float
207
+ lastlow: float
208
+ volume_real: float
209
+ volumehigh_real: float
210
+ volumelow_real: float
211
+ option_strike: float
212
+ point: float
213
+ trade_tick_value: float
214
+ trade_tick_value_profit: float
215
+ trade_tick_value_loss: float
216
+ trade_tick_size: float
217
+ trade_contract_size: float
218
+ trade_accrued_interest: float
219
+ trade_face_value: float
220
+ trade_liquidity_rate: float
221
+ volume_min: float
222
+ volume_max: float
223
+ volume_step: float
224
+ volume_limit: float
225
+ swap_long: float
226
+ swap_short: float
227
+ margin_initial: float
228
+ margin_maintenance: float
229
+ session_volume: float
230
+ session_turnover: float
231
+ session_interest: float
232
+ session_buy_orders_volume: float
233
+ session_sell_orders_volume: float
234
+ session_open: float
235
+ session_close: float
236
+ session_aw: float
237
+ session_price_settlement: float
238
+ session_price_limit_min: float
239
+ session_price_limit_max: float
240
+ margin_hedged: float
241
+ price_change: float
242
+ price_volatility: float
243
+ price_theoretical: float
244
+ price_greeks_delta: float
245
+ price_greeks_theta: float
246
+ price_greeks_gamma: float
247
+ price_greeks_vega: float
248
+ price_greeks_rho: float
249
+ price_greeks_omega: float
250
+ price_sensitivity: float
251
+ basis: str
252
+ category: str
253
+ currency_base: str
254
+ currency_profit: str
255
+ currency_margin: str
256
+ bank: str
257
+ description: str
258
+ exchange: str
259
+ formula: str
260
+ isin: str
261
+ name: str
262
+ page: str
263
+ path: str
264
+
265
+
266
+ class TickInfo(NamedTuple):
267
+ """
268
+ Represents the last tick for the specified financial instrument.
269
+ * time: Time of the last prices update
270
+ * bid: Current Bid price
271
+ * ask: Current Ask price
272
+ * last: Price of the last deal (Last)
273
+ * volume: Volume for the current Last price
274
+ * time_msc: Time of a price last update in milliseconds
275
+ * flags: Tick flags
276
+ * volume_real: Volume for the current Last price with greater accuracy
277
+ """
278
+
279
+ time: datetime
280
+ bid: float
281
+ ask: float
282
+ last: float
283
+ volume: int
284
+ time_msc: int
285
+ flags: int
286
+ volume_real: float
287
+
288
+
289
+ class TradeRequest(NamedTuple):
290
+ """
291
+ Represents a Trade Request Structure
292
+ See https://www.mql5.com/en/docs/constants/structures/mqltraderequest
293
+ """
294
+
295
+ action: int
296
+ magic: int
297
+ order: int
298
+ symbol: str
299
+ volume: float
300
+ price: float
301
+ stoplimit: float
302
+ sl: float
303
+ tp: float
304
+ deviation: int
305
+ type: int
306
+ type_filling: int
307
+ type_time: int
308
+ expiration: int
309
+ comment: str
310
+ position: int
311
+ position_by: int
312
+
313
+
314
+ class OrderCheckResult(NamedTuple):
315
+ """
316
+ The Structure of Results of a Trade Request Check
317
+ See https://www.mql5.com/en/docs/constants/structures/mqltradecheckresult
318
+ """
319
+
320
+ retcode: int
321
+ balance: float
322
+ equity: float
323
+ profit: float
324
+ margin: float
325
+ margin_free: float
326
+ margin_level: float
327
+ comment: str
328
+ request: TradeRequest
329
+
330
+
331
+ class OrderSentResult(NamedTuple):
332
+ """
333
+ The Structure of a Trade Request Result
334
+ See https://www.mql5.com/en/docs/constants/structures/mqltraderesult
335
+ """
336
+
337
+ retcode: int
338
+ deal: int
339
+ order: int
340
+ volume: float
341
+ price: float
342
+ bid: float
343
+ ask: float
344
+ comment: str
345
+ request_id: int
346
+ retcode_external: int
347
+ request: TradeRequest
348
+
349
+
350
+ class TradeOrder(NamedTuple):
351
+ """
352
+ Represents a trade order.
353
+ See https://www.mql5.com/en/docs/constants/tradingconstants/orderproperties
354
+ """
355
+
356
+ ticket: int
357
+ time_setup: int
358
+ time_setup_msc: int
359
+ time_done: int
360
+ time_done_msc: int
361
+ time_expiration: int
362
+ type: int
363
+ type_time: int
364
+ type_filling: int
365
+ state: int
366
+ magic: int
367
+ position_id: int
368
+ position_by_id: int
369
+ reason: int
370
+ volume_initial: float
371
+ volume_current: float
372
+ price_open: float
373
+ sl: float # Stop Loss
374
+ tp: float # Take Profit
375
+ price_current: float
376
+ price_stoplimit: float
377
+ symbol: str
378
+ comment: str
379
+ external_id: str
380
+
381
+
382
+ class TradePosition(NamedTuple):
383
+ """
384
+ Represents a trade position with attributes like ticket, open/close prices,
385
+ volume, profit, and other trading details.
386
+ See https://www.mql5.com/en/docs/constants/tradingconstants/positionproperties
387
+ """
388
+
389
+ ticket: int
390
+ time: int
391
+ time_msc: int
392
+ time_update: int
393
+ time_update_msc: int
394
+ type: int
395
+ magic: int
396
+ identifier: int
397
+ reason: int
398
+ volume: float
399
+ price_open: float
400
+ sl: float # Stop Loss
401
+ tp: float # Take Profit
402
+ price_current: float
403
+ swap: float
404
+ profit: float
405
+ symbol: str
406
+ comment: str
407
+ external_id: str
408
+
409
+
410
+ class TradeDeal(NamedTuple):
411
+ """
412
+ Represents a trade deal execution.
413
+ See https://www.mql5.com/en/docs/constants/tradingconstants/dealproperties
414
+ """
415
+
416
+ ticket: int
417
+ order: int
418
+ time: int
419
+ time_msc: int
420
+ type: int
421
+ entry: int
422
+ magic: int
423
+ position_id: int
424
+ reason: int
425
+ volume: float
426
+ price: float
427
+ commission: float
428
+ swap: float
429
+ profit: float
430
+ fee: float
431
+ symbol: str
432
+ comment: str
433
+ external_id: str
434
+
435
+
436
+ class InvalidBroker(Exception):
437
+ """Exception raised for invalid broker errors."""
438
+
439
+ def __init__(self, message="Invalid broker."):
440
+ super().__init__(message)
441
+
442
+
443
+ class MT5TerminalError(Exception):
444
+ """Base exception class for trading-related errors."""
445
+
446
+ def __init__(self, code, message):
447
+ super().__init__(message)
448
+ self.code = code
449
+ self.message = message
450
+
451
+ def __str__(self) -> str:
452
+ if self.message is None:
453
+ return f"{self.__class__.__name__}"
454
+ else:
455
+ return f"{self.__class__.__name__}, {self.message}"
456
+
457
+
458
+ class GenericFail(MT5TerminalError):
459
+ """Exception raised for generic failure."""
460
+
461
+ def __init__(self, message="Generic fail"):
462
+ super().__init__(MT5.RES_E_FAIL, message)
463
+
464
+
465
+ class InvalidParams(MT5TerminalError):
466
+ """Exception raised for invalid arguments or parameters."""
467
+
468
+ def __init__(self, message="Invalid arguments or parameters."):
469
+ super().__init__(MT5.RES_E_INVALID_PARAMS, message)
470
+
471
+
472
+ class HistoryNotFound(MT5TerminalError):
473
+ """Exception raised when no history is found."""
474
+
475
+ def __init__(self, message="No history found."):
476
+ super().__init__(MT5.RES_E_NOT_FOUND, message)
477
+
478
+
479
+ class InvalidVersion(MT5TerminalError):
480
+ """Exception raised for an invalid version."""
481
+
482
+ def __init__(self, message="Invalid version."):
483
+ super().__init__(MT5.RES_E_INVALID_VERSION, message)
484
+
485
+
486
+ class AuthFailed(MT5TerminalError):
487
+ """Exception raised for authorization failure."""
488
+
489
+ def __init__(self, message="Authorization failed."):
490
+ super().__init__(MT5.RES_E_AUTH_FAILED, message)
491
+
492
+
493
+ class UnsupportedMethod(MT5TerminalError):
494
+ """Exception raised for an unsupported method."""
495
+
496
+ def __init__(self, message="Unsupported method."):
497
+ super().__init__(MT5.RES_E_UNSUPPORTED, message)
498
+
499
+
500
+ class AutoTradingDisabled(MT5TerminalError):
501
+ """Exception raised when auto-trading is disabled."""
502
+
503
+ def __init__(self, message="Auto-trading is disabled."):
504
+ super().__init__(MT5.RES_E_AUTO_TRADING_DISABLED, message)
505
+
506
+
507
+ class InternalFailError(MT5TerminalError):
508
+ """Base exception class for internal IPC errors."""
509
+
510
+ pass
511
+
512
+
513
+ class InternalFailSend(InternalFailError):
514
+ """Exception raised for internal IPC send failure."""
515
+
516
+ def __init__(self, message="Internal IPC send failed."):
517
+ super().__init__(MT5.RES_E_INTERNAL_FAIL_SEND, message)
518
+
519
+
520
+ class InternalFailReceive(InternalFailError):
521
+ """Exception raised for internal IPC receive failure."""
522
+
523
+ def __init__(self, message="Internal IPC receive failed."):
524
+ super().__init__(MT5.RES_E_INTERNAL_FAIL_RECEIVE, message)
525
+
526
+
527
+ class InternalFailInit(InternalFailError):
528
+ """Exception raised for internal IPC initialization failure."""
529
+
530
+ def __init__(self, message="Internal IPC initialization failed."):
531
+ super().__init__(MT5.RES_E_INTERNAL_FAIL_INIT, message)
532
+
533
+
534
+ class InternalFailConnect(InternalFailError):
535
+ """Exception raised for no IPC connection."""
536
+
537
+ def __init__(self, message="No IPC connection."):
538
+ super().__init__(MT5.RES_E_INTERNAL_FAIL_CONNECT, message)
539
+
540
+
541
+ class InternalFailTimeout(InternalFailError):
542
+ """Exception raised for an internal timeout."""
543
+
544
+ def __init__(self, message="Internal timeout."):
545
+ super().__init__(MT5.RES_E_INTERNAL_FAIL_TIMEOUT, message)
546
+
547
+
548
+ # Dictionary to map error codes to exception classes
549
+ _ERROR_CODE_TO_EXCEPTION_ = {
550
+ MT5.RES_E_FAIL: GenericFail,
551
+ MT5.RES_E_INVALID_PARAMS: InvalidParams,
552
+ MT5.RES_E_NOT_FOUND: HistoryNotFound,
553
+ MT5.RES_E_INVALID_VERSION: InvalidVersion,
554
+ MT5.RES_E_AUTH_FAILED: AuthFailed,
555
+ MT5.RES_E_UNSUPPORTED: UnsupportedMethod,
556
+ MT5.RES_E_AUTO_TRADING_DISABLED: AutoTradingDisabled,
557
+ MT5.RES_E_INTERNAL_FAIL_SEND: InternalFailSend,
558
+ MT5.RES_E_INTERNAL_FAIL_RECEIVE: InternalFailReceive,
559
+ MT5.RES_E_INTERNAL_FAIL_INIT: InternalFailInit,
560
+ MT5.RES_E_INTERNAL_FAIL_CONNECT: InternalFailConnect,
561
+ MT5.RES_E_INTERNAL_FAIL_TIMEOUT: InternalFailTimeout,
562
+ }
563
+
564
+
565
+ def raise_mt5_error(message: Optional[str] = None):
566
+ """Raises an exception based on the given error code.
567
+
568
+ Args:
569
+ message: An optional custom error message.
570
+
571
+ Raises:
572
+ MT5TerminalError: A specific exception based on the error code.
573
+ """
574
+ error = _ERROR_CODE_TO_EXCEPTION_.get(MT5.last_error()[0])
575
+ raise Exception(f"{error(None)} {message or MT5.last_error()[1]}")
576
+
577
+
578
+ _ORDER_FILLING_TYPE_ = "https://www.mql5.com/en/docs/constants/tradingconstants/orderproperties#enum_order_type_filling"
579
+ _ORDER_TYPE_ = "https://www.mql5.com/en/docs/constants/tradingconstants/orderproperties#enum_order_type"
580
+ _POSITION_IDENTIFIER_ = "https://www.mql5.com/en/docs/constants/tradingconstants/positionproperties#enum_position_property_integer"
581
+ _FIFO_RULE_ = "https://www.mql5.com/en/docs/constants/environment_state/accountinformation#enum_account_info_integer"
582
+
583
+ _TRADE_RETCODE_MESSAGES_ = {
584
+ 10004: "Requote: The price has changed, please try again",
585
+ 10006: "Request rejected",
586
+ 10007: "Request canceled by trader",
587
+ 10008: "Order placed",
588
+ 10009: "Request completed",
589
+ 10010: "Only part of the request was completed",
590
+ 10011: "Request processing error",
591
+ 10012: "Request canceled by timeout",
592
+ 10013: "Invalid request",
593
+ 10014: "Invalid volume in the request",
594
+ 10015: "Invalid price in the request",
595
+ 10016: "Invalid stops in the request",
596
+ 10017: "Trade is disabled",
597
+ 10018: "Market is closed",
598
+ 10019: "Insufficient funds to complete the request",
599
+ 10020: "Prices changed",
600
+ 10021: "No quotes to process the request",
601
+ 10022: "Invalid order expiration date in the request",
602
+ 10023: "Order state changed",
603
+ 10024: "Too many requests, please try again later",
604
+ 10025: "No changes in request",
605
+ 10026: "Autotrading disabled by server",
606
+ 10027: "Autotrading disabled by client terminal",
607
+ 10028: "Request locked for processing",
608
+ 10029: "Order or position frozen",
609
+ 10030: "Invalid order filling type: see" + " " + _ORDER_FILLING_TYPE_,
610
+ 10031: "No connection with the trade server",
611
+ 10032: "Operation allowed only for live accounts",
612
+ 10033: "The number of pending orders has reached the limit",
613
+ 10034: "Order/position volume limit for the symbol reached",
614
+ 10035: "Incorrect or prohibited order type: see" + " " + _ORDER_TYPE_,
615
+ 10036: "Position with the specified ID has already been closed: see"
616
+ + " "
617
+ + _POSITION_IDENTIFIER_,
618
+ 10038: "Close volume exceeds the current position volume",
619
+ 10039: "A close order already exists for this position",
620
+ 10040: "Maximum number of open positions reached",
621
+ 10041: "Pending order activation rejected, order canceled",
622
+ 10042: "Only long positions are allowed",
623
+ 10043: "Only short positions are allowed",
624
+ 10044: "Only position closing is allowed",
625
+ 10045: "Position closing allowed only by FIFO rule: see" + " " + _FIFO_RULE_,
626
+ 10046: "Opposite positions on this symbol are disabled",
627
+ }
628
+
629
+
630
+ def trade_retcode_message(code, display=False, add_msg=""):
631
+ """
632
+ Retrieves a user-friendly message corresponding to a given trade return code.
633
+
634
+ Args:
635
+ code (int): The trade return code to look up.
636
+ display (bool, optional): Whether to print the message to the console. Defaults to False.
637
+
638
+ Returns:
639
+ str: The message associated with the provided trade return code. If the code is not found,
640
+ it returns "Unknown trade error.".
641
+ """
642
+ message = _TRADE_RETCODE_MESSAGES_.get(code, "Unknown trade error")
643
+ if display:
644
+ print(message + add_msg)
645
+ return message