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

@@ -4,17 +4,15 @@ import numpy as np
4
4
  import pandas as pd
5
5
  import seaborn as sns
6
6
 
7
- from bbstrader.metatrader.account import check_mt5_connection
7
+ from bbstrader.metatrader.account import check_mt5_connection, shutdown_mt5
8
8
  from bbstrader.metatrader.utils import TIMEFRAMES
9
9
 
10
10
  sns.set_theme()
11
11
 
12
12
 
13
- def _get_data(path, symbol, timeframe, bars):
14
- check_mt5_connection(path=path)
13
+ def _get_data(symbol, timeframe, bars):
15
14
  rates = mt5.copy_rates_from_pos(symbol, timeframe, 0, bars)
16
15
  df = pd.DataFrame(rates)
17
- df["time"] = pd.to_datetime(df["time"], unit="s")
18
16
  return df
19
17
 
20
18
 
@@ -76,10 +74,14 @@ def display_volume_profile(
76
74
  Returns:
77
75
  None: Displays a matplotlib chart of the volume profile.
78
76
  """
79
- df = _get_data(path, symbol, TIMEFRAMES[timeframe], bars)
77
+ check_mt5_connection(path=path)
78
+ df = _get_data(symbol, TIMEFRAMES[timeframe], bars)
79
+ if df.empty:
80
+ raise ValueError(f"No data found for {symbol} in {path}")
80
81
  hist, bin_edges, bin_centers = volume_profile(df, bins)
81
82
  poc, vah, val = value_area(hist, bin_centers, va_percentage)
82
83
  current_price = mt5.symbol_info_tick(symbol).bid
84
+ shutdown_mt5()
83
85
 
84
86
  plt.figure(figsize=(6, 10))
85
87
  plt.barh(bin_centers, hist, height=bin_centers[1] - bin_centers[0], color="skyblue")
@@ -572,6 +572,8 @@ class TradeCopier(object):
572
572
  f"Modify {ORDER_TYPE[source_order.type][1]} Order #{ticket} on @{destination.get('login')}::{symbol}, "
573
573
  f"SOURCE=@{self.source.get('login')}::{source_order.symbol}"
574
574
  )
575
+ if result.retcode == Mt5.TRADE_RETCODE_NO_CHANGES:
576
+ return
575
577
  if result.retcode != Mt5.TRADE_RETCODE_DONE:
576
578
  self.log_message(
577
579
  f"Error modifying {ORDER_TYPE[source_order.type][1]} Order #{ticket} on @{destination.get('login')}::{symbol},"
@@ -624,6 +626,8 @@ class TradeCopier(object):
624
626
  f"Modify {ORDER_TYPE[source_pos.type][1]} Position #{ticket} on @{destination.get('login')}::{symbol}, "
625
627
  f"SOURCE=@{self.source.get('login')}::{source_pos.symbol}"
626
628
  )
629
+ if result.retcode == Mt5.TRADE_RETCODE_NO_CHANGES:
630
+ return
627
631
  if result.retcode != Mt5.TRADE_RETCODE_DONE:
628
632
  self.log_message(
629
633
  f"Error modifying {ORDER_TYPE[source_pos.type][1]} Position #{ticket} on @{destination.get('login')}::{symbol}, "
@@ -2,7 +2,7 @@ import argparse
2
2
  import multiprocessing
3
3
  import sys
4
4
 
5
- from bbstrader.apps._copier import main as RunCopyAPP
5
+ from bbstrader.apps._copier import main as RunCopyApp
6
6
  from bbstrader.metatrader.copier import RunCopier, config_copier, copier_worker_process
7
7
 
8
8
 
@@ -18,6 +18,15 @@ def copier_args(parser: argparse.ArgumentParser):
18
18
  parser.add_argument(
19
19
  "-s", "--source", type=str, nargs="?", default=None, help="Source section name"
20
20
  )
21
+ parser.add_argument(
22
+ "-I", "--id", type=int, default=0, help="Source Account unique ID"
23
+ )
24
+ parser.add_argument(
25
+ "-U",
26
+ "--unique",
27
+ action="store_true",
28
+ help="Specify if the source account is only master",
29
+ )
21
30
  parser.add_argument(
22
31
  "-d",
23
32
  "--destinations",
@@ -70,6 +79,8 @@ def copy_trades(unknown):
70
79
  Options:
71
80
  -m, --mode: CLI for terminal app and GUI for Desktop app
72
81
  -s, --source: Source Account section name
82
+ -I, --id: Source Account unique ID
83
+ -U, --unique: Specify if the source account is only master
73
84
  -d, --destinations: Destination Account section names (multiple allowed)
74
85
  -i, --interval: Update interval in seconds
75
86
  -M, --multiprocess: When set to True, each destination account runs in a separate process.
@@ -86,7 +97,7 @@ def copy_trades(unknown):
86
97
  copy_args = copy_parser.parse_args(unknown)
87
98
 
88
99
  if copy_args.mode == "GUI":
89
- RunCopyAPP()
100
+ RunCopyApp()
90
101
 
91
102
  elif copy_args.mode == "CLI":
92
103
  source, destinations = config_copier(
@@ -94,6 +105,8 @@ def copy_trades(unknown):
94
105
  dest_sections=copy_args.destinations,
95
106
  inifile=copy_args.config,
96
107
  )
108
+ source["id"] = copy_args.id
109
+ source["unique"] = copy_args.unique
97
110
  if copy_args.multiprocess:
98
111
  copier_processes = []
99
112
  for dest_config in destinations:
@@ -2,6 +2,8 @@ from datetime import datetime
2
2
  from enum import Enum
3
3
  from typing import NamedTuple, Optional
4
4
 
5
+ import numpy as np
6
+
5
7
  try:
6
8
  import MetaTrader5 as MT5
7
9
  except ImportError:
@@ -28,6 +30,10 @@ __all__ = [
28
30
  "HistoryNotFound",
29
31
  "InvalidVersion",
30
32
  "AuthFailed",
33
+ "RateInfo",
34
+ "RateDtype",
35
+ "TickDtype",
36
+ "TickFlag",
31
37
  "UnsupportedMethod",
32
38
  "AutoTradingDisabled",
33
39
  "InternalFailSend",
@@ -285,6 +291,26 @@ class SymbolType(Enum):
285
291
  unknown = "UNKNOWN" # Unknown or unsupported type
286
292
 
287
293
 
294
+ TickDtype = np.dtype(
295
+ [
296
+ ("time", "<i8"),
297
+ ("bid", "<f8"),
298
+ ("ask", "<f8"),
299
+ ("last", "<f8"),
300
+ ("volume", "<u8"),
301
+ ("time_msc", "<i8"),
302
+ ("flags", "<u4"),
303
+ ("volume_real", "<f8"),
304
+ ]
305
+ )
306
+
307
+ TickFlag = {
308
+ "all": MT5.COPY_TICKS_ALL,
309
+ "info": MT5.COPY_TICKS_INFO,
310
+ "trade": MT5.COPY_TICKS_TRADE,
311
+ }
312
+
313
+
288
314
  class TickInfo(NamedTuple):
289
315
  """
290
316
  Represents the last tick for the specified financial instrument.
@@ -308,6 +334,44 @@ class TickInfo(NamedTuple):
308
334
  volume_real: float
309
335
 
310
336
 
337
+ RateDtype = np.dtype(
338
+ [
339
+ ("time", "<i8"),
340
+ ("open", "<f8"),
341
+ ("high", "<f8"),
342
+ ("low", "<f8"),
343
+ ("close", "<f8"),
344
+ ("tick_volume", "<u8"),
345
+ ("spread", "<i4"),
346
+ ("real_volume", "<u8"),
347
+ ]
348
+ )
349
+
350
+
351
+ class RateInfo(NamedTuple):
352
+ """
353
+ Reprents a candle (bar) for a specified period.
354
+ * time: Time in seconds since 1970.01.01 00:00
355
+ * open: Open price
356
+ * high: High price
357
+ * low: Low price
358
+ * close: Close price
359
+ * tick_volume: Tick volume
360
+ * spread: Spread value
361
+ * real_volume: Real volume
362
+
363
+ """
364
+
365
+ time: int
366
+ open: float
367
+ high: float
368
+ low: float
369
+ close: float
370
+ tick_volume: float
371
+ spread: int
372
+ real_volume: float
373
+
374
+
311
375
  class BookInfo(NamedTuple):
312
376
  """
313
377
  Represents the structure of a book.
bbstrader/models/ml.py CHANGED
@@ -1283,16 +1283,19 @@ class LightGBModel(object):
1283
1283
  except Exception as e:
1284
1284
  self.logger.error(f"Error getting last date: {e}")
1285
1285
  try:
1286
- days = 3 if now.weekday() == 0 else 1
1287
- time_delta = last_date - (now - pd.Timedelta(days=days)).normalize()
1288
- assert time_delta.days == days or last_date == now.normalize()
1286
+ if now.weekday() == 0: # Monday
1287
+ expected_date = (now - pd.Timedelta(days=3)).normalize() # last Friday
1288
+ else:
1289
+ expected_date = (now - pd.Timedelta(days=1)).normalize() # yesterday
1290
+
1291
+ assert last_date == expected_date or last_date == now.normalize()
1289
1292
  return True
1290
1293
  except AssertionError:
1291
1294
  yesterday = (now - pd.Timedelta(days=1)).normalize()
1292
1295
  last_friday = (now - pd.Timedelta(days=now.weekday() + 3)).normalize()
1293
1296
  self.logger.debug(
1294
- f"Last date in predictions ({last_date}) is not equal to \
1295
- yesterday ({yesterday}) or last Friday ({last_friday})"
1297
+ f"Last date in predictions ({last_date}) is not equal to "
1298
+ f"yesterday ({yesterday}) or last Friday ({last_friday})"
1296
1299
  )
1297
1300
  return False
1298
1301
 
@@ -192,8 +192,8 @@ class Mt5ExecutionEngine:
192
192
  show_positions_orders: bool = False,
193
193
  iter_time: int | float = 5,
194
194
  use_trade_time: bool = True,
195
- period: Literal["24/7", "day", "week", "month"] = "week",
196
- period_end_action: Literal["break", "sleep"] = "break",
195
+ period: Literal["24/7", "day", "week", "month"] = "month",
196
+ period_end_action: Literal["break", "sleep"] = "sleep",
197
197
  closing_pnl: Optional[float] = None,
198
198
  trading_days: Optional[List[str]] = None,
199
199
  comment: Optional[str] = None,
@@ -631,12 +631,12 @@ class Mt5ExecutionEngine:
631
631
  def _update_risk(self, weights):
632
632
  try:
633
633
  check_mt5_connection(**self.kwargs)
634
- if weights is not None:
634
+ if weights is not None and not all(v == 0 for v in weights.values()):
635
+ assert self.daily_risk is not None
635
636
  for symbol in self.symbols:
636
637
  if symbol not in weights:
637
638
  continue
638
639
  trade = self.trades_instances[symbol]
639
- assert self.daily_risk is not None
640
640
  dailydd = round(weights[symbol] * self.daily_risk, 5)
641
641
  trade.dailydd = dailydd
642
642
  except Exception as e:
@@ -930,7 +930,6 @@ class Mt5ExecutionEngine:
930
930
  return
931
931
 
932
932
  if not signals:
933
- logger.info("No signals to process.")
934
933
  return
935
934
 
936
935
  # We want to create a temporary function that
@@ -994,14 +993,12 @@ class Mt5ExecutionEngine:
994
993
  self._sleep()
995
994
  self._handle_period_end_actions(today)
996
995
  except KeyboardInterrupt:
997
- logger.info(
998
- f"Stopping Execution Engine for {self.STRATEGY} STRATEGY on {self.ACCOUNT} Account"
999
- )
1000
996
  self.stop()
1001
997
  sys.exit(0)
1002
998
  except Exception as e:
1003
999
  msg = f"Running Execution Engine, STRATEGY={self.STRATEGY} , ACCOUNT={self.ACCOUNT}"
1004
1000
  self._print_exc(msg, e)
1001
+ self._sleep()
1005
1002
 
1006
1003
  def stop(self):
1007
1004
  """Stops the execution engine."""
@@ -1038,6 +1035,10 @@ def RunMt5Engine(account_id: str, **kwargs):
1038
1035
  symbol_list, trades_instances, strategy_cls, **kwargs
1039
1036
  )
1040
1037
  engine.run()
1038
+ except KeyboardInterrupt:
1039
+ log.info(f"Execution engine for {account_id} interrupted by user")
1040
+ engine.stop()
1041
+ sys.exit(0)
1041
1042
  except Exception as e:
1042
1043
  log.exception(f"Error running execution engine for {account_id}: {e}")
1043
1044
  finally:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bbstrader
3
- Version: 0.3.3
3
+ Version: 0.3.4
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/
@@ -37,8 +37,8 @@ Requires-Dist: ipython>=8.32.0
37
37
  Requires-Dist: lightgbm>=4.5.0
38
38
  Requires-Dist: nltk>=3.9.1
39
39
  Requires-Dist: notify-py>=0.3.43
40
- Requires-Dist: numpy<2.0.0,>=1.26.0
41
- Requires-Dist: pandas_ta>=0.3.14b0
40
+ Requires-Dist: numpy>=1.26.0
41
+ Requires-Dist: pandas_ta<=0.4.67b0
42
42
  Requires-Dist: praw>=7.8.1
43
43
  Requires-Dist: pyfiglet>=1.0.2
44
44
  Requires-Dist: pykalman>=0.10.1
@@ -164,7 +164,7 @@ To begin using `bbstrader`, please ensure your system meets the following prereq
164
164
  * **MetaTrader 5 (MT5)**:
165
165
  * The MetaTrader 5 platform must be installed on your system (primarily for Windows users needing live trading or direct MT5 interaction).
166
166
  * An active trading account with a MetaTrader 5 broker. `bbstrader` currently supports:
167
- * [Admirals Group AS](https://cabinet.a-partnership.com/visit/?bta=35537&brand=admiralmarkets) (for Stocks, ETFs, Indices, Commodities, Futures, Forex)
167
+ * [Admirals Group AS](https://one.justmarkets.link/a/tufvj0xugm/registration/trader) (for Stocks, ETFs, Indices, Commodities, Futures, Forex)
168
168
  * [Just Global Markets Ltd.](https://one.justmarkets.link/a/tufvj0xugm/registration/trader) (for Stocks, Crypto, Indices, Commodities, Forex)
169
169
  * [FTMO](https://trader.ftmo.com/?affiliates=JGmeuQqepAZLMcdOEQRp) (Proprietary Firm)
170
170
 
@@ -1,8 +1,10 @@
1
1
  bbstrader/__init__.py,sha256=4KVGBEYU3ao9zPVM3rMWqNuvCleCeA6C2MVe_AFc4rw,581
2
- bbstrader/__main__.py,sha256=f6EsJGNrP1OuuNUyS5y_aMAVp4uDYjkHYxFuKwDh0dM,2233
2
+ bbstrader/__main__.py,sha256=RjUIJWaD2_Od8ZMSLL8dzc2ZuCkkzlvNaE7-LIu3RGU,2488
3
3
  bbstrader/compat.py,sha256=djbHMvTvy0HYm1zyZ6Ttp_LMwP2PqTSVw1r7pqbz7So,487
4
4
  bbstrader/config.py,sha256=riZxwb4hN0I-dSsWcjnROc5dWQpSJ9iKOMIp4PMGfko,3970
5
5
  bbstrader/tseries.py,sha256=SM_LTQHJ3ZXVkVJyZ51CefUDzJDl2TkJqBKMp_uM8s4,43833
6
+ bbstrader/apps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ bbstrader/apps/_copier.py,sha256=z6GhLSmFjHc_oh7pYRTflH42Xmpe3Z7B2fUyedzDQPo,25387
6
8
  bbstrader/btengine/__init__.py,sha256=y1btjaEfhWsH8vuE7mBRpP9Tu-Azt9REhuVYsPCAfBU,2955
7
9
  bbstrader/btengine/backtest.py,sha256=o3eoCVzpjykDx-9VgkxK6DKZdGDAuLVeeWLCa2U_zeY,14652
8
10
  bbstrader/btengine/data.py,sha256=WcBLtabboZkQdtOQS3SjbiJD9BcWc75sdhZ2voQ_lUw,27061
@@ -11,37 +13,37 @@ bbstrader/btengine/execution.py,sha256=4MytWjcKg8J_w14P43emHqsvKOElkQTfhVYNakU6e
11
13
  bbstrader/btengine/performance.py,sha256=1ecWrTzHBQbk4ORvbTEKxwCzlL1brcXOEUwgbnjAwx4,12470
12
14
  bbstrader/btengine/portfolio.py,sha256=z98M65HQeCyma8gMZkAxspxBA9jtIhzxMyJUHPPj34c,16128
13
15
  bbstrader/btengine/scripts.py,sha256=8o66dq4Ex4DsH4s8xvJqUOFjLzZJSnbBvvNBzohtzoE,4837
14
- bbstrader/btengine/strategy.py,sha256=MV50N46OPOV-xGLnO0aFp6drbh0A5e3vck_m4SFg7Sc,34400
16
+ bbstrader/btengine/strategy.py,sha256=hDghr5sXNitWlWXXvgl8Vj-QHWaIuVlx54LUXAbQrHQ,36725
15
17
  bbstrader/core/__init__.py,sha256=GIFzFSStPfE0XM2j7mDeZZQeMTh_AwPsDOQXwMVJLgw,97
16
18
  bbstrader/core/data.py,sha256=5-ByClb-E3-iqDz8CBJ4om9wBIA7DmUWezu4A-tv5ys,25095
17
19
  bbstrader/core/scripts.py,sha256=bMZ5I_hCTPCsAn9v9Sz9fQQ7JFkf_Zwtv7uUJwaqVBU,5466
18
- bbstrader/core/utils.py,sha256=WjuabzBjhY65ku2KL-f7CMalE2x-wrX-6mCA_qhhFPE,2728
20
+ bbstrader/core/utils.py,sha256=tHXQimmmlYZHktNnYNKn_wVq6v-85pI7DXF6xlJV7ps,2780
19
21
  bbstrader/ibkr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
22
  bbstrader/ibkr/utils.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
23
  bbstrader/metatrader/__init__.py,sha256=A5Ye9tpc2sp9Xk5qjKw-EfYsoRcZtAt8nqvC3tCtZs8,333
22
- bbstrader/metatrader/account.py,sha256=17HV2lv9K2xhuU0H1SFDQaVZycf8ZAWlwOalwiKj_S8,59688
23
- bbstrader/metatrader/analysis.py,sha256=pnZWdvEnf9wkPT0vhEIbZipnN5EUf6TaQftplYP9jRs,3564
24
- bbstrader/metatrader/copier.py,sha256=KFlBHguWAvxxlIKdnhs9o6y6OB8i7h-o2ZufMmQZbBA,54030
24
+ bbstrader/metatrader/account.py,sha256=Ur0vKz_puUP9jmdJUpvdQq6W5ue0RfnhkL0JLPBQkOE,65200
25
+ bbstrader/metatrader/analysis.py,sha256=ywETmG3qxZ7ms_DCjR1GcQoUrQ0es5-n-CPDYKBAm8Q,3614
26
+ bbstrader/metatrader/copier.py,sha256=knGvLsiWUzM6eYe7CrKF6rTeReFdogdCQU5Q7p7UHmY,54186
25
27
  bbstrader/metatrader/rates.py,sha256=w9mr6FB6E1zLcHCDtDGt-oMnw6sakIU6Qe3455KDsSg,20782
26
28
  bbstrader/metatrader/risk.py,sha256=NhW8qtSg350Z6H9oLcDqOU_erqd_7Y7F5FwpfPN5Qso,27262
27
- bbstrader/metatrader/scripts.py,sha256=DQGwrzqOZLX4CVWM4Du73kc5k_8RxUaIkoc8rvPTt7c,3558
29
+ bbstrader/metatrader/scripts.py,sha256=8meq6_zz6jPSibNgtYtaO8Ba-uJZOoLkpqYUIjidk-U,4010
28
30
  bbstrader/metatrader/trade.py,sha256=ezkALiUgtIu54R4m4blQjptWoRXNVG5wwuoctP2b90Y,80624
29
- bbstrader/metatrader/utils.py,sha256=9RC1RqBqgIPiEloUYD5zhXBHkf7_oeOfzhqqYQoZXqY,19561
31
+ bbstrader/metatrader/utils.py,sha256=PnFZ8EuBSZgsYlvwZDOxj4vUTtt-hUYnnwFBmu7gxxw,20738
30
32
  bbstrader/models/__init__.py,sha256=B-bn2h_SCK6gRAs2li6dDVnvV8jDT5suZimldk5xxcw,497
31
33
  bbstrader/models/factors.py,sha256=Y1rjwhWU4aiSRd-jFOLnLZczFCY0bJUxauCo17HvOFY,12791
32
- bbstrader/models/ml.py,sha256=CxbasDRXzBF3rOF4ObpIjhJw6IZ9G3C3jwoC-o_BMyI,48809
34
+ bbstrader/models/ml.py,sha256=d_maEXwGOGuWawisjIbMO5FUsaSjlgmJu5XiMu28Wf8,48921
33
35
  bbstrader/models/nlp.py,sha256=hcvz9d_8j1cIC1h3oqa1DBjExRIEd6WSiZb95Vr3NPo,32638
34
36
  bbstrader/models/optimization.py,sha256=Fa4tdhynMmvKt5KHV9cH1TXmmJVJwU4QWpYkbeVq4aI,6395
35
37
  bbstrader/models/portfolio.py,sha256=r-47Zrn2r7iKCHm5YVtwkbBJXAZGM3QYy-rXCWY9-Bg,8079
36
38
  bbstrader/models/risk.py,sha256=MKCk53HtGIcivrNzH8Ikm5KMs1rXhFT5zkorUf30PyQ,506
37
39
  bbstrader/trading/__init__.py,sha256=ycLyuuxN5SujqtzR9X0Q74UQfK93q2va-GGAXdr-KS8,457
38
- bbstrader/trading/execution.py,sha256=nAxb9EDPNF3g8I5C5vz6e9k0xF5CNNLHmKlHgck0-rY,41460
40
+ bbstrader/trading/execution.py,sha256=CYt4fageoqcpMFvdRH-jX4hexAGUiG_wE94i1qg7BFM,41479
39
41
  bbstrader/trading/scripts.py,sha256=Tf5q33WqqygjpIv43_8nA82VZ3GM0qgb4Ggo3fHJ_wg,5744
40
42
  bbstrader/trading/strategies.py,sha256=RZ6P4SfIyRW72v0OnPnrc4Hv8X00FdxR-_sD23xe_Pg,11756
41
43
  bbstrader/trading/utils.py,sha256=57dKF9dcRu04oU2VRqydRrzW39dCW2wlDWhVt-sZdRw,1857
42
- bbstrader-0.3.3.dist-info/licenses/LICENSE,sha256=ZwC_RqqGmOPBUiMDKqLyJZ5HBeHq53LpL7TMRzrJY8c,1094
43
- bbstrader-0.3.3.dist-info/METADATA,sha256=JPXFWSA0ytHgqPoaOenYRpKsea6toGN-Gc1TcrEt7Bk,27106
44
- bbstrader-0.3.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
45
- bbstrader-0.3.3.dist-info/entry_points.txt,sha256=0yDCbhbgHswOzJnY5wRSM_FjjyMHGvY7lJpSSVh0xtI,54
46
- bbstrader-0.3.3.dist-info/top_level.txt,sha256=Wwj322jZmxGZ6gD_TdaPiPLjED5ReObm5omerwlmZIg,10
47
- bbstrader-0.3.3.dist-info/RECORD,,
44
+ bbstrader-0.3.4.dist-info/licenses/LICENSE,sha256=ZwC_RqqGmOPBUiMDKqLyJZ5HBeHq53LpL7TMRzrJY8c,1094
45
+ bbstrader-0.3.4.dist-info/METADATA,sha256=_Zc1Yp1Exs0wUpD0KV1G41uDnR3v7mhHWawRDbkutSM,27089
46
+ bbstrader-0.3.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
47
+ bbstrader-0.3.4.dist-info/entry_points.txt,sha256=0yDCbhbgHswOzJnY5wRSM_FjjyMHGvY7lJpSSVh0xtI,54
48
+ bbstrader-0.3.4.dist-info/top_level.txt,sha256=Wwj322jZmxGZ6gD_TdaPiPLjED5ReObm5omerwlmZIg,10
49
+ bbstrader-0.3.4.dist-info/RECORD,,