bbstrader 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

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