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

@@ -1,7 +1,7 @@
1
1
  import os
2
2
  import re
3
3
  import urllib.request
4
- from datetime import datetime
4
+ from datetime import datetime, timedelta
5
5
  from typing import Any, Dict, List, Literal, Optional, Tuple, Union
6
6
 
7
7
  import pandas as pd
@@ -54,9 +54,7 @@ BROKERS_TIMEZONES = {
54
54
  "PGL": "Europe/Helsinki",
55
55
  }
56
56
 
57
- _ADMIRAL_MARKETS_URL_ = (
58
- "https://cabinet.a-partnership.com/visit/?bta=35537&brand=admiralmarkets"
59
- )
57
+ _ADMIRAL_MARKETS_URL_ = "https://one.justmarkets.link/a/tufvj0xugm/registration/trader"
60
58
  _JUST_MARKETS_URL_ = "https://one.justmarkets.link/a/tufvj0xugm/registration/trader"
61
59
  _FTMO_URL_ = "https://trader.ftmo.com/?affiliates=JGmeuQqepAZLMcdOEQRp"
62
60
  _ADMIRAL_MARKETS_PRODUCTS_ = [
@@ -138,13 +136,13 @@ AMG_EXCHANGES = {
138
136
  }
139
137
 
140
138
 
141
- def check_mt5_connection(**kwargs):
139
+ def check_mt5_connection(**kwargs) -> bool:
142
140
  """
143
141
  Initialize the connection to the MetaTrader 5 terminal.
144
142
 
145
143
  Args:
146
144
  path (str, optional): The path to the MetaTrader 5 terminal executable file.
147
- Defaults to None (e.g., "C:\\Program Files\\MetaTrader 5\\terminal64.exe").
145
+ Defaults to None (e.g., "C:/Program Files/MetaTrader 5/terminal64.exe").
148
146
  login (int, optional): The login ID of the trading account. Defaults to None.
149
147
  password (str, optional): The password of the trading account. Defaults to None.
150
148
  server (str, optional): The name of the trade server to which the client terminal is connected.
@@ -165,6 +163,7 @@ def check_mt5_connection(**kwargs):
165
163
  timeout = kwargs.get("timeout", 60_000)
166
164
  portable = kwargs.get("portable", False)
167
165
 
166
+ init = False
168
167
  if path is None and (login or password or server):
169
168
  raise ValueError(
170
169
  "You must provide a path to the terminal executable file"
@@ -189,6 +188,7 @@ def check_mt5_connection(**kwargs):
189
188
  raise_mt5_error(INIT_MSG)
190
189
  except Exception:
191
190
  raise_mt5_error(INIT_MSG)
191
+ return init
192
192
 
193
193
 
194
194
  def shutdown_mt5():
@@ -391,6 +391,7 @@ class Account(object):
391
391
  password: Optional[str] = None,
392
392
  server: Optional[str] = None,
393
393
  timeout: Optional[int] = 60_000,
394
+ path: Optional[str] = None,
394
395
  ) -> Union[AccountInfo, None]:
395
396
  """
396
397
  Get info on the current trading account or a specific account .
@@ -408,6 +409,8 @@ class Account(object):
408
409
  If not specified, the value of 60 000 (60 seconds) is applied.
409
410
  If the connection is not established within the specified time,
410
411
  the call is forcibly terminated and the exception is generated.
412
+ path (str, optional): The path to the MetaTrader 5 terminal executable file.
413
+ Defaults to None (e.g., "C:/Program Files/MetaTrader 5/terminal64.exe").
411
414
 
412
415
  Returns:
413
416
  - AccountInfo in the form of a Namedtuple structure.
@@ -419,6 +422,15 @@ class Account(object):
419
422
  # connect to the trade account specifying a password and a server
420
423
  if account is not None and password is not None and server is not None:
421
424
  try:
425
+ # If a path is provided, initialize the MT5 terminal with it
426
+ if path is not None:
427
+ check_mt5_connection(
428
+ path=path,
429
+ login=account,
430
+ password=password,
431
+ server=server,
432
+ timeout=timeout,
433
+ )
422
434
  authorized = mt5.login(
423
435
  account, password=password, server=server, timeout=timeout
424
436
  )
@@ -760,10 +772,7 @@ class Account(object):
760
772
  This mthods works primarly with Admirals Group AS products and Pepperstone Group Limited,
761
773
  For other brokers use `get_symbols()` or this method will use it by default.
762
774
  """
763
- if (
764
- self.broker != AdmiralMarktsGroup()
765
- or self.broker != PepperstoneGroupLimited()
766
- ):
775
+ if self.broker not in [AdmiralMarktsGroup(), PepperstoneGroupLimited()]:
767
776
  return self.get_symbols(symbol_type=SymbolType.FOREX)
768
777
  else:
769
778
  fx_categories = {
@@ -816,11 +825,8 @@ class Account(object):
816
825
  This mthods works primarly with Admirals Group AS products and Pepperstone Group Limited,
817
826
  For other brokers use `get_symbols()` or this method will use it by default.
818
827
  """
819
-
820
- if (
821
- self.broker != AdmiralMarktsGroup()
822
- or self.broker != PepperstoneGroupLimited()
823
- ):
828
+
829
+ if self.broker not in [AdmiralMarktsGroup(), PepperstoneGroupLimited()]:
824
830
  return self.get_symbols(symbol_type=SymbolType.STOCKS)
825
831
  else:
826
832
  stocks, etfs = [], []
@@ -883,7 +889,7 @@ class Account(object):
883
889
  SymbolType.STOCKS, exchange_code, exchange_map
884
890
  )
