bbstrader 0.2.97__tar.gz → 0.2.98__tar.gz

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 (53) hide show
  1. {bbstrader-0.2.97/bbstrader.egg-info → bbstrader-0.2.98}/PKG-INFO +12 -16
  2. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/__main__.py +1 -1
  3. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/btengine/strategy.py +4 -3
  4. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/metatrader/account.py +30 -6
  5. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/metatrader/copier.py +6 -11
  6. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/metatrader/trade.py +36 -64
  7. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/metatrader/utils.py +16 -0
  8. bbstrader-0.2.98/bbstrader/trading/execution.py +990 -0
  9. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/trading/scripts.py +10 -11
  10. {bbstrader-0.2.97 → bbstrader-0.2.98/bbstrader.egg-info}/PKG-INFO +12 -16
  11. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader.egg-info/requires.txt +11 -15
  12. {bbstrader-0.2.97 → bbstrader-0.2.98}/requirements.txt +11 -16
  13. {bbstrader-0.2.97 → bbstrader-0.2.98}/setup.py +1 -1
  14. bbstrader-0.2.97/bbstrader/trading/execution.py +0 -857
  15. {bbstrader-0.2.97 → bbstrader-0.2.98}/LICENSE +0 -0
  16. {bbstrader-0.2.97 → bbstrader-0.2.98}/MANIFEST.in +0 -0
  17. {bbstrader-0.2.97 → bbstrader-0.2.98}/README.md +0 -0
  18. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/__ini__.py +0 -0
  19. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/btengine/__init__.py +0 -0
  20. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/btengine/backtest.py +0 -0
  21. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/btengine/data.py +0 -0
  22. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/btengine/event.py +0 -0
  23. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/btengine/execution.py +0 -0
  24. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/btengine/performance.py +0 -0
  25. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/btengine/portfolio.py +0 -0
  26. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/btengine/scripts.py +0 -0
  27. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/compat.py +0 -0
  28. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/config.py +0 -0
  29. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/core/__init__.py +0 -0
  30. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/core/data.py +0 -0
  31. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/core/utils.py +0 -0
  32. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/ibkr/__init__.py +0 -0
  33. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/ibkr/utils.py +0 -0
  34. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/metatrader/__init__.py +0 -0
  35. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/metatrader/rates.py +0 -0
  36. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/metatrader/risk.py +0 -0
  37. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/metatrader/scripts.py +0 -0
  38. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/models/__init__.py +0 -0
  39. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/models/factors.py +0 -0
  40. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/models/ml.py +0 -0
  41. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/models/nlp.py +0 -0
  42. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/models/optimization.py +0 -0
  43. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/models/portfolio.py +0 -0
  44. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/models/risk.py +0 -0
  45. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/trading/__init__.py +0 -0
  46. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/trading/strategies.py +0 -0
  47. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/trading/utils.py +0 -0
  48. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader/tseries.py +0 -0
  49. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader.egg-info/SOURCES.txt +0 -0
  50. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader.egg-info/dependency_links.txt +0 -0
  51. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader.egg-info/entry_points.txt +0 -0
  52. {bbstrader-0.2.97 → bbstrader-0.2.98}/bbstrader.egg-info/top_level.txt +0 -0
  53. {bbstrader-0.2.97 → bbstrader-0.2.98}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bbstrader
3
- Version: 0.2.97
3
+ Version: 0.2.98
4
4
  Summary: Simplified Investment & Trading Toolkit
5
5
  Home-page: https://github.com/bbalouki/bbstrader
6
6
  Download-URL: https://pypi.org/project/bbstrader/
@@ -24,21 +24,24 @@ Classifier: Operating System :: MacOS
24
24
  Classifier: License :: OSI Approved :: MIT License
25
25
  Description-Content-Type: text/markdown
26
26
  License-File: LICENSE
