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

@@ -124,11 +124,11 @@ class Rates(object):
124
124
  self.sd = session_duration
125
125
  self.start_pos = self._get_start_pos(start_pos, timeframe)
126
126
  self.count = count
127
- self._mt5_initialized(**kwargs)
127
+ self.__initializ_mt5(**kwargs)
128
128
  self.__account = Account(**kwargs)
129
129
  self.__data = self.get_rates_from_pos()
130
130
 
131
- def _mt5_initialized(self, **kwargs):
131
+ def __initializ_mt5(self, **kwargs):
132
132
  check_mt5_connection(**kwargs)
133
133
 
134
134
  def _get_start_pos(self, index, time_frame):
@@ -590,7 +590,7 @@ class Trade(RiskManagement):
590
590
  else:
591
591
  raise ValueError("You need to set a price for pending orders")
592
592
  else:
593
- _price = self.get_tick_info(self.symbol).ask
593
+ _price = self.get_tick_info(self.symbol).bid
594
594
 
595
595
  lot = volume or self.get_lot()
596
596
  stop_loss = self.get_stop_loss()
@@ -683,7 +683,7 @@ class Trade(RiskManagement):
683
683
  else:
684
684
  raise ValueError("You need to set a price for pending orders")
685
685
  else:
686
- _price = self.get_tick_info(self.symbol).bid
686
+ _price = self.get_tick_info(self.symbol).ask
687
687
 
688
688
  lot = volume or self.get_lot()
689
689
  stop_loss = self.get_stop_loss()
@@ -486,11 +486,7 @@ class MT5TerminalError(Exception):
486
486
  self.code = code
487
487
  self.message = message
488
488
 
489
- def __str__(self) -> str:
490
- # if self.message is None:
491
- # return f"{self.__class__.__name__}"
492
- # else:
493
- # return f"{self.__class__.__name__}, {self.message}"
489
+ def __repr__(self) -> str:
494
490
  msg_str = str(self.message) if self.message is not None else ""
495
491
  return f"{self.code} - {self.__class__.__name__}: {msg_str}"
496
492
 
bbstrader/models/nlp.py CHANGED
@@ -12,6 +12,7 @@ import matplotlib.pyplot as plt
12
12
  import nltk
13
13
  import pandas as pd
14
14
  import plotly.express as px
15
+ from bbstrader.core.data import FinancialNews
15
16
  from dash import dcc, html
16
17
  from dash.dependencies import Input, Output
17
18
  from nltk.corpus import stopwords
@@ -19,11 +20,10 @@ from nltk.tokenize import word_tokenize
19
20
  from textblob import TextBlob
20
21
  from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer
21
22
 
