bbstrader 0.2.98__py3-none-any.whl → 0.2.99__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,10 +1,10 @@
1
1
  import multiprocessing as mp
2
2
  import time
3
- from datetime import datetime
3
+ from datetime import date, datetime
4
4
  from typing import Dict, List, Literal, Optional
5
5
 
6
+ import pandas as pd
6
7
  from loguru import logger as log
7
- from logging import Logger
8
8
 
9
9
  from bbstrader.btengine.strategy import MT5Strategy, Strategy
10
10
  from bbstrader.config import BBSTRADER_DIR
@@ -18,7 +18,7 @@ except ImportError:
18
18
  import bbstrader.compat # noqa: F401
19
19
 
20
20
 
21
- __all__ = ["Mt5ExecutionEngine", "TWSExecutionEngine"]
21
+ __all__ = ["Mt5ExecutionEngine", "RunMt5Engine", "RunMt5Engines", "TWSExecutionEngine"]
22
22
 
23
23
  _TF_MAPPING = {
24
24
  "1m": 1,
@@ -38,6 +38,7 @@ _TF_MAPPING = {
38
38
 
39
39
  TradingDays = ["monday", "tuesday", "wednesday", "thursday", "friday"]
40
40
  WEEK_DAYS = TradingDays + ["saturday", "sunday"]
41
+ FRIDAY = "friday"
41
42
 
42
43
  BUYS = ["BMKT", "BLMT", "BSTP", "BSTPLMT"]
43
44
  SELLS = ["SMKT", "SLMT", "SSTP", "SSTPLMT"]
@@ -181,12 +182,12 @@ class Mt5ExecutionEngine:
181
182
  show_positions_orders: bool = False,
182
183
  iter_time: int | float = 5,
183
184
  use_trade_time: bool = True,
184
- period: Literal["day", "week", "month"] = "week",
185
+ period: Literal["24/7", "day", "week", "month"] = "week",
185
186
  period_end_action: Literal["break", "sleep"] = "break",
186
187
  closing_pnl: Optional[float] = None,
187
- trading_days: Optional[List[str]] = TradingDays,
188
+ trading_days: Optional[List[str]] = None,
188
189
  comment: Optional[str] = None,
189
- **kwargs,
190
+ **kwargs
190
191
  ):
191
192
  """
192
193
  Args:
@@ -199,8 +200,8 @@ class Mt5ExecutionEngine:
199
200
  show_positions_orders : Print open positions and orders. Defaults to False.
200
201
  iter_time : Interval to check for signals and `mm`. Defaults to 5.
201
202
  use_trade_time : Open trades after the time is completed. Defaults to True.
202
- period : Period to trade. Defaults to 'week'.
203
- period_end_action : Action to take at the end of the period. Defaults to 'break',
203
+ period : Period to trade ("24/7", "day", "week", "month"). Defaults to 'week'.
204
+ period_end_action : Action to take at the end of the period ("break", "sleep"). Defaults to 'break',
204
205
  this only applies when period is 'day', 'week'.
205
206
  closing_pnl : Minimum profit in percentage of target profit to close positions. Defaults to -0.001.
206
207
  trading_days : Trading days in a week. Defaults to monday to friday.
@@ -213,11 +214,12 @@ class Mt5ExecutionEngine:
213
214
  - telegram (bool): Enable telegram notifications. Defaults to False.
214
215
  - bot_token (str): Telegram bot token. Defaults to None.
215
216
  - chat_id (Union[int, str, List] ): Telegram chat id. Defaults to None.
217
+ - MT5 connection arguments.
216
218
 
217
219
  Note:
218
220
  1. For `trail` , `stop_trail` , `trail_after_points` , `be_plus_points` see `bbstrader.metatrader.trade.Trade.break_even()` .
219
- 2. All Strategies must inherit from `bbstrader.btengine.strategy.Strategy` or `bbstrader.btengine.strategy.MT5Strategy` class
220
- and have a `calculate_signals` method that returns a dictionary of signals for each symbol in symbol_list.
221
+ 2. All Strategies must inherit from `bbstrader.btengine.strategy.MT5Strategy` class
222
+ and have a `calculate_signals` method that returns a List of ``bbstrader.metatrader.trade.TradingSignal``.
221
223
 
222
224
  3. All strategies must have the following arguments in their `__init__` method:
223
225
  - bars (DataHandler): DataHandler instance default to None
@@ -245,14 +247,12 @@ class Mt5ExecutionEngine:
245
247
  self.show_positions_orders = show_positions_orders
246
248
  self.iter_time = iter_time
247
249
  self.use_trade_time = use_trade_time
248
- self.period = period
250
+ self.period = period.strip()
249
251
  self.period_end_action = period_end_action
250
252
  self.closing_pnl = closing_pnl
251
- self.trading_days = trading_days
252
253
  self.comment = comment
253
254
  self.kwargs = kwargs
254
255
 
255
- self.num_days = 0
256
256
  self.time_intervals = 0
257
257
  self.time_frame = kwargs.get("time_frame", "15m")
258
258
  self.trade_time = _TF_MAPPING[self.time_frame]
@@ -270,7 +270,7 @@ class Mt5ExecutionEngine:
270
270
 
271
271
  def _initialize_engine(self, **kwargs):
272
272
  global logger
273
- logger: Logger = kwargs.get("logger", log)
273
+ logger = kwargs.get("logger", log)
274
274
  try:
275
275
  self.daily_risk = kwargs.get("daily_risk")
276
276
  self.notify = kwargs.get("notify", False)
@@ -280,16 +280,19 @@ class Mt5ExecutionEngine:
280
280
  self.STRATEGY = kwargs.get("strategy_name")
281
281
  self.ACCOUNT = kwargs.get("account", "MT5 Account")
282
282
  self.signal_tickers = kwargs.get("signal_tickers", self.symbols)
283
- self.FRIDAY = "friday"
284
283
 
285
284
  self.expert_ids = self._expert_ids(kwargs.get("expert_ids"))
286
285
  self.max_trades = self._max_trades(kwargs.get("max_trades"))
287
286
  if self.comment is None:
288
287
  trade = self.trades_instances[self.symbols[0]]
289
288
  self.comment = f"{trade.expert_name}@{trade.version}"
290
-
291
- if self.period.lower() == "24/7":
292
- self.trading_days = WEEK_DAYS
289
+ if kwargs.get("trading_days") is None:
290
+ if self.period.lower() == "24/7":
291
+ self.trading_days = WEEK_DAYS
292
+ else:
293
+ self.trading_days = TradingDays
294
+ else:
295
+ self.trading_days = kwargs.get("trading_days")
293
296
  except Exception as e:
294
297
  self._print_exc(
295
298
  f"Initializing Execution Engine, STRATEGY={self.STRATEGY}, ACCOUNT={self.ACCOUNT}",
@@ -440,24 +443,6 @@ class Mt5ExecutionEngine:
440
443
  {positions_orders[order_type][symbol]}, STRATEGY={self.STRATEGY} , ACCOUNT={self.ACCOUNT}"
441
444
  )
442
445
 
443
- def _update_risk(self, weights):
444
- try:
445
- check_mt5_connection(**self.kwargs)
446
- if weights is not None:
447
- for symbol in self.symbols:
448
- if symbol not in weights:
449
- continue
450
- trade = self.trades_instances[symbol]
451
- assert self.daily_risk is not None
452
- dailydd = round(weights[symbol] * self.daily_risk, 5)
453
- trade.dailydd = dailydd
454
- except Exception as e:
455
- self._print_exc(
456
- f"Updating Risk, STRATEGY={self.STRATEGY} , ACCOUNT={self.ACCOUNT}",
457
- e,
458
- )
459
- pass
460
-
461
446
  def _send_notification(self, signal, symbol):
462
447
  telegram = self.kwargs.get("telegram", False)
463
448
  bot_token = self.kwargs.get("bot_token")
@@ -488,6 +473,73 @@ class Mt5ExecutionEngine:
488
473
  def _sleepmsg(self, sleep_time):
489
474
  logger.info(f"{self.ACCOUNT} Sleeping for {sleep_time} minutes ...\n")
490
475
 
476
+ def _sleep_over_night(self, sessionmsg):
477
+ sleep_time = self.trades_instances[self.symbols[-1]].sleep_time()
478
+ self._sleepmsg(sleep_time + self.delay)
479
+ time.sleep(60 * sleep_time + self.delay)
480
+ logger.info(sessionmsg)
481
+
482
+ def _sleep_over_weekend(self, sessionmsg):
483
+ sleep_time = self.trades_instances[self.symbols[-1]].sleep_time(weekend=True)
484
+ self._sleepmsg(sleep_time + self.delay)
485
+ time.sleep(60 * sleep_time + self.delay)
486
+ logger.info(sessionmsg)
487
+
488
+ def _check_is_day_ends(self, trade: Trade, symbol, period_type, today, closing):
489
+ if trade.days_end():
490
+ self._logmsgif("Day", symbol) if today != FRIDAY else self._logmsgif(
491
+ "Week", symbol
492
+ )
493
+ if (
494
+ (
495
+ period_type == "month"
496
+ and today == FRIDAY
497
+ and self._is_month_ends()
498
+ and closing
499
+ )
500
+ or (period_type == "week" and today == FRIDAY and closing)
501
+ or (period_type == "day" and closing)
502
+ or (period_type == "24/7" and closing)
503
+ ):
504
+ for id in self.expert_ids:
505
+ trade.close_positions(
506
+ position_type="all", id=id, comment=self.comment
507
+ )
508
+ trade.statistics(save=True)
509
+
510
+ def _is_month_ends(self):
511
+ today = pd.Timestamp(date.today())
512
+ last_business_day = today + pd.tseries.offsets.BMonthEnd(0)
513
+ return today == last_business_day
514
+
515
+ def _daily_end_checks(self, today, closing, sessionmsg):
516
+ self.strategy.perform_period_end_checks()
517
+ if self.period_end_action == "break" and closing:
518
+ exit(0)
519
+ elif self.period_end_action == "sleep" and today != FRIDAY or not closing:
520
+ self._sleep_over_night(sessionmsg)
521
+ elif self.period_end_action == "sleep" and today == FRIDAY:
522
+ self._sleep_over_weekend(sessionmsg)
523
+
524
+ def _weekly_end_checks(self, today, closing, sessionmsg):
525
+ if today != FRIDAY:
526
+ self._sleep_over_night(sessionmsg)
527
+ elif today == FRIDAY:
528
+ self.strategy.perform_period_end_checks()
529
+ if self.period_end_action == "break" and closing:
530
+ exit(0)
531
+ elif self.period_end_action == "sleep" or not closing:
532
+ self._sleep_over_weekend(sessionmsg)
533
+
534
+ def _monthly_end_cheks(self, today, closing, sessionmsg):
535
+ if today != FRIDAY:
536
+ self._sleep_over_night(sessionmsg)
537
+ elif today == FRIDAY and self._is_month_ends() and closing:
538
+ self.strategy.perform_period_end_checks()
539
+ exit(0)
540
+ else:
541
+ self._sleep_over_weekend(sessionmsg)
542
+
491
543
  def _perform_period_end_actions(
492
544
  self,
493
545
  today,
@@ -495,107 +547,27 @@ class Mt5ExecutionEngine:
495
547
  closing,
496
548
  sessionmsg,
497
549
  ):
498
- period_type = self.period.lower()
499
- for symbol in self.symbols:
500
- trade = self.trades_instances[symbol]
501
-
502
- if trade.days_end():
503
- if period_type in ["weekly", "monthly"] and today != self.FRIDAY:
504
- self._logmsgif("Day", symbol)
505
- elif period_type in ["weekly", "monthly"] and today == self.FRIDAY:
506
- self._logmsgif("Week", symbol)
507
-
508
- if (
509
- (
510
- period_type == "monthly"
511
- and today == self.FRIDAY
512
- and self.num_days >= 20
513
- and closing
514
- )
515
- or (period_type == "weekly" and today == self.FRIDAY and closing)
516
- or (period_type == "daily" and closing)
517
- or (period_type == "24/7" and closing)
518
- ):
519
- for id in self.expert_ids:
520
- trade.close_positions(
521
- position_type="all", id=id, comment=self.comment
522
- )
523
-
524
- if period_type == "monthly":
525
- self._logmsgif("Month", symbol)
526
- elif period_type == "weekly":
527
- self._logmsgif("Week", symbol)
528
- else:
529
- self._logmsgif("Day", symbol)
530
-
531
- trade.statistics(save=True)
550
+ period = self.period.lower()
551
+ for symbol, trade in self.trades_instances.items():
552
+ self._check_is_day_ends(trade, symbol, period, today, closing)
532
553
 
533
554
  if day_end:
534
- if period_type == "24/7":
535
- self.strategy.perform_period_end_checks()
536
- sleep_time = self.trades_instances[self.symbols[-1]].sleep_time()
537
- self._sleepmsg(sleep_time + self.delay)
538
- time.sleep(60 * sleep_time + self.delay)
539
- logger.info(sessionmsg)
540
-
541
- elif period_type == "daily":
542
- self.strategy.perform_period_end_checks()
543
- if self.period_end_action == "break" and closing:
544
- exit(0)
545
- elif (
546
- self.period_end_action == "sleep"
547
- and today != self.FRIDAY
548
- or not closing
549
- ):
550
- sleep_time = self.trades_instances[self.symbols[-1]].sleep_time()
551
- self._sleepmsg(sleep_time + self.delay)
552
- time.sleep(60 * sleep_time + self.delay)
553
- logger.info(sessionmsg)
554
- elif self.period_end_action == "sleep" and today == self.FRIDAY:
555
- sleep_time = self.trades_instances[self.symbols[-1]].sleep_time(
556
- weekend=True
557
- )
558
- self._sleepmsg(sleep_time + self.delay)
559
- time.sleep(60 * sleep_time + self.delay)
560
- logger.info(sessionmsg)
561
-
562
- elif period_type == "weekly":
563
- if today != self.FRIDAY:
564
- sleep_time = self.trades_instances[self.symbols[-1]].sleep_time()
565
- self._sleepmsg(sleep_time + self.delay)
566
- time.sleep(60 * sleep_time + self.delay)
567
- logger.info(sessionmsg)
568
- elif today == self.FRIDAY:
569
- self.strategy.perform_period_end_checks()
570
- if self.period_end_action == "break" and closing:
571
- exit(0)
572
- elif self.period_end_action == "sleep" or not closing:
573
- sleep_time = self.trades_instances[self.symbols[-1]].sleep_time(
574
- weekend=True
575
- )
576
- self._sleepmsg(sleep_time + self.delay)
577
- time.sleep(60 * sleep_time + self.delay)
578
- logger.info(sessionmsg)
579
-
580
- elif period_type == "monthly":
581
- if today != self.FRIDAY:
582
- sleep_time = self.trades_instances[self.symbols[-1]].sleep_time()
583
- self._sleepmsg(sleep_time + self.delay)
584
- time.sleep(60 * sleep_time + self.delay)
585
- logger.info(sessionmsg)
586
- elif today == self.FRIDAY:
555
+ self.time_intervals = 0
556
+ match period:
557
+ case "24/7":
587
558
  self.strategy.perform_period_end_checks()
588
- if self.period_end_action == "break" and closing:
589
- exit(0)
590
- elif self.period_end_action == "sleep" or not closing:
591
- sleep_time = self.trades_instances[self.symbols[-1]].sleep_time(
592
- weekend=True
593
- )
594
- self._sleepmsg(sleep_time + self.delay)
595
- time.sleep(60 * sleep_time + self.delay)
596
- logger.info(sessionmsg)
559
+ self._sleep_over_night(sessionmsg)
597
560
 
598
- self.time_intervals = 0
561
+ case "day":
562
+ self._daily_end_checks(today, closing, sessionmsg)
563
+
564
+ case "week":
565
+ self._weekly_end_checks(today, closing, sessionmsg)
566
+
567
+ case "month":
568
+ self._monthly_end_cheks(today, closing, sessionmsg)
569
+ case _:
570
+ raise ValueError(f"Invalid period {period}")
599
571
 
600
572
  def _check(self, buys, sells, symbol):
601
573
  if not self.mm:
@@ -609,6 +581,41 @@ class Mt5ExecutionEngine:
609
581
  be_plus_points=self.be_plus_points,
610
582
  )
611
583
 
584
+ def _get_signals_and_weights(self):
585
+ try:
586
+ check_mt5_connection(**self.kwargs)
587
+ signals = self.strategy.calculate_signals()
588
+ weights = (
589
+ self.strategy.apply_risk_management(self.optimizer)
590
+ if hasattr(self.strategy, "apply_risk_management")
591
+ else None
592
+ )
593
+ return signals, weights
594
+ except Exception as e:
595
+ self._print_exc(
596
+ f"Calculating Signals, STRATEGY={self.STRATEGY} , ACCOUNT={self.ACCOUNT}",
597
+ e,
598
+ )
599
+ pass
600
+
601
+ def _update_risk(self, weights):
602
+ try:
603
+ check_mt5_connection(**self.kwargs)
604
+ if weights is not None:
605
+ for symbol in self.symbols:
606
+ if symbol not in weights:
607
+ continue
608
+ trade = self.trades_instances[symbol]
609
+ assert self.daily_risk is not None
610
+ dailydd = round(weights[symbol] * self.daily_risk, 5)
611
+ trade.dailydd = dailydd
612
+ except Exception as e:
613
+ self._print_exc(
614
+ f"Updating Risk, STRATEGY={self.STRATEGY} , ACCOUNT={self.ACCOUNT}",
615
+ e,
616
+ )
617
+ pass
618
+
612
619
  def _open_buy(
613
620
  self, signal, symbol, id, trade: Trade, price, stoplimit, sigmsg, msg, comment
614
621
  ):
@@ -822,23 +829,6 @@ class Mt5ExecutionEngine:
822
829
  f"(e.g., if time_frame is 15m, iter_time must be 1.5, 3, 5, 15 etc)"
823
830
  )
824
831
 
825
- def _get_signals_and_weights(self):
826
- try:
827
- check_mt5_connection(**self.kwargs)
828
- signals = self.strategy.calculate_signals()
829
- weights = (
830
- self.strategy.apply_risk_management(self.optimizer)
831
- if hasattr(self.strategy, "apply_risk_management")
832
- else None
833
- )
834
- return signals, weights
835
- except Exception as e:
836
- self._print_exc(
837
- f"Calculating Signals, STRATEGY={self.STRATEGY} , ACCOUNT={self.ACCOUNT}",
838
- e,
839
- )
840
- pass
841
-
842
832
  def _handle_signals(self, today, signals, buys, sells):
843
833
  try:
844
834
  check_mt5_connection(**self.kwargs)
@@ -892,9 +882,6 @@ class Mt5ExecutionEngine:
892
882
  closing,
893
883
  sessionmsg,
894
884
  )
895
- except KeyboardInterrupt:
896
- logger.info("Stopping the Execution Engine ...")
897
- quit()
898
885
  except Exception as e:
899
886
  msg = f"Handling period end actions, STRATEGY={self.STRATEGY} , ACCOUNT={self.ACCOUNT}"
900
887
  self._print_exc(msg, e)
@@ -907,18 +894,18 @@ class Mt5ExecutionEngine:
907
894
  positions_orders = self._check_positions_orders()
908
895
  if self.show_positions_orders:
909
896
  self._display_positions_orders(positions_orders)
910
- buys = positions_orders["buys"]
911
- sells = positions_orders["sells"]
897
+ buys = positions_orders.get("buys")
898
+ sells = positions_orders.get("sells")
912
899
  self.long_market, self.short_market = self._long_short_market(
913
900
  buys, sells
914
901
  )
915
- signals, weights = self._get_signals_and_weights()
916
- self._update_risk(weights)
917
902
  today = datetime.now().strftime("%A").lower()
903
+ signals, weights = self._get_signals_and_weights()
918
904
  if len(signals) == 0:
919
905
  for symbol in self.symbols:
920
906
  self._check(buys[symbol], sells[symbol], symbol)
921
907
  else:
908
+ self._update_risk(weights)
922
909
  self._handle_signals(today, signals, buys, sells)
923
910
  self._sleep()
924
911
  self._handle_period_end_actions(today)
@@ -1,6 +1,14 @@
1
1
  """
2
2
  Strategies module for trading strategies backtesting and execution.
3
3
 
4
+ # NOTE
5
+ These strategies inherit from the Strategy class, not from MT5Strategy, because we chose to demonstrate the modular approach to building and backtesting strategies using the bbstrader framework.
6
+
7
+ If these strategies need to be sent to the Mt5ExecutionEngine,
8
+ they must return signals as a list of bbstrader.metatrader.trade.TradingSignal objects.
9
+
10
+ Later, we will implement the Execution Engine for the Interactive Brokers TWS platform.
11
+
4
12
  DISCLAIMER:
5
13
  This module is for educational purposes only and should not be
6
14
  considered as financial advice. Always consult with a qualified financial advisor before making any investment decisions.
@@ -18,7 +26,7 @@ import yfinance as yf
18
26
 
19
27
  from bbstrader.btengine.backtest import BacktestEngine
20
28
  from bbstrader.btengine.data import DataHandler, MT5DataHandler, YFDataHandler
21
- from bbstrader.btengine.event import SignalEvent
29
+ from bbstrader.btengine.event import Events, SignalEvent
22
30
  from bbstrader.btengine.execution import MT5ExecutionHandler, SimExecutionHandler
23
31
  from bbstrader.btengine.strategy import Strategy
24
32
  from bbstrader.metatrader.account import Account
@@ -189,7 +197,7 @@ class SMAStrategy(Strategy):
189
197
 
190
198
  def calculate_signals(self, event=None):
191
199
  if self.mode == "backtest" and event is not None:
192
- if event.type == "MARKET":
200
+ if event.type == Events.MARKET:
193
201
  signals = self.create_backtest_signals()
194
202
  for signal in signals.values():
195
203
  if signal is not None:
@@ -377,7 +385,7 @@ class ArimaGarchStrategy(Strategy):
377
385
 
378
386
  def calculate_signals(self, event=None):
379
387
  if self.mode == "backtest" and event is not None:
380
- if event.type == "MARKET":
388
+ if event.type == Events.MARKET:
381
389
  signals = self.create_backtest_signal()
382
390
  for signal in signals.values():
383
391
  if signal is not None:
@@ -560,7 +568,7 @@ class KalmanFilterStrategy(Strategy):
560
568
  Calculate the Kalman Filter strategy.
561
569
  """
562
570
  if self.mode == "backtest" and event is not None:
563
- if event.type == "MARKET":
571
+ if event.type == Events.MARKET:
564
572
  self.calculate_backtest_signals()
565
573
  elif self.mode == "live":
566
574
  return self.calculate_live_signals()
@@ -744,7 +752,7 @@ class StockIndexSTBOTrading(Strategy):
744
752
 
745
753
  def calculate_signals(self, event=None) -> Dict[str, Union[str, None]]:
746
754
  if self.mode == "backtest" and event is not None:
747
- if event.type == "MARKET":
755
+ if event.type == Events.MARKET:
748
756
  self.calculate_backtest_signals()
749
757
  elif self.mode == "live":
750
758
  return self.calculate_live_signals()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bbstrader
3
- Version: 0.2.98
3
+ Version: 0.2.99
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/
@@ -4,42 +4,43 @@ bbstrader/compat.py,sha256=djbHMvTvy0HYm1zyZ6Ttp_LMwP2PqTSVw1r7pqbz7So,487
4
4
  bbstrader/config.py,sha256=c2nCUw-bYWf5kkyFls5Nqld8HdMczexSilTni7rYUBw,3973
5
5
  bbstrader/tseries.py,sha256=H4D_A966HdN8YjBfuCcF8QBQdhjOrTcidR98wP2KN_I,68339
6
6
  bbstrader/btengine/__init__.py,sha256=y1btjaEfhWsH8vuE7mBRpP9Tu-Azt9REhuVYsPCAfBU,2955
7
- bbstrader/btengine/backtest.py,sha256=ZzGhoN-_g0cF-OCyk173imze2OXEhykHTUiJ9MowDO8,14582
7
+ bbstrader/btengine/backtest.py,sha256=UiOmtqYSh72KZz0DCXz8iKrqTCOkx9Er3XjqK6H9Do8,14765
8
8
  bbstrader/btengine/data.py,sha256=Tuc6M8itbGpPjsfRpZBB8v0FJpPt7-hUkP6I5meP0Sg,26927
9
- bbstrader/btengine/event.py,sha256=38mhZH9d53C4x7bZER2B0O6M18txzS4u7zveKyxeP5Y,8603
10
- bbstrader/btengine/execution.py,sha256=mXY0tyFv6G1XF48R5Kx2-pDnwu1mUyOc0ZeeysQG62A,10587
9
+ bbstrader/btengine/event.py,sha256=gC2nqWU_t1pRzg4DYYey-mbaBo8T_JOPzzAzRF53U7o,8757
10
+ bbstrader/btengine/execution.py,sha256=tN7QIx0PHiQmJcn3MdSQiwwAYCDxFDn3C4i5tlM5xoY,10605
11
11
  bbstrader/btengine/performance.py,sha256=1ecWrTzHBQbk4ORvbTEKxwCzlL1brcXOEUwgbnjAwx4,12470
12
- bbstrader/btengine/portfolio.py,sha256=mh2_zNJDmKzb0lo55PXhbXYxXMmXRA4YLkgzwxRMuZE,16110
12
+ bbstrader/btengine/portfolio.py,sha256=z98M65HQeCyma8gMZkAxspxBA9jtIhzxMyJUHPPj34c,16128
13
13
  bbstrader/btengine/scripts.py,sha256=8o66dq4Ex4DsH4s8xvJqUOFjLzZJSnbBvvNBzohtzoE,4837
14
- bbstrader/btengine/strategy.py,sha256=147_i9mnYo1RUMpF6QqxXRhE6cuHh6eY9WbnZ5nqSso,31373
14
+ bbstrader/btengine/strategy.py,sha256=FrYoB8ogHODTfRDZf2gW-CGdTxdVoIYwxko1cYgwcgg,31631
15
15
  bbstrader/core/__init__.py,sha256=GIFzFSStPfE0XM2j7mDeZZQeMTh_AwPsDOQXwMVJLgw,97
16
16
  bbstrader/core/data.py,sha256=VPuynoT0uFYduh7la8gZSnEv_Gq8Xu2vJZJ7TfQMll8,18797
17
17
  bbstrader/core/utils.py,sha256=WjuabzBjhY65ku2KL-f7CMalE2x-wrX-6mCA_qhhFPE,2728
18
18
  bbstrader/ibkr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  bbstrader/ibkr/utils.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
20
  bbstrader/metatrader/__init__.py,sha256=A5Ye9tpc2sp9Xk5qjKw-EfYsoRcZtAt8nqvC3tCtZs8,333
21
- bbstrader/metatrader/account.py,sha256=Pwg8rVmxLLkUvcrUajfdA9Lx7u9kauJ5xXWdkPOpvTE,57374
22
- bbstrader/metatrader/copier.py,sha256=4Rlq3V7jmMNPRb43f9AiZxdQkYOpze8lYdg3DoSxSfo,31532
21
+ bbstrader/metatrader/account.py,sha256=kSEJChls4o2smMWa-FeHoxS5gd6yyOPfzoU_6jmzxTg,57374
22
+ bbstrader/metatrader/analysis.py,sha256=pnZWdvEnf9wkPT0vhEIbZipnN5EUf6TaQftplYP9jRs,3564
23
+ bbstrader/metatrader/copier.py,sha256=pCZkRfkUWxufnmiO0cc1Kz6UF59HTUUGs-c60LYNUbE,32307
23
24
  bbstrader/metatrader/rates.py,sha256=0bKyjGnafZ19DwC0-IxXUBJzOdAo-cNmhabwhKLxcos,20585
24
25
  bbstrader/metatrader/risk.py,sha256=pwG4q1_uPGgPlokDGVNtd04O6p28yIbgT-evvHuo-Qc,27029
25
26
  bbstrader/metatrader/scripts.py,sha256=Yjp7Un-wDTInptHS_rPFpXNKWbVM871VEkaHxsO2MPQ,2115
26
- bbstrader/metatrader/trade.py,sha256=R3PR1bJhBw9osCuAuoERucAJ0kUWGlXyG27wy3yXw9w,80137
27
+ bbstrader/metatrader/trade.py,sha256=2vkoe972OUi5m5rVkWa2DuyXSo04JCSsF5tyahDbL3s,79926
27
28
  bbstrader/metatrader/utils.py,sha256=szORxKJW9OG-H6nof_ovOhToSx_n8EtVwe0mkLt3eFg,17424
28
29
  bbstrader/models/__init__.py,sha256=s2mJrtKePXQaw_PvcrtPCD2mPCdVXP4Myzg0MlLVipo,547
29
- bbstrader/models/factors.py,sha256=5kSAOS1MHvTZ-Ti03TtjOxl_EvC-V_9e389xeR_82ak,13020
30
+ bbstrader/models/factors.py,sha256=-ODeD2GK4nmxFQAIh5MF3SFM9ofppi2pW6Q728LzLv8,12701
30
31
  bbstrader/models/ml.py,sha256=tCr7YyODl0CDoOUpYqJ1q12ls86Sc-_Fu3b2Y0Z7TJ8,47551
31
32
  bbstrader/models/nlp.py,sha256=P7SYaTIqEBldjwYfS6IrO66Y6-ioDXUrCSf3bZxQrDE,28073
32
33
  bbstrader/models/optimization.py,sha256=vnks6uxFZdqXgxaZJNxq8z0IH45KZ8yaXo38JhIVKGc,6399
33
34
  bbstrader/models/portfolio.py,sha256=r-47Zrn2r7iKCHm5YVtwkbBJXAZGM3QYy-rXCWY9-Bg,8079
34
35
  bbstrader/models/risk.py,sha256=JQOXPshMOru6Eb0AMU6oKCNzg6mlGfL6_tN90lWcVBE,14878
35
36
  bbstrader/trading/__init__.py,sha256=ycLyuuxN5SujqtzR9X0Q74UQfK93q2va-GGAXdr-KS8,457
36
- bbstrader/trading/execution.py,sha256=IQCPd-MAColYw3H_2WqbBzLIOAhVqNWvZ-bZIaml-ro,38160
37
+ bbstrader/trading/execution.py,sha256=ukOiv1O-a_SdKp35QlU_3_Wt_7CTgh4pLPN9s7qPEdw,37117
37
38
  bbstrader/trading/scripts.py,sha256=Tf5q33WqqygjpIv43_8nA82VZ3GM0qgb4Ggo3fHJ_wg,5744
38
- bbstrader/trading/strategies.py,sha256=D_M91UwRiFwZKvKFKmz427TPd-dNyWWbhehaP8RI17c,36539
39
+ bbstrader/trading/strategies.py,sha256=rRNPZM1Z9twzO5UWd5qI3FgkZmOhF3Dd2h7XRn7EoDs,37011
39
40
  bbstrader/trading/utils.py,sha256=57dKF9dcRu04oU2VRqydRrzW39dCW2wlDWhVt-sZdRw,1857
40
- bbstrader-0.2.98.dist-info/licenses/LICENSE,sha256=ZwC_RqqGmOPBUiMDKqLyJZ5HBeHq53LpL7TMRzrJY8c,1094
41
- bbstrader-0.2.98.dist-info/METADATA,sha256=M9AZGC1rK4ygm9BEIZZJMYyfldWxb2Pu0zY9Q6ep7Ew,11508
42
- bbstrader-0.2.98.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
43
- bbstrader-0.2.98.dist-info/entry_points.txt,sha256=0yDCbhbgHswOzJnY5wRSM_FjjyMHGvY7lJpSSVh0xtI,54
44
- bbstrader-0.2.98.dist-info/top_level.txt,sha256=Wwj322jZmxGZ6gD_TdaPiPLjED5ReObm5omerwlmZIg,10
45
- bbstrader-0.2.98.dist-info/RECORD,,
41
+ bbstrader-0.2.99.dist-info/licenses/LICENSE,sha256=ZwC_RqqGmOPBUiMDKqLyJZ5HBeHq53LpL7TMRzrJY8c,1094
42
+ bbstrader-0.2.99.dist-info/METADATA,sha256=8JnFfL70KEiiqXvBhDEoqafZ4-KMTNtbheytb3-5HZ4,11508
43
+ bbstrader-0.2.99.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
44
+ bbstrader-0.2.99.dist-info/entry_points.txt,sha256=0yDCbhbgHswOzJnY5wRSM_FjjyMHGvY7lJpSSVh0xtI,54
45
+ bbstrader-0.2.99.dist-info/top_level.txt,sha256=Wwj322jZmxGZ6gD_TdaPiPLjED5ReObm5omerwlmZIg,10
46
+ bbstrader-0.2.99.dist-info/RECORD,,