bbstrader 0.3.2__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.

@@ -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):
@@ -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:
@@ -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()
@@ -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.
@@ -486,11 +550,7 @@ class MT5TerminalError(Exception):
486
550
  self.code = code
487
551
  self.message = message
488
552
 
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}"
553
+ def __repr__(self) -> str:
494
554
  msg_str = str(self.message) if self.message is not None else ""
495
555
  return f"{self.code} - {self.__class__.__name__}: {msg_str}"
496
556
 
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
 
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,
@@ -187,8 +192,8 @@ class Mt5ExecutionEngine:
187
192
  show_positions_orders: bool = False,
188
193
  iter_time: int | float = 5,
189
194
  use_trade_time: bool = True,
190
- period: Literal["24/7", "day", "week", "month"] = "week",
191
- period_end_action: Literal["break", "sleep"] = "break",
195
+ period: Literal["24/7", "day", "week", "month"] = "month",
196
+ period_end_action: Literal["break", "sleep"] = "sleep",
192
197
  closing_pnl: Optional[float] = None,
193
198
  trading_days: Optional[List[str]] = None,
194
199
  comment: Optional[str] = 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__)
@@ -617,12 +631,12 @@ class Mt5ExecutionEngine:
617
631
  def _update_risk(self, weights):
618
632
  try:
619
633
  check_mt5_connection(**self.kwargs)
620
- 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
621
636
  for symbol in self.symbols:
622
637
  if symbol not in weights:
623
638
  continue
624
639
  trade = self.trades_instances[symbol]
625
- assert self.daily_risk is not None
626
640
  dailydd = round(weights[symbol] * self.daily_risk, 5)
627
641
  trade.dailydd = dailydd
628
642
  except Exception as e:
@@ -866,46 +880,78 @@ 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
+ return
934
+
935
+ # We want to create a temporary function that
936
+ # already has the 'today', 'buys', and 'sells' arguments filled in.
937
+ # This is necessary because executor.map only iterates over one sequence (signals).
938
+ signal_processor = functools.partial(
939
+ self._handle_one_signal, today=today, buys=buys, sells=sells
940
+ )
941
+ if self.multithread:
942
+ with concurrent.futures.ThreadPoolExecutor(
943
+ max_workers=max_workers
944
+ ) as executor:
945
+ # 'map' will apply our worker function to every item in the 'signals' list.
946
+ # It will automatically manage the distribution of tasks to the worker threads.
947
+ # We wrap it in list() to ensure all tasks are complete before moving on.
948
+ list(executor.map(signal_processor, signals))
949
+ else:
950
+ for signal in signals:
951
+ try:
952
+ signal_processor(signal)
953
+ except Exception as e:
954
+ self._print_exc(f"Failed to process signal {signal}: ", e)
909
955
 
910
956
  def _handle_period_end_actions(self, today):
911
957
  try:
@@ -925,7 +971,7 @@ class Mt5ExecutionEngine:
925
971
  pass
926
972
 
927
973
  def run(self):
928
- while self._running:
974
+ while self._running and not self.shutdown_event.is_set():
929
975
  try:
930
976
  check_mt5_connection(**self.kwargs)
931
977
  positions_orders = self._check_positions_orders()
@@ -943,24 +989,25 @@ class Mt5ExecutionEngine:
943
989
  self._check(buys[symbol], sells[symbol], symbol)
944
990
  else:
945
991
  self._update_risk(weights)
946
- self._handle_signals(today, signals, buys, sells)
992
+ self._handle_all_signals(today, signals, buys, sells)
947
993
  self._sleep()
948
994
  self._handle_period_end_actions(today)
949
995
  except KeyboardInterrupt:
950
- logger.info(
951
- f"Stopping Execution Engine for {self.STRATEGY} STRATEGY on {self.ACCOUNT} Account"
952
- )
953
- break
996
+ self.stop()
997
+ sys.exit(0)
954
998
  except Exception as e:
955
999
  msg = f"Running Execution Engine, STRATEGY={self.STRATEGY} , ACCOUNT={self.ACCOUNT}"
956
1000
  self._print_exc(msg, e)
1001
+ self._sleep()
957
1002
 
958
1003
  def stop(self):
