bbstrader 0.2.93__py3-none-any.whl → 0.2.95__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.
- bbstrader/__ini__.py +20 -20
- bbstrader/__main__.py +50 -50
- bbstrader/btengine/__init__.py +54 -54
- bbstrader/btengine/scripts.py +157 -157
- bbstrader/compat.py +19 -19
- bbstrader/config.py +137 -137
- bbstrader/core/data.py +22 -22
- bbstrader/core/utils.py +146 -146
- bbstrader/metatrader/__init__.py +6 -6
- bbstrader/metatrader/account.py +1516 -1516
- bbstrader/metatrader/copier.py +750 -745
- bbstrader/metatrader/rates.py +584 -584
- bbstrader/metatrader/risk.py +749 -748
- bbstrader/metatrader/scripts.py +81 -81
- bbstrader/metatrader/trade.py +1836 -1836
- bbstrader/metatrader/utils.py +645 -645
- bbstrader/models/__init__.py +10 -10
- bbstrader/models/factors.py +312 -312
- bbstrader/models/ml.py +1272 -1272
- bbstrader/models/optimization.py +182 -182
- bbstrader/models/portfolio.py +223 -223
- bbstrader/models/risk.py +398 -398
- bbstrader/trading/__init__.py +11 -11
- bbstrader/trading/execution.py +846 -846
- bbstrader/trading/script.py +155 -155
- bbstrader/trading/scripts.py +69 -69
- bbstrader/trading/strategies.py +860 -860
- bbstrader/tseries.py +1842 -1842
- {bbstrader-0.2.93.dist-info → bbstrader-0.2.95.dist-info}/LICENSE +21 -21
- {bbstrader-0.2.93.dist-info → bbstrader-0.2.95.dist-info}/METADATA +188 -187
- bbstrader-0.2.95.dist-info/RECORD +44 -0
- bbstrader-0.2.93.dist-info/RECORD +0 -44
- {bbstrader-0.2.93.dist-info → bbstrader-0.2.95.dist-info}/WHEEL +0 -0
- {bbstrader-0.2.93.dist-info → bbstrader-0.2.95.dist-info}/entry_points.txt +0 -0
- {bbstrader-0.2.93.dist-info → bbstrader-0.2.95.dist-info}/top_level.txt +0 -0
bbstrader/metatrader/utils.py
CHANGED
|
@@ -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
|