27
- Requires-Dist: pandas
27
+ Requires-Dist: numpy<2.0.0,>=1.26.0
28
+ Requires-Dist: scikit-learn
29
+ Requires-Dist: statsmodels
30
+ Requires-Dist: seaborn
31
+ Requires-Dist: lightgbm
32
+ Requires-Dist: dash
33
+ Requires-Dist: nltk
34
+ Requires-Dist: spacy
35
+ Requires-Dist: pmdarima
36
+ Requires-Dist: pyportfolioopt
37
+ Requires-Dist: alphalens-reloaded
28
38
  Requires-Dist: pandas_ta
29
- Requires-Dist: numpy<2.0.0
30
39
  Requires-Dist: yfinance
31
- Requires-Dist: scipy
32
40
  Requires-Dist: hmmlearn
33
- Requires-Dist: pmdarima
34
41
  Requires-Dist: arch
35
42
  Requires-Dist: hurst
36
- Requires-Dist: seaborn
37
- Requires-Dist: statsmodels
38
- Requires-Dist: matplotlib
39
43
  Requires-Dist: filterpy
40
44
  Requires-Dist: pykalman
41
- Requires-Dist: pytest
42
45
  Requires-Dist: CurrencyConverter
43
46
  Requires-Dist: tabulate
44
47
  Requires-Dist: python-dotenv
@@ -46,24 +49,17 @@ Requires-Dist: ipython
46
49
  Requires-Dist: QuantStats
47
50
  Requires-Dist: exchange-calendars
48
51
  Requires-Dist: tqdm
49
- Requires-Dist: scikit-learn
50
52
  Requires-Dist: notify-py
51
53
  Requires-Dist: python-telegram-bot
52
- Requires-Dist: pyportfolioopt
53
54
  Requires-Dist: eodhd
54
55
  Requires-Dist: financetoolkit
55
56
  Requires-Dist: PyYAML
56
57
  Requires-Dist: tables
57
- Requires-Dist: lightgbm
58
- Requires-Dist: alphalens-reloaded
59
58
  Requires-Dist: pyfiglet
60
59
  Requires-Dist: colorama
61
60
  Requires-Dist: praw
62
61
  Requires-Dist: tweepy
63
62
  Requires-Dist: beautifulsoup4
64
- Requires-Dist: dash
65
- Requires-Dist: nltk
66
- Requires-Dist: spacy
67
63
  Requires-Dist: textblob
68
64
  Requires-Dist: vaderSentiment
69
65
  Provides-Extra: mt5
@@ -16,7 +16,7 @@ USAGE_TEXT = """
16
16
  Modules:
17
17
  copier: Copy trades from one MetaTrader account to another or multiple accounts
18
18
  backtest: Backtest a strategy, see bbstrader.btengine.backtest.run_backtest
19
- execution: Execute a strategy, see bbstrader.trading.execution.MT5ExecutionEngine
19
+ execution: Execute a strategy, see bbstrader.trading.execution.Mt5ExecutionEngine
20
20
 
21
21
  python -m bbstrader --run <module> --help for more information on the module
22
22
  """
@@ -12,13 +12,13 @@ from loguru import logger
12
12
  from bbstrader.btengine.data import DataHandler
13
13
  from bbstrader.btengine.event import FillEvent, SignalEvent
14
14
  from bbstrader.config import BBSTRADER_DIR
15
- from bbstrader.metatrader.trade import TradeSignal
16
15
  from bbstrader.metatrader.account import (
17
16
  Account,
18
17
  AdmiralMarktsGroup,
19
18
  PepperstoneGroupLimited,
20
19
  )
21
20
  from bbstrader.metatrader.rates import Rates
21
+ from bbstrader.metatrader.trade import TradeSignal
22
22
  from bbstrader.models.optimization import optimized_weights
23
23
 
24
24
  __all__ = ["Strategy", "MT5Strategy"]
@@ -67,7 +67,7 @@ class MT5Strategy(Strategy):
67
67
  """
68
68
  A `MT5Strategy()` object is a subclass of `Strategy` that is used to
69
69
  calculate signals for the MetaTrader 5 trading platform. The signals
70
- are generated by the `MT5Strategy` object and sent to the the `MT5ExecutionEngine`
70
+ are generated by the `MT5Strategy` object and sent to the the `Mt5ExecutionEngine`
71
71
  for live trading and `MT5BacktestEngine` objects for backtesting.
72
72
  """