22
- from bbstrader.core.data import FinancialNews
23
-
24
23
  __all__ = [
25
24
  "TopicModeler",
26
25
  "SentimentAnalyzer",
26
+ "LEXICON",
27
27
  "EQUITY_LEXICON",
28
28
  "FOREX_LEXICON",
29
29
  "COMMODITIES_LEXICON",
@@ -331,6 +331,17 @@ FINANCIAL_LEXICON = {
331
331
  **BONDS_LEXICON,
332
332
  }
333
333
 
334
+ LEXICON = {
335
+ "stock": EQUITY_LEXICON,
336
+ "etf": EQUITY_LEXICON,
337
+ "future": FINANCIAL_LEXICON,
338
+ "forex": FOREX_LEXICON,
339
+ "crypto": CRYPTO_LEXICON,
340
+ "index": EQUITY_LEXICON,
341
+ "bond": BONDS_LEXICON,
342
+ "commodity": COMMODITIES_LEXICON,
343
+ }
344
+
334
345
 
335
346
  class TopicModeler(object):
336
347
  def __init__(self):
@@ -379,11 +390,6 @@ class SentimentAnalyzer(object):
379
390
  analysis using VADER (SentimentIntensityAnalyzer) and optional TextBlob
380
391
  for enhanced polarity scoring.
381
392
 
382
- Attributes:
383
- nlp (spacy.Language): A SpaCy NLP pipeline for tokenization and lemmatization,
384
- with Named Entity Recognition (NER) disabled.
385
- analyzer (SentimentIntensityAnalyzer): An instance of VADER's sentiment analyzer
386
- for financial sentiment scoring.
387
393
  """
388
394
 
389
395
  def __init__(self):
@@ -395,8 +401,6 @@ class SentimentAnalyzer(object):
395
401
  - Loads the `en_core_web_sm` SpaCy model with Named Entity Recognition (NER) disabled.
396
402
  - Initializes VADER's SentimentIntensityAnalyzer for sentiment scoring.
397
403
 
398
- Args:
399
- use_spacy (bool): If True, uses SpaCy for lemmatization. Defaults to False.
400
404
  """
401
405
  nltk.download("punkt", quiet=True)
402
406
  nltk.download("stopwords", quiet=True)
@@ -617,8 +621,9 @@ class SentimentAnalyzer(object):
617
621
 
618
622
  # Suppress stdout/stderr from underlying libraries during execution
619
623
  with open(os.devnull, "w") as devnull:
620
- with contextlib.redirect_stdout(devnull), contextlib.redirect_stderr(
621
- devnull
624
+ with (
625
+ contextlib.redirect_stdout(devnull),
626
+ contextlib.redirect_stderr(devnull),
622
627
  ):
623
628
  with ThreadPoolExecutor() as executor:
624
629
  # Map each future to its ticker for easy result lookup
@@ -1,7 +1,10 @@
1
+ import concurrent.futures
2
+ import functools
1
3
  import multiprocessing as mp
2
4
  import sys
3
5
  import time
4
6
  from datetime import date, datetime
7
+ from multiprocessing.synchronize import Event
5
8
  from typing import Callable, Dict, List, Literal, Optional
6
9
 
7
10
  import pandas as pd
@@ -179,6 +182,8 @@ class Mt5ExecutionEngine:
179
182
  mm: bool = True,
180
183
  auto_trade: bool = True,
181
184
  prompt_callback: Callable = None,
185
+ multithread: bool = False,
186
+ shutdown_event: Event = None,
182
187
  optimizer: str = "equal",
183
188
  trail: bool = True,
184
189
  stop_trail: Optional[int] = None,
@@ -207,6 +212,10 @@ class Mt5ExecutionEngine:
207
212
  the user for confimation.
208
213
  prompt_callback : Callback function to prompt the user for confirmation.
209
214
  This is useful when integrating with GUI applications.
215
+ multithread : If True, use a thread pool to process signals in parallel.
216
+ If False, process them sequentially. Set this to True only if the engine
217
+ is running in a separate process. Default to False.
218
+ shutdown_event : Use to terminate the copy process when runs in a custum environment like web App or GUI.
210
219
  show_positions_orders : Print open positions and orders. Defaults to False.
211
220
  iter_time : Interval to check for signals and `mm`. Defaults to 5.
212
221
  use_trade_time : Open trades after the time is completed. Defaults to True.
@@ -251,6 +260,7 @@ class Mt5ExecutionEngine:
251
260
  self.mm = mm
252
261
  self.auto_trade = auto_trade
253
262
  self.prompt_callback = prompt_callback
263
+ self.multithread = multithread
254
264
  self.optimizer = optimizer
255
265
  self.trail = trail
256
266
  self.stop_trail = stop_trail
@@ -274,6 +284,9 @@ class Mt5ExecutionEngine:
274
284
 
275
285
  self._initialize_engine(**kwargs)
276
286
  self.strategy = self._init_strategy(**kwargs)
287
+ self.shutdown_event = (
288
+ shutdown_event if shutdown_event is not None else mp.Event()
289
+ )
277
290
  self._running = True
278
291
 
279
292
  def __repr__(self):
@@ -316,6 +329,7 @@ class Mt5ExecutionEngine:
316
329
  def _print_exc(self, msg: str, e: Exception):
317
330
  if isinstance(e, KeyboardInterrupt):
318
331
  logger.info("Stopping the Execution Engine ...")
332
+ self.stop()
319
333
  sys.exit(0)
320
334
  if self.debug_mode:
321
335
  raise ValueError(msg).with_traceback(e.__traceback__)
@@ -866,46 +880,79 @@ class Mt5ExecutionEngine:
866
880
  f"(e.g., if time_frame is 15m, iter_time must be 1.5, 3, 5, 15 etc)"
867
881
  )
868
882
 
869
- def _handle_signals(self, today, signals, buys, sells):
883
+ def _handle_one_signal(self, signal, today, buys, sells):
870
884
  try:
871
- check_mt5_connection(**self.kwargs)
872
- for signal in signals:
873
- symbol = signal.symbol
874
- trade: Trade = self.trades_instances[symbol]
875
- if trade.trading_time() and today in self.trading_days:
876
- if signal.action is not None:
877
- action = (
878
- signal.action.value
879
- if isinstance(signal.action, TradeAction)
880
- else signal.action
881
- )
882
- self._run_trade_algorithm(
883
- action,
884
- symbol,
885
- signal.id,
886
- trade,
887
- signal.price,
888
- signal.stoplimit,
889
- buys,
890
- sells,
891
- signal.comment or self.comment,
892
- )
893
- else:
894
- if len(self.symbols) >= 10:
895
- if symbol == self.symbols[-1]:
896
- logger.info(
897
- f"Not trading Time !!!, STRATEGY={self.STRATEGY} , ACCOUNT={self.ACCOUNT}"
898
- )
899
- else:
885
+ symbol = signal.symbol
886
+ trade: Trade = self.trades_instances[symbol]
887
+ if trade.trading_time() and today in self.trading_days:
888
+ if signal.action is not None:
889
+ action = (
890
+ signal.action.value
891
+ if isinstance(signal.action, TradeAction)
892
+ else signal.action
893
+ )
894
+ self._run_trade_algorithm(
895
+ action,
896
+ symbol,
897
+ signal.id,
898
+ trade,
899
+ signal.price,
900
+ signal.stoplimit,
901
+ buys,
902
+ sells,
903
+ signal.comment or self.comment,
904
+ )
905
+ else:
906
+ if len(self.symbols) >= 10:
907
+ if symbol == self.symbols[-1]:
900
908
  logger.info(
901
- f"Not trading Time !!! SYMBOL={trade.symbol}, STRATEGY={self.STRATEGY} , ACCOUNT={self.ACCOUNT}"
909
+ f"Not trading Time !!!, STRATEGY={self.STRATEGY} , ACCOUNT={self.ACCOUNT}"
902
910
  )
903
- self._check(buys[symbol], sells[symbol], symbol)
911
+ else:
912
+ logger.info(
913
+ f"Not trading Time !!! SYMBOL={trade.symbol}, STRATEGY={self.STRATEGY} , ACCOUNT={self.ACCOUNT}"
914
+ )
915
+ self._check(buys[symbol], sells[symbol], symbol)
904
916
 
905
917
  except Exception as e:
906
- msg = f"Handling Signals, SYMBOL={symbol}, STRATEGY={self.STRATEGY} , ACCOUNT={self.ACCOUNT}"
918
+ msg = (
919
+ f"Error handling signal for SYMBOL={signal.symbol} (SIGNAL: {action}), "
920
+ f"STRATEGY={self.STRATEGY}, ACCOUNT={self.ACCOUNT}"
921
+ )
907
922
  self._print_exc(msg, e)
908
- pass
923
+
924
+ def _handle_all_signals(self, today, signals, buys, sells, max_workers=50):
925
+ try:
926
+ check_mt5_connection(**self.kwargs)
927
+ except Exception as e:
928
+ msg = "Initial MT5 connection check failed. Aborting signal processing."
929
+ self._print_exc(msg, e)
930
+ return
931
+
932
+ if not signals:
933
+ logger.info("No signals to process.")
934
+ return
935
+
936
+ # We want to create a temporary function that
937
+ # already has the 'today', 'buys', and 'sells' arguments filled in.
938
+ # This is necessary because executor.map only iterates over one sequence (signals).
939
+ signal_processor = functools.partial(
940
+ self._handle_one_signal, today=today, buys=buys, sells=sells
941
+ )
942
+ if self.multithread:
943
+ with concurrent.futures.ThreadPoolExecutor(
944
+ max_workers=max_workers
945
+ ) as executor:
946
+ # 'map' will apply our worker function to every item in the 'signals' list.
947
+ # It will automatically manage the distribution of tasks to the worker threads.
948
+ # We wrap it in list() to ensure all tasks are complete before moving on.
949
+ list(executor.map(signal_processor, signals))
950
+ else:
951
+ for signal in signals:
952
+ try:
953
+ signal_processor(signal)
954
+ except Exception as e:
955
+ self._print_exc(f"Failed to process signal {signal}: ", e)
909
956
 
910
957
  def _handle_period_end_actions(self, today):
911
958
  try:
@@ -925,7 +972,7 @@ class Mt5ExecutionEngine:
925
972
  pass
926
973
 
927
974
  def run(self):
928
- while self._running:
975
+ while self._running and not self.shutdown_event.is_set():
929
976
  try:
930
977
  check_mt5_connection(**self.kwargs)
931
978
  positions_orders = self._check_positions_orders()
@@ -943,24 +990,27 @@ class Mt5ExecutionEngine:
943
990
  self._check(buys[symbol], sells[symbol], symbol)
944
991
  else:
945
992
  self._update_risk(weights)
946
- self._handle_signals(today, signals, buys, sells)
993
+ self._handle_all_signals(today, signals, buys, sells)
947
994
  self._sleep()
948
995
  self._handle_period_end_actions(today)
949
996
  except KeyboardInterrupt:
950
997
  logger.info(
951
998
  f"Stopping Execution Engine for {self.STRATEGY} STRATEGY on {self.ACCOUNT} Account"
952
999
  )
953
- break
1000
+ self.stop()
1001
+ sys.exit(0)
954
1002
  except Exception as e:
955
1003
  msg = f"Running Execution Engine, STRATEGY={self.STRATEGY} , ACCOUNT={self.ACCOUNT}"
956
1004
  self._print_exc(msg, e)
957
1005
 
958
1006
  def stop(self):
959
1007
  """Stops the execution engine."""
960
- self._running = False
961
- logger.info(
962
- f"Stopping Execution Engine for {self.STRATEGY} STRATEGY on {self.ACCOUNT} Account"
963
- )
1008
+ if self._running:
1009
+ logger.info(
1010
+ f"Stopping Execution Engine for {self.STRATEGY} STRATEGY on {self.ACCOUNT} Account"
1011
+ )
1012
+ self._running = False
1013
+ self.shutdown_event.set()
964
1014
  logger.info("Execution Engine stopped successfully.")
965
1015
 
966
1016
 
@@ -1008,6 +1058,7 @@ def RunMt5Engines(accounts: Dict[str, Dict], start_delay: float = 1.0):
1008
1058
 
1009
1059
  for account_id, params in accounts.items():
1010
1060
  log.info(f"Starting process for {account_id}")
1061
+ params["multithread"] = True
1011
1062
  process = mp.Process(target=RunMt5Engine, args=(account_id,), kwargs=params)
1012
1063
  process.start()
1013
1064
  processes[process] = account_id
bbstrader/tseries.py CHANGED
@@ -24,8 +24,6 @@ from statsmodels.tsa.vector_ar.var_model import VAR
24
24
  from statsmodels.tsa.vector_ar.vecm import coint_johansen
25
25
  from tqdm import tqdm
26
26
 
27
- warnings.filterwarnings("ignore")
28
-
29
27
 
30
28
  __all__ = [
31
29
  "run_kalman_filter",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bbstrader
3
- Version: 0.3.2
3
+ Version: 0.3.3
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/
@@ -380,7 +380,7 @@ You may need to create the `~/.bbstrader/` directory and its subdirectories (lik
380
380
 
381
381
  For comprehensive information, including detailed API references, tutorials, and advanced guides for each module, please refer to our full documentation hosted on ReadTheDocs:
382
382
 
383
- [**View the Full Documentation on ReadTheDocs**](https://bbstrader.readthedocs.io/en/latest/)
383
+ [**View the Full Documentation**](https://bbstrader.readthedocs.io/en/latest/)
384
384
 
385
385
  Additionally, the codebase is commented and includes docstrings, which can be a valuable resource for understanding the implementation details of specific functions and classes.
386
386
 
@@ -1,47 +1,47 @@
1
1
  bbstrader/__init__.py,sha256=4KVGBEYU3ao9zPVM3rMWqNuvCleCeA6C2MVe_AFc4rw,581
2
- bbstrader/__main__.py,sha256=pLg7yerYsz9kIQVu6EZXNxkFDS6OpY8BfwRAaj0niSw,2228
2
+ bbstrader/__main__.py,sha256=f6EsJGNrP1OuuNUyS5y_aMAVp4uDYjkHYxFuKwDh0dM,2233
3
3
  bbstrader/compat.py,sha256=djbHMvTvy0HYm1zyZ6Ttp_LMwP2PqTSVw1r7pqbz7So,487
4
4
  bbstrader/config.py,sha256=riZxwb4hN0I-dSsWcjnROc5dWQpSJ9iKOMIp4PMGfko,3970
5
- bbstrader/tseries.py,sha256=1gaS5lcn-7GqNasDNP_EficcLnshnb_pEz14eYT7Kzs,43868
5
+ bbstrader/tseries.py,sha256=SM_LTQHJ3ZXVkVJyZ51CefUDzJDl2TkJqBKMp_uM8s4,43833
6
6
  bbstrader/btengine/__init__.py,sha256=y1btjaEfhWsH8vuE7mBRpP9Tu-Azt9REhuVYsPCAfBU,2955
7
7
  bbstrader/btengine/backtest.py,sha256=o3eoCVzpjykDx-9VgkxK6DKZdGDAuLVeeWLCa2U_zeY,14652
8
- bbstrader/btengine/data.py,sha256=Ko1QfW8Mo2w7Nufv-pERB9NC6v9E61YzbS9b1EK-osg,27055
8
+ bbstrader/btengine/data.py,sha256=WcBLtabboZkQdtOQS3SjbiJD9BcWc75sdhZ2voQ_lUw,27061
9
9
  bbstrader/btengine/event.py,sha256=Ydl1avAXp9WAWOBXDAckcb9g1UkcnCO0rRzcJZwIq20,8714
10
10
  bbstrader/btengine/execution.py,sha256=4MytWjcKg8J_w14P43emHqsvKOElkQTfhVYNakU6euQ,11190
11
11
  bbstrader/btengine/performance.py,sha256=1ecWrTzHBQbk4ORvbTEKxwCzlL1brcXOEUwgbnjAwx4,12470
12
12
  bbstrader/btengine/portfolio.py,sha256=z98M65HQeCyma8gMZkAxspxBA9jtIhzxMyJUHPPj34c,16128
13
13
  bbstrader/btengine/scripts.py,sha256=8o66dq4Ex4DsH4s8xvJqUOFjLzZJSnbBvvNBzohtzoE,4837
14
- bbstrader/btengine/strategy.py,sha256=bH45muSk-lRaOKc8rPljJf3_XnVh3dSjihMwIJF9nYg,34319
14
+ bbstrader/btengine/strategy.py,sha256=MV50N46OPOV-xGLnO0aFp6drbh0A5e3vck_m4SFg7Sc,34400
15
15
  bbstrader/core/__init__.py,sha256=GIFzFSStPfE0XM2j7mDeZZQeMTh_AwPsDOQXwMVJLgw,97
16
- bbstrader/core/data.py,sha256=u8hhAF1Gf9fr_2Ci4mv7Smr1dt3E_KDAHXC3BFbis0Q,25055
17
- bbstrader/core/scripts.py,sha256=orPtnQlNNNFV-yfqFGRAIGFtWgilykZ3xyTx6d4DLsk,3963
16
+ bbstrader/core/data.py,sha256=5-ByClb-E3-iqDz8CBJ4om9wBIA7DmUWezu4A-tv5ys,25095
17
+ bbstrader/core/scripts.py,sha256=bMZ5I_hCTPCsAn9v9Sz9fQQ7JFkf_Zwtv7uUJwaqVBU,5466
18
18
  bbstrader/core/utils.py,sha256=WjuabzBjhY65ku2KL-f7CMalE2x-wrX-6mCA_qhhFPE,2728
19
19
  bbstrader/ibkr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
20
  bbstrader/ibkr/utils.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
21
  bbstrader/metatrader/__init__.py,sha256=A5Ye9tpc2sp9Xk5qjKw-EfYsoRcZtAt8nqvC3tCtZs8,333
22
- bbstrader/metatrader/account.py,sha256=isEqyPBwSw9TJe5zIMuZqPWabDX2WAmKzrZ_zCdwEuY,59633
22
+ bbstrader/metatrader/account.py,sha256=17HV2lv9K2xhuU0H1SFDQaVZycf8ZAWlwOalwiKj_S8,59688
23
23
  bbstrader/metatrader/analysis.py,sha256=pnZWdvEnf9wkPT0vhEIbZipnN5EUf6TaQftplYP9jRs,3564
24
- bbstrader/metatrader/copier.py,sha256=sh2j15Vw_q9JiIpoMkK1VzWzR0ke8h7Ce-ClgpVWd3I,49180
25
- bbstrader/metatrader/rates.py,sha256=6B-5AHguyDhicw3GqJ33FfFpdCKzxgUdlrHyPFuOLZg,20784
24
+ bbstrader/metatrader/copier.py,sha256=KFlBHguWAvxxlIKdnhs9o6y6OB8i7h-o2ZufMmQZbBA,54030
25
+ bbstrader/metatrader/rates.py,sha256=w9mr6FB6E1zLcHCDtDGt-oMnw6sakIU6Qe3455KDsSg,20782
26
26
  bbstrader/metatrader/risk.py,sha256=NhW8qtSg350Z6H9oLcDqOU_erqd_7Y7F5FwpfPN5Qso,27262
27
27
  bbstrader/metatrader/scripts.py,sha256=DQGwrzqOZLX4CVWM4Du73kc5k_8RxUaIkoc8rvPTt7c,3558
28
- bbstrader/metatrader/trade.py,sha256=pZRAJ9f0KQUQeguA4CsVct6DEOLpYKezaQHeO2I5Ipw,80624
29
- bbstrader/metatrader/utils.py,sha256=tNLiwXxFwe-bovXxS8zu6WLg-XOBrwEr84aga07yR-Y,19727
28
+ bbstrader/metatrader/trade.py,sha256=ezkALiUgtIu54R4m4blQjptWoRXNVG5wwuoctP2b90Y,80624
29
+ bbstrader/metatrader/utils.py,sha256=9RC1RqBqgIPiEloUYD5zhXBHkf7_oeOfzhqqYQoZXqY,19561
30
30
  bbstrader/models/__init__.py,sha256=B-bn2h_SCK6gRAs2li6dDVnvV8jDT5suZimldk5xxcw,497
31
31
  bbstrader/models/factors.py,sha256=Y1rjwhWU4aiSRd-jFOLnLZczFCY0bJUxauCo17HvOFY,12791
32
32
  bbstrader/models/ml.py,sha256=CxbasDRXzBF3rOF4ObpIjhJw6IZ9G3C3jwoC-o_BMyI,48809
33
- bbstrader/models/nlp.py,sha256=muJQqQGw4Gq-OmJA0vzhWHXqaehn4ezq2fmP_LQVySs,32800
33
+ bbstrader/models/nlp.py,sha256=hcvz9d_8j1cIC1h3oqa1DBjExRIEd6WSiZb95Vr3NPo,32638
34
34
  bbstrader/models/optimization.py,sha256=Fa4tdhynMmvKt5KHV9cH1TXmmJVJwU4QWpYkbeVq4aI,6395
35
35
  bbstrader/models/portfolio.py,sha256=r-47Zrn2r7iKCHm5YVtwkbBJXAZGM3QYy-rXCWY9-Bg,8079
36
36
  bbstrader/models/risk.py,sha256=MKCk53HtGIcivrNzH8Ikm5KMs1rXhFT5zkorUf30PyQ,506
37
37
  bbstrader/trading/__init__.py,sha256=ycLyuuxN5SujqtzR9X0Q74UQfK93q2va-GGAXdr-KS8,457
38
- bbstrader/trading/execution.py,sha256=53WrgCR8qHMeZZfZ_IwSn-Du7GKWalVDncOustaBROQ,39220
38
+ bbstrader/trading/execution.py,sha256=nAxb9EDPNF3g8I5C5vz6e9k0xF5CNNLHmKlHgck0-rY,41460
39
39
  bbstrader/trading/scripts.py,sha256=Tf5q33WqqygjpIv43_8nA82VZ3GM0qgb4Ggo3fHJ_wg,5744
40
40
  bbstrader/trading/strategies.py,sha256=RZ6P4SfIyRW72v0OnPnrc4Hv8X00FdxR-_sD23xe_Pg,11756
41
41
  bbstrader/trading/utils.py,sha256=57dKF9dcRu04oU2VRqydRrzW39dCW2wlDWhVt-sZdRw,1857
42
- bbstrader-0.3.2.dist-info/licenses/LICENSE,sha256=ZwC_RqqGmOPBUiMDKqLyJZ5HBeHq53LpL7TMRzrJY8c,1094
43
- bbstrader-0.3.2.dist-info/METADATA,sha256=koetow8qNITciM8k0Urf-lLd0_t3h-XPWKqO0dy9ZeA,27121
44
- bbstrader-0.3.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
45
- bbstrader-0.3.2.dist-info/entry_points.txt,sha256=0yDCbhbgHswOzJnY5wRSM_FjjyMHGvY7lJpSSVh0xtI,54
46
- bbstrader-0.3.2.dist-info/top_level.txt,sha256=Wwj322jZmxGZ6gD_TdaPiPLjED5ReObm5omerwlmZIg,10
47
- bbstrader-0.3.2.dist-info/RECORD,,
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,,