959
1004
  """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
- )
1005
+ if self._running:
1006
+ logger.info(
1007
+ f"Stopping Execution Engine for {self.STRATEGY} STRATEGY on {self.ACCOUNT} Account"
1008
+ )
1009
+ self._running = False
1010
+ self.shutdown_event.set()
964
1011
  logger.info("Execution Engine stopped successfully.")
965
1012
 
966
1013
 
@@ -988,6 +1035,10 @@ def RunMt5Engine(account_id: str, **kwargs):
988
1035
  symbol_list, trades_instances, strategy_cls, **kwargs
989
1036
  )
990
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)
991
1042
  except Exception as e:
992
1043
  log.exception(f"Error running execution engine for {account_id}: {e}")
993
1044
  finally:
@@ -1008,6 +1059,7 @@ def RunMt5Engines(accounts: Dict[str, Dict], start_delay: float = 1.0):
1008
1059
 
1009
1060
  for account_id, params in accounts.items():
1010
1061
  log.info(f"Starting process for {account_id}")
1062
+ params["multithread"] = True
1011
1063
  process = mp.Process(target=RunMt5Engine, args=(account_id,), kwargs=params)
1012
1064
  process.start()
1013
1065
  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.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
 
@@ -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,49 @@
1
1
  bbstrader/__init__.py,sha256=4KVGBEYU3ao9zPVM3rMWqNuvCleCeA6C2MVe_AFc4rw,581
2
- bbstrader/__main__.py,sha256=pLg7yerYsz9kIQVu6EZXNxkFDS6OpY8BfwRAaj0niSw,2228
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
- bbstrader/tseries.py,sha256=1gaS5lcn-7GqNasDNP_EficcLnshnb_pEz14eYT7Kzs,43868
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
- bbstrader/btengine/data.py,sha256=Ko1QfW8Mo2w7Nufv-pERB9NC6v9E61YzbS9b1EK-osg,27055
10
+ bbstrader/btengine/data.py,sha256=WcBLtabboZkQdtOQS3SjbiJD9BcWc75sdhZ2voQ_lUw,27061
9
11
  bbstrader/btengine/event.py,sha256=Ydl1avAXp9WAWOBXDAckcb9g1UkcnCO0rRzcJZwIq20,8714
10
12
  bbstrader/btengine/execution.py,sha256=4MytWjcKg8J_w14P43emHqsvKOElkQTfhVYNakU6euQ,11190
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=bH45muSk-lRaOKc8rPljJf3_XnVh3dSjihMwIJF9nYg,34319
16
+ bbstrader/btengine/strategy.py,sha256=hDghr5sXNitWlWXXvgl8Vj-QHWaIuVlx54LUXAbQrHQ,36725
15
17
  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
18
- bbstrader/core/utils.py,sha256=WjuabzBjhY65ku2KL-f7CMalE2x-wrX-6mCA_qhhFPE,2728
18
+ bbstrader/core/data.py,sha256=5-ByClb-E3-iqDz8CBJ4om9wBIA7DmUWezu4A-tv5ys,25095
19
+ bbstrader/core/scripts.py,sha256=bMZ5I_hCTPCsAn9v9Sz9fQQ7JFkf_Zwtv7uUJwaqVBU,5466
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=isEqyPBwSw9TJe5zIMuZqPWabDX2WAmKzrZ_zCdwEuY,59633
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/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
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
28
- bbstrader/metatrader/trade.py,sha256=pZRAJ9f0KQUQeguA4CsVct6DEOLpYKezaQHeO2I5Ipw,80624
29
- bbstrader/metatrader/utils.py,sha256=tNLiwXxFwe-bovXxS8zu6WLg-XOBrwEr84aga07yR-Y,19727
29
+ bbstrader/metatrader/scripts.py,sha256=8meq6_zz6jPSibNgtYtaO8Ba-uJZOoLkpqYUIjidk-U,4010
30
+ bbstrader/metatrader/trade.py,sha256=ezkALiUgtIu54R4m4blQjptWoRXNVG5wwuoctP2b90Y,80624
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
33
- bbstrader/models/nlp.py,sha256=muJQqQGw4Gq-OmJA0vzhWHXqaehn4ezq2fmP_LQVySs,32800
34
+ bbstrader/models/ml.py,sha256=d_maEXwGOGuWawisjIbMO5FUsaSjlgmJu5XiMu28Wf8,48921
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=53WrgCR8qHMeZZfZ_IwSn-Du7GKWalVDncOustaBROQ,39220
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.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,,
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,,