73
73
 
@@ -146,9 +146,9 @@ class MT5Strategy(Strategy):
146
146
  def _initialize_portfolio(self):
147
147
  positions = ["LONG", "SHORT"]
148
148
  orders = ["BLMT", "BSTP", "BSTPLMT", "SLMT", "SSTP", "SSTPLMT"]
149
+ self._orders: Dict[str, Dict[str, List[SignalEvent]]] = {}
149
150
  self._positions: Dict[str, Dict[str, int | float]] = {}
150
151
  self._trades: Dict[str, Dict[str, int]] = {}
151
- self._orders: Dict[str, Dict[str, List[SignalEvent]]] = {}
152
152
  for symbol in self.symbols:
153
153
  self._positions[symbol] = {}
154
154
  self._orders[symbol] = {}
@@ -206,6 +206,7 @@ class MT5Strategy(Strategy):
206
206
  - ``price``: The price at which to execute the action, used for pending orders.
207
207
  - ``stoplimit``: The stop-limit price for STOP-LIMIT orders, used for pending stop limit orders.
208
208
  - ``id``: The unique identifier for the strategy or order.
209
+ - ``comment``: An optional comment or description related to the trade signal.
209
210
  """
210
211
  pass
211
212
 
@@ -9,6 +9,7 @@ from currency_converter import SINGLE_DAY_ECB_URL, CurrencyConverter
9
9
 
10
10
  from bbstrader.metatrader.utils import (
11
11
  AccountInfo,
12
+ BookInfo,
12
13
  InvalidBroker,
13
14
  OrderCheckResult,
14
15
  OrderSentResult,
@@ -188,10 +189,12 @@ def check_mt5_connection(**kwargs):
188
189
  except Exception:
189
190
  raise_mt5_error(INIT_MSG)
190
191
 
192
+
191
193
  def shutdown_mt5():
192
194
  """Close the connection to the MetaTrader 5 terminal."""
193
195
  mt5.shutdown()
194
-
196
+
197
+
195
198
  class Broker(object):
196
199
  def __init__(self, name: str = None, **kwargs):
197
200
  if name is None:
@@ -323,7 +326,7 @@ class Account(object):
323
326
  f"For {supported['FTMO'].name}, See [{ftmo_url}]\n"
324
327
  )
325
328
  raise InvalidBroker(message=msg)
326
-
329
+
327
330
  def shutdown(self):
328
331
  """Close the connection to the MetaTrader 5 terminal."""
329
332
  shutdown_mt5()
@@ -1037,6 +1040,29 @@ class Account(object):
1037
1040
  """
1038
1041
  self._show_info(self.get_tick_info, "tick", symbol=symbol)
1039
1042
 
1043
+ def get_market_book(self, symbol: str) -> Union[Tuple[BookInfo], None]:
1044
+ """
1045
+ Get the Market Depth content for a specific symbol.
1046
+ Args:
1047
+ symbol (str): Financial instrument name. Required unnamed parameter.
1048
+ The symbol name should be specified in the same format as in the Market Watch window.
1049
+
1050
+ Returns:
1051
+ The Market Depth content as a tuple from BookInfo entries featuring order type, price and volume in lots.
1052
+ Return None in case of an error.
1053
+
1054
+ Raises:
1055
+ MT5TerminalError: A specific exception based on the error code.
1056
+ """
1057
+ try:
1058
+ book = mt5.market_book_get(symbol)
1059
+ if book is None:
1060
+ return None
1061
+ else:
1062
+ return Tuple([BookInfo(**entry._asdict()) for entry in book])
1063
+ except Exception as e:
1064
+ raise_mt5_error(e)
1065
+
1040
1066
  def calculate_margin(
1041
1067
  self, action: Literal["buy", "sell"], symbol: str, lot: float, price: float
1042
1068
  ) -> float:
@@ -1299,8 +1325,7 @@ class Account(object):
1299
1325
  df.drop(["time_msc", "external_id"], axis=1, inplace=True)