885
891
  etfs = self._get_symbols_by_category(
886
- SymbolType.ETFs, exchange_code, exchange_map
892
+ SymbolType.ETFs, exchange_code, exchange_map
887
893
  )
888
894
  return stocks + etfs if etf else stocks
889
895
 
@@ -1085,6 +1091,39 @@ class Account(object):
1085
1091
  except Exception as e:
1086
1092
  raise_mt5_error(e)
1087
1093
 
1094
+ def calculate_profit(
1095
+ self,
1096
+ action: Literal["buy", "sell"],
1097
+ symbol: str,
1098
+ lot: float,
1099
+ price_open: float,
1100
+ price_close: float,
1101
+ ) -> float:
1102
+ """
1103
+ Calculate profit in the account currency for a specified trading operation.
1104
+
1105
+ Args:
1106
+ action (str): The trading action, either 'buy' or 'sell'.
1107
+ symbol (str): The symbol of the financial instrument.
1108
+ lot (float): The lot size of the order.
1109
+ price_open (float): The price at which to position was opened.
1110
+ price_close (float): The price at which to position was closed.
1111
+
1112
+ Returns:
1113
+ float: The profit value
1114
+
1115
+ Raises:
1116
+ MT5TerminalError: A specific exception based on the error code.
1117
+
1118
+ """
1119
+ actions = {"buy": mt5.ORDER_TYPE_BUY, "sell": mt5.ORDER_TYPE_SELL}
1120
+ try:
1121
+ return mt5.order_calc_profit(
1122
+ actions[action], symbol, lot, price_open, price_close
1123
+ )
1124
+ except Exception as e:
1125
+ raise_mt5_error(e)
1126
+
1088
1127
  def check_order(self, request: Dict[str, Any]) -> OrderCheckResult:
1089
1128
  """
1090
1129
  Check funds sufficiency for performing a required trading operation.
@@ -1533,3 +1572,29 @@ class Account(object):
1533
1572
  else:
1534
1573
  history_orders = [TradeOrder(**td._asdict()) for td in history_orders]
1535
1574
  return tuple(history_orders)
1575
+
1576
+ def get_today_deals(self, id, group=None) -> List[TradeDeal]:
1577
+ """
1578
+ Get all today deals for a specific symbol or group of symbols
1579
+
1580
+ Args:
1581
+ id (int): strategy or expert id
1582
+ group (str): Symbol or group or symbol
1583
+ Returns:
1584
+ List[TradeDeal]: List of today deals
1585
+ """
1586
+ date_from = datetime.now() - timedelta(days=2)
1587
+ history = (
1588
+ self.get_trades_history(date_from=date_from, group=group, to_df=False) or []
1589
+ )
1590
+ positions_ids = set([deal.position_id for deal in history if deal.magic == id])
1591
+ today_deals = []
1592
+ for position in positions_ids:
1593
+ deal = self.get_trades_history(
1594
+ date_from=date_from, position=position, to_df=False
1595
+ )
1596
+ if deal is not None and len(deal) == 2:
1597
+ deal_time = datetime.fromtimestamp(deal[1].time)
1598
+ if deal_time.date() == datetime.now().date():
1599
+ today_deals.append(deal[1])
1600
+ return today_deals