1300
1326
  df.set_index("time", inplace=True)
1301
1327
  if save:
1302
- file = "trade_history.csv"
1303
- df.to_csv(file)
1328
+ df.to_csv("trades_history.csv")
1304
1329
  if to_df:
1305
1330
  return df
1306
1331
  else:
@@ -1507,8 +1532,7 @@ class Account(object):
1507
1532
  df["time_done"] = pd.to_datetime(df["time_done"], unit="s")
1508
1533
 
1509
1534
  if save:
1510
- file = "trade_history.csv"
1511
- df.to_csv(file)
1535
+ df.to_csv("orders_history.csv")
1512
1536
  if to_df:
1513
1537
  return df
1514
1538
  else:
@@ -302,16 +302,11 @@ class TradeCopier(object):
302
302
  if self.start_time is None or self.end_time is None:
303
303
  return True
304
304
  else:
305
- start_hour, start_minutes = self.start_time.split(":")
306
- end_hour, end_minutes = self.end_time.split(":")
307
- if int(start_hour) < datetime.now().hour < int(end_hour):
305
+ now = datetime.now()
306
+ start_time = datetime.strptime(self.start_time, "%H:%M").time()
307
+ end_time = datetime.strptime(self.end_time, "%H:%M").time()
308
+ if start_time <= now.time() <= end_time:
308
309
  return True
309
- elif datetime.now().hour == int(start_hour):
310
- if datetime.now().minute >= int(start_minutes):
311
- return True
312
- elif datetime.now().hour == int(end_hour):
313
- if datetime.now().minute < int(end_minutes):
314
- return True
315
310
  return False
316
311
 
317
312
  def copy_new_trade(
@@ -608,10 +603,10 @@ class TradeCopier(object):
608
603
  self.copy_orders(destination)
609
604
  self.copy_positions(destination)
610
605
  time.sleep(0.1)
611
- except Exception as e:
612
- self.log_error(e)
613
606
  except KeyboardInterrupt:
614
607
  break
608
+ except Exception as e:
609
+ self.log_error(e)
615
610
  time.sleep(self.sleeptime)
616
611
 
617
612
 
@@ -137,6 +137,20 @@ class TradeSignal:
137
137
  )
138
138
 
139
139
 
140
+ Buys = Literal["BMKT", "BLMT", "BSTP", "BSTPLMT"]
141
+ Sells = Literal["SMKT", "SLMT", "SSTP", "SSTPLMT"]
142
+ Positions = Literal["all", "buy", "sell", "profitable", "losing"]
143
+ Orders = Literal[
144
+ "all",
145
+ "buy_stops",
146
+ "sell_stops",
147
+ "buy_limits",
148
+ "sell_limits",
149
+ "buy_stop_limits",
150
+ "sell_stop_limits",
151
+ ]
152
+
153
+
140
154
  class Trade(RiskManagement):
141
155
  """
142
156
  Extends the `RiskManagement` class to include specific trading operations,
@@ -239,15 +253,12 @@ class Trade(RiskManagement):
239
253
  See the ``bbstrader.metatrader.risk.RiskManagement`` class for more details on these parameters.
240
254
  See `bbstrader.metatrader.account.check_mt5_connection()` for more details on how to connect to MT5 terminal.
241
255
  """
242
- # Call the parent class constructor first
243
256
  super().__init__(
244
257
  symbol=symbol,
245
258
  start_time=start_time,
246
259
  finishing_time=finishing_time,
247
- **kwargs, # Pass kwargs to the parent constructor
260
+ **kwargs,
248
261
  )
249
-
250
- # Initialize Trade-specific attributes
251
262
  self.symbol = symbol
252
263
  self.expert_name = expert_name
253
264
  self.expert_id = expert_id
@@ -261,12 +272,6 @@ class Trade(RiskManagement):
261
272
  self.tf = kwargs.get("time_frame", "D1")
262
273
  self.kwargs = kwargs
263
274
 
264
- self.start_time_hour, self.start_time_minutes = self.start.split(":")
265
- self.finishing_time_hour, self.finishing_time_minutes = self.finishing.split(
266
- ":"
267
- )
268
- self.ending_time_hour, self.ending_time_minutes = self.end.split(":")
269
-
270
275
  self.buy_positions = []
271
276
  self.sell_positions = []
272
277
  self.opened_positions = []
@@ -480,18 +485,14 @@ class Trade(RiskManagement):
480
485
  session_data, headers=["Statistics", "Values"], tablefmt="outline"
481
486
  )
482
487
 
483
- # Print the formatted statistics
484
488
  if self.verbose:
485
489
  print("\n[======= Trading Session Statistics =======]")
486
490
  print(session_table)
487
491
 
488
- # Save to CSV if specified
489
- if save:
492
+ if save and stats["deals"] > 0:
490
493
  today_date = datetime.now().strftime("%Y%m%d%H%M%S")
491
- # Create a dictionary with the statistics
492
494
  statistics_dict = {item[0]: item[1] for item in session_data}
493
495
  stats_df = pd.DataFrame(statistics_dict, index=[0])
494
- # Create the directory if it doesn't exist
495
496
  dir = dir or ".sessions"
496
497
  os.makedirs(dir, exist_ok=True)
497
498
  if "." in self.symbol:
@@ -504,8 +505,6 @@ class Trade(RiskManagement):
504
505
  stats_df.to_csv(filepath, index=False)
505
506
  LOGGER.info(f"Session statistics saved to {filepath}")
506
507
 
507
- Buys = Literal["BMKT", "BLMT", "BSTP", "BSTPLMT"]
508
-
509
508
  def open_buy_position(
510
509
  self,
511
510
  action: Buys = "BMKT",
@@ -595,8 +594,6 @@ class Trade(RiskManagement):
595
594
  }
596
595
  return type
597
596
 
598
- Sells = Literal["SMKT", "SLMT", "SSTP", "SSTPLMT"]
599
-
600
597
  def open_sell_position(
601
598
  self,
602
599
  action: Sells = "SMKT",
@@ -1370,13 +1367,16 @@ class Trade(RiskManagement):
1370
1367
  profit = 0.0
1371
1368
  balance = self.get_account_info().balance
1372
1369
  target = round((balance * self.target) / 100, 2)
1373
- if len(self.opened_positions) != 0:
1374
- for position in self.opened_positions:
1370
+ opened_positions = self.get_today_deals(group=self.symbol)
1371
+ if len(opened_positions) != 0:
1372
+ for position in opened_positions:
1375
1373
  time.sleep(0.1)
1376
1374
  # This return two TradeDeal Object,
1377
1375
  # The first one is the opening order
1378
1376
  # The second is the closing order
1379
- history = self.get_trades_history(position=position, to_df=False)
1377
+ history = self.get_trades_history(
1378
+ position=position.position_id, to_df=False
1379
+ )
1380
1380
  if history is not None and len(history) == 2:
1381
1381
  profit += history[1].profit
1382
1382
  commission += history[0].commission
@@ -1598,16 +1598,6 @@ class Trade(RiskManagement):
1598
1598
  f"No {order_type.upper()} {tikets_type.upper()} to close, SYMBOL={self.symbol}."
1599
1599
  )
1600
1600
 
1601
- Orders = Literal[
1602
- "all",
1603
- "buy_stops",
1604
- "sell_stops",
1605
- "buy_limits",
1606
- "sell_limits",
1607
- "buy_stop_limits",
1608
- "sell_stop_limits",
1609
- ]
1610
-
1611
1601
  def close_orders(
1612
1602
  self,
1613
1603
  order_type: Orders,
@@ -1642,8 +1632,6 @@ class Trade(RiskManagement):
1642
1632
  orders, "orders", self.close_order, order_type, id=id, comment=comment
1643
1633
  )
1644
1634
 
1645
- Positions = Literal["all", "buy", "sell", "profitable", "losing"]
1646
-
1647
1635
  def close_positions(
1648
1636
  self,
1649
1637
  position_type: Positions,
@@ -1689,13 +1677,11 @@ class Trade(RiskManagement):
1689
1677
  List[TradeDeal]: List of today deals
1690
1678
  """
1691
1679
  date_from = datetime.now() - timedelta(days=2)
1692
- history = self.get_trades_history(date_from=date_from, group=group, to_df=False)
1680
+ history = (
1681
+ self.get_trades_history(date_from=date_from, group=group, to_df=False) or []
1682
+ )
1693
1683
  positions_ids = set(
1694
- [
1695
- deal.position_id
1696
- for deal in history
1697
- if history is not None and deal.magic == self.expert_id
1698
- ]
1684
+ [deal.position_id for deal in history if deal.magic == self.expert_id]
1699
1685
  )
1700
1686
  today_deals = []
1701
1687
  for position in positions_ids:
@@ -1715,11 +1701,12 @@ class Trade(RiskManagement):
1715
1701
  :return: bool
1716
1702
  """
1717
1703
  negative_deals = 0
1704
+ max_trades = self.max_trade()
1718
1705
  today_deals = self.get_today_deals(group=self.symbol)
1719
1706
  for deal in today_deals:
1720
1707
  if deal.profit < 0:
1721
1708
  negative_deals += 1
1722
- if negative_deals >= self.max_trades:
1709
+ if negative_deals >= max_trades:
1723
1710
  return True
1724
1711
  return False
1725
1712
 
@@ -1800,7 +1787,6 @@ class Trade(RiskManagement):
1800
1787
  The function assumes that the returns are the excess of
1801
1788
  those compared to a benchmark.
1802
1789
  """
1803
- # Get total history
1804
1790
  import warnings
1805
1791
 
1806
1792
  warnings.filterwarnings("ignore")
@@ -1817,33 +1803,19 @@ class Trade(RiskManagement):
1817
1803
 
1818
1804
  def days_end(self) -> bool:
1819
1805
  """Check if it is the end of the trading day."""
1820
- current_hour = datetime.now().hour
1821
- current_minute = datetime.now().minute
1822
-
1823
- ending_hour = int(self.ending_time_hour)
1824
- ending_minute = int(self.ending_time_minutes)
1825
-
1826
- if current_hour > ending_hour or (
1827
- current_hour == ending_hour and current_minute >= ending_minute
1828
- ):
1806
+ now = datetime.now()
1807
+ end = datetime.strptime(self.end, "%H:%M").time()
1808
+ if now.time() >= end:
1829
1809
  return True
1830
- else:
1831
- return False
1810
+ return False
1832
1811
 
1833
1812
  def trading_time(self):
1834
1813
  """Check if it is time to trade."""
1835
- if (
1836
- int(self.start_time_hour)
1837
- < datetime.now().hour
1838
- < int(self.finishing_time_hour)
1839
- ):
1814
+ now = datetime.now()
1815
+ start = datetime.strptime(self.start, "%H:%M").time()
1816
+ end = datetime.strptime(self.finishing, "%H:%M").time()
1817
+ if start <= now.time() <= end:
1840
1818
  return True
1841
- elif datetime.now().hour == int(self.start_time_hour):
1842
- if datetime.now().minute >= int(self.start_time_minutes):
1843
- return True
1844
- elif datetime.now().hour == int(self.finishing_time_hour):
1845
- if datetime.now().minute < int(self.finishing_time_minutes):
1846
- return True
1847
1819
  return False
1848
1820
 
1849
1821
  def sleep_time(self, weekend=False):
@@ -286,6 +286,22 @@ class TickInfo(NamedTuple):
286
286
  volume_real: float
287
287
 
288
288
 
289
+ class BookInfo(NamedTuple):
290
+ """
291
+ Represents the structure of a book.
292
+ * type: Type of the order (buy/sell)
293
+ * price: Price of the order
294
+ * volume: Volume of the order in lots
295
+ * volume_dbl: Volume with greater accuracy
296
+
297
+ """
298
+
299
+ type: int
300
+ price: float
301
+ volume: float
302
+ volume_dbl: float
303
+
304
+
289
305
  class TradeRequest(NamedTuple):
290
306
  """
291
307
  Represents a Trade Request Structure