bbstrader 0.1.5__tar.gz → 0.1.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of bbstrader might be problematic. Click here for more details.

Files changed (33) hide show
  1. {bbstrader-0.1.5 → bbstrader-0.1.6}/PKG-INFO +1 -1
  2. {bbstrader-0.1.5 → bbstrader-0.1.6}/bbstrader/btengine/backtest.py +10 -7
  3. {bbstrader-0.1.5 → bbstrader-0.1.6}/bbstrader/btengine/data.py +22 -11
  4. {bbstrader-0.1.5 → bbstrader-0.1.6}/bbstrader/btengine/event.py +4 -3
  5. {bbstrader-0.1.5 → bbstrader-0.1.6}/bbstrader/btengine/execution.py +1 -1
  6. {bbstrader-0.1.5 → bbstrader-0.1.6}/bbstrader/btengine/strategy.py +3 -1
  7. {bbstrader-0.1.5 → bbstrader-0.1.6}/bbstrader/metatrader/account.py +14 -2
  8. {bbstrader-0.1.5 → bbstrader-0.1.6}/bbstrader/metatrader/rates.py +30 -12
  9. {bbstrader-0.1.5 → bbstrader-0.1.6}/bbstrader/metatrader/trade.py +10 -7
  10. {bbstrader-0.1.5 → bbstrader-0.1.6}/bbstrader/metatrader/utils.py +3 -2
  11. {bbstrader-0.1.5 → bbstrader-0.1.6}/bbstrader/trading/execution.py +24 -12
  12. {bbstrader-0.1.5 → bbstrader-0.1.6}/bbstrader.egg-info/PKG-INFO +1 -1
  13. {bbstrader-0.1.5 → bbstrader-0.1.6}/setup.py +1 -1
  14. {bbstrader-0.1.5 → bbstrader-0.1.6}/LICENSE +0 -0
  15. {bbstrader-0.1.5 → bbstrader-0.1.6}/README.md +0 -0
  16. {bbstrader-0.1.5 → bbstrader-0.1.6}/bbstrader/__ini__.py +0 -0
  17. {bbstrader-0.1.5 → bbstrader-0.1.6}/bbstrader/btengine/__init__.py +0 -0
  18. {bbstrader-0.1.5 → bbstrader-0.1.6}/bbstrader/btengine/performance.py +0 -0
  19. {bbstrader-0.1.5 → bbstrader-0.1.6}/bbstrader/btengine/portfolio.py +0 -0
  20. {bbstrader-0.1.5 → bbstrader-0.1.6}/bbstrader/metatrader/__init__.py +0 -0
  21. {bbstrader-0.1.5 → bbstrader-0.1.6}/bbstrader/metatrader/risk.py +0 -0
  22. {bbstrader-0.1.5 → bbstrader-0.1.6}/bbstrader/models/__init__.py +0 -0
  23. {bbstrader-0.1.5 → bbstrader-0.1.6}/bbstrader/models/risk.py +0 -0
  24. {bbstrader-0.1.5 → bbstrader-0.1.6}/bbstrader/strategies.py +0 -0
  25. {bbstrader-0.1.5 → bbstrader-0.1.6}/bbstrader/trading/__init__.py +0 -0
  26. {bbstrader-0.1.5 → bbstrader-0.1.6}/bbstrader/trading/run.py +0 -0
  27. {bbstrader-0.1.5 → bbstrader-0.1.6}/bbstrader/trading/utils.py +0 -0
  28. {bbstrader-0.1.5 → bbstrader-0.1.6}/bbstrader/tseries.py +0 -0
  29. {bbstrader-0.1.5 → bbstrader-0.1.6}/bbstrader.egg-info/SOURCES.txt +0 -0
  30. {bbstrader-0.1.5 → bbstrader-0.1.6}/bbstrader.egg-info/dependency_links.txt +0 -0
  31. {bbstrader-0.1.5 → bbstrader-0.1.6}/bbstrader.egg-info/requires.txt +0 -0
  32. {bbstrader-0.1.5 → bbstrader-0.1.6}/bbstrader.egg-info/top_level.txt +0 -0
  33. {bbstrader-0.1.5 → bbstrader-0.1.6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: bbstrader
3
- Version: 0.1.5
3
+ Version: 0.1.6
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/
@@ -6,7 +6,6 @@ import pandas as pd
6
6
  import yfinance as yf
7
7
  from queue import Queue
8
8
  from datetime import datetime
9
- from seaborn import saturate
10
9
  from bbstrader.btengine.data import *
11
10
  from bbstrader.btengine.execution import *
12
11
  from bbstrader.btengine.portfolio import Portfolio
@@ -132,7 +131,8 @@ class Backtest(object):
132
131
  self.start_date,
133
132
  self.initial_capital, **self.kwargs
134
133
  )
135
- self.execution_handler: ExecutionHandler = self.eh_cls(self.events)
134
+ self.execution_handler: ExecutionHandler = self.eh_cls(
135
+ self.events, **self.kwargs)
136
136
 
137
137
  def _run_backtest(self):
138
138
  """
@@ -172,7 +172,7 @@ class Backtest(object):
172
172
  self.fills += 1
173
173
  self.portfolio.update_fill(event)
174
174
 
175
- time.sleep(self.heartbeat)
175
+ time.sleep(self.heartbeat)
176
176
 
177
177
  def _output_performance(self):
178
178
  """
@@ -788,10 +788,10 @@ def _run_sma_backtest(
788
788
  "quantity": quantity,
789
789
  "hmm_end": "2009-12-31",
790
790
  "hmm_tiker": "^GSPC",
791
- "yf_start": "2010-01-01",
791
+ "yf_start": "2010-01-04",
792
792
  "hmm_start": "1990-01-01",
793
- "start_pos": "2023-01-01",
794
- "session_duration": 23.0,
793
+ "mt5_start": datetime(2010, 1, 4),
794
+ "mt5_end": datetime(2023, 1, 1),
795
795
  "backtester_class": SMAStrategyBacktester,
796
796
  "data_handler": MT5HistoricDataHandler
797
797
  }
@@ -892,7 +892,10 @@ def run_backtest(
892
892
  quantity=test_quantity
893
893
  )
894
894
  else:
895
- execution_handler = kwargs.get("exc_handler", SimulatedExecutionHandler)
895
+ if exc_handler is None:
896
+ execution_handler = SimulatedExecutionHandler
897
+ else:
898
+ execution_handler = exc_handler
896
899
  engine = Backtest(
897
900
  symbol_list, initial_capital, heartbeat, start_date,
898
901
  data_handler, execution_handler, strategy, **kwargs
@@ -8,6 +8,7 @@ from queue import Queue
8
8
  from abc import ABCMeta, abstractmethod
9
9
  from bbstrader.metatrader.rates import Rates
10
10
  from bbstrader.btengine.event import MarketEvent
11
+ from datetime import datetime
11
12
 
12
13
 
13
14
  __all__ = [
@@ -260,7 +261,7 @@ class HistoricCSVDataHandler(BaseCSVDataHandler):
260
261
  csv_dir = kwargs.get("csv_dir")
261
262
  super().__init__(events, symbol_list, csv_dir)
262
263
 
263
- MAX_BARS = 10_000_000
264
+
264
265
  class MT5HistoricDataHandler(BaseCSVDataHandler):
265
266
  """
266
267
  Downloads historical data from MetaTrader 5 (MT5) and provides
@@ -281,19 +282,16 @@ class MT5HistoricDataHandler(BaseCSVDataHandler):
281
282
  symbol_list (List[str]): A list of symbol strings to download data for.
282
283
  **kwargs: Keyword arguments for data retrieval:
283
284
  time_frame (str): MT5 time frame (e.g., 'D1' for daily).
284
- max_bars (int): Maximum number of bars to download per symbol.
285
- start_pos (int | str, optional): Starting bar position (default: 0).
286
- If it set to `str`, it must be in 'YYYY-MM-DD' format and
287
- session_duration (int | float): Number of trading hours per day.
285
+ mt5_start (datetime): Start date for historical data.
286
+ mt5_end (datetime): End date for historical data.
288
287
  mt5_data (str): Directory for storing data (default: 'mt5_data').
289
288
 
290
289
  Note:
291
290
  Requires a working connection to an MT5 terminal.
292
291
  """
293
292
  self.tf = kwargs.get('time_frame', 'D1')
294
- self.start_pos = kwargs.get('start_pos', 0)
295
- self.max_bars = kwargs.get('max_bars', MAX_BARS)
296
- self.sd = kwargs.get('session_duration', 6.5)
293
+ self.start = kwargs.get('mt5_start')
294
+ self.end = kwargs.get('mt5_end', datetime.now())
297
295
  self.data_dir = kwargs.get('mt5_data', 'mt5_data')
298
296
  self.symbol_list = symbol_list
299
297
  csv_dir = self._download_data(self.data_dir)
@@ -304,8 +302,10 @@ class MT5HistoricDataHandler(BaseCSVDataHandler):
304
302
  data_dir.mkdir(parents=True, exist_ok=True)
305
303
  for symbol in self.symbol_list:
306
304
  try:
307
- rate = Rates(symbol, self.tf, self.start_pos, self.max_bars, self.sd)
308
- data = rate.get_rates_from_pos()
305
+ rate = Rates(symbol=symbol, time_frame=self.tf)
306
+ data = rate.get_historical_data(
307
+ date_from=self.start, date_to=self.end
308
+ )
309
309
  if data is None:
310
310
  raise ValueError(f"No data found for {symbol}")
311
311
  data.to_csv(data_dir / f'{symbol}.csv')
@@ -359,16 +359,27 @@ class YFHistoricDataHandler(BaseCSVDataHandler):
359
359
  return cache_dir
360
360
 
361
361
 
362
+ # TODO # Get data from EODHD
363
+ class EODHDHistoricDataHandler(BaseCSVDataHandler):
364
+ ...
365
+
362
366
  # TODO # Get data from FinancialModelingPrep ()
363
367
  class FMPHistoricDataHandler(BaseCSVDataHandler):
364
368
  ...
365
369
 
366
370
 
367
371
  class BaseFMPDataHanler(object):
372
+ """
373
+ This will serve as the base class for all other FMP data
374
+ that is not historical data and does not have an OHLC structure.
375
+ """
368
376
  ...
369
377
 
370
378
 
371
379
  class FMPFundamentalDataHandler(BaseFMPDataHanler):
372
380
  ...
373
381
 
374
- # TODO Add other Handlers
382
+ # TODO Add other Handlers for FMP
383
+
384
+
385
+ # TODO Add data Handlers for Interactive Brokers
@@ -1,4 +1,5 @@
1
1
  from datetime import datetime
2
+ from typing import Literal
2
3
 
3
4
  __all__ = [
4
5
  "Event",
@@ -150,8 +151,8 @@ class FillEvent(Event):
150
151
  symbol: str,
151
152
  exchange: str,
152
153
  quantity: int | float,
153
- direction: str,
154
- fill_cost: int | float,
154
+ direction: Literal['LONG', 'SHORT', 'EXIT'],
155
+ fill_cost: int | float | None,
155
156
  commission: float | None = None
156
157
  ):
157
158
  """
@@ -168,7 +169,7 @@ class FillEvent(Event):
168
169
  symbol (str): The instrument which was filled.
169
170
  exchange (str): The exchange where the order was filled.
170
171
  quantity (int | float): The filled quantity.
171
- direction (str): The direction of fill ('BUY' or 'SELL')
172
+ direction (str): The direction of fill `('LONG', 'SHORT', 'EXIT')`
172
173
  fill_cost (int | float): The holdings value in dollars.
173
174
  commission (float | None): An optional commission sent from IB.
174
175
  """
@@ -51,7 +51,7 @@ class SimulatedExecutionHandler(ExecutionHandler):
51
51
  handler.
52
52
  """
53
53
 
54
- def __init__(self, events: Queue):
54
+ def __init__(self, events: Queue, **kwargs):
55
55
  """
56
56
  Initialises the handler, setting the event queues
57
57
  up internally.
@@ -1,4 +1,5 @@
1
1
  from abc import ABCMeta, abstractmethod
2
+ from typing import Dict, Union
2
3
 
3
4
 
4
5
  class Strategy(metaclass=ABCMeta):
@@ -22,9 +23,10 @@ class Strategy(metaclass=ABCMeta):
22
23
  """
23
24
 
24
25
  @abstractmethod
25
- def calculate_signals(self):
26
+ def calculate_signals(self, *args, **kwargs) -> Dict[str, Union[str, None]]:
26
27
  """
27
28
  Provides the mechanisms to calculate the list of signals.
29
+ This methods should return a dictionary of symbols and their respective signals.
28
30
  """
29
31
  raise NotImplementedError(
30
32
  "Should implement calculate_signals()"
@@ -555,13 +555,19 @@ class Account(object):
555
555
 
556
556
  Raises:
557
557
  MT5TerminalError: A specific exception based on the error code.
558
+
559
+ Notes:
560
+ The `time` property is converted to a `datetime` object using Broker server time.
558
561
  """
559
562
  try:
560
563
  symbol_info = mt5.symbol_info(symbol)
561
564
  if symbol_info is None:
562
565
  return None
563
566
  else:
564
- return SymbolInfo(**symbol_info._asdict())
567
+ symbol_info_dict = symbol_info._asdict()
568
+ time = datetime.fromtimestamp(symbol_info.time)
569
+ symbol_info_dict['time'] = time
570
+ return SymbolInfo(**symbol_info_dict)
565
571
  except Exception as e:
566
572
  msg = self._symbol_info_msg(symbol)
567
573
  raise_mt5_error(message=f"{e+msg}")
@@ -595,13 +601,19 @@ class Account(object):
595
601
 
596
602
  Raises:
597
603
  MT5TerminalError: A specific exception based on the error code.
604
+
605
+ Notes:
606
+ The `time` property is converted to a `datetime` object using Broker server time.
598
607
  """
599
608
  try:
600
609
  tick_info = mt5.symbol_info_tick(symbol)
601
610
  if tick_info is None:
602
611
  return None
603
612
  else:
604
- return TickInfo(**tick_info._asdict())
613
+ info_dict = tick_info._asdict()
614
+ time = datetime.fromtimestamp(tick_info.time)
615
+ info_dict['time'] = time
616
+ return TickInfo(**info_dict)
605
617
  except Exception as e:
606
618
  msg = self._symbol_info_msg(symbol)
607
619
  raise_mt5_error(message=f"{e+msg}")
@@ -21,6 +21,10 @@ class Rates(object):
21
21
  flexibility in retrieving data either by specifying a starting position
22
22
  and count of bars or by providing a specific date range.
23
23
 
24
+ Notes:
25
+ The `get_open, get_high, get_low, get_close, get_adj_close, get_returns,
26
+ get_volume` properties return data in Broker's timezone.
27
+
24
28
  Example:
25
29
  >>> rates = Rates("EURUSD", "1h")
26
30
  >>> df = rates.get_historical_data(
@@ -62,8 +66,14 @@ class Rates(object):
62
66
  self.start_pos = self._get_start_pos(start_pos, time_frame)
63
67
  self.count = count
64
68
  self._mt5_initialized()
65
- self.data = self.get_rates_from_pos()
69
+ self.__data = self.get_rates_from_pos()
70
+
66
71
 
72
+ def _mt5_initialized(self):
73
+ """Ensures the MetaTrader 5 Terminal is initialized."""
74
+ if not Mt5.initialize():
75
+ raise_mt5_error(message=INIT_MSG)
76
+
67
77
  def _get_start_pos(self, index, time_frame):
68
78
  if isinstance(index, int):
69
79
  start_pos = index
@@ -112,11 +122,6 @@ class Rates(object):
112
122
  )
113
123
  return TIMEFRAMES[time_frame]
114
124
 
115
- def _mt5_initialized(self):
116
- """Ensures the MetaTrader 5 Terminal is initialized."""
117
- if not Mt5.initialize():
118
- raise_mt5_error(message=INIT_MSG)
119
-
120
125
  def _fetch_data(
121
126
  self, start: Union[int, datetime],
122
127
  count: Union[int, datetime]
@@ -160,6 +165,13 @@ class Rates(object):
160
165
  Returns:
161
166
  Union[pd.DataFrame, None]: A DataFrame containing historical
162
167
  data if successful, otherwise None.
168
+
169
+ Raises:
170
+ ValueError: If `start_pos` or `count` is not provided during
171
+ initialization.
172
+
173
+ Notes:
174
+ The Datetime for this method is in Broker's timezone.
163
175
  """
164
176
  if self.start_pos is None or self.count is None:
165
177
  raise ValueError(
@@ -171,23 +183,23 @@ class Rates(object):
171
183
 
172
184
  @property
173
185
  def get_open(self):
174
- return self.data['Open']
186
+ return self.__data['Open']
175
187
 
176
188
  @property
177
189
  def get_high(self):
178
- return self.data['High']
190
+ return self.__data['High']
179
191
 
180
192
  @property
181
193
  def get_low(self):
182
- return self.data['Low']
194
+ return self.__data['Low']
183
195
 
184
196
  @property
185
197
  def get_close(self):
186
- return self.data['Close']
198
+ return self.__data['Close']
187
199
 
188
200
  @property
189
201
  def get_adj_close(self):
190
- return self.data['Adj Close']
202
+ return self.__data['Adj Close']
191
203
 
192
204
  @property
193
205
  def get_returns(self):
@@ -202,7 +214,7 @@ class Rates(object):
202
214
  It calculates fractional change (also known as `per unit change or relative change`)
203
215
  and `not percentage change`. If you need the percentage change, multiply these values by 100.
204
216
  """
205
- data = self.data.copy()
217
+ data = self.__data.copy()
206
218
  data['Returns'] = data['Adj Close'].pct_change()
207
219
  data = data.dropna()
208
220
  return data['Returns']
@@ -230,6 +242,12 @@ class Rates(object):
230
242
  Returns:
231
243
  Union[pd.DataFrame, None]: A DataFrame containing historical data
232
244
  if successful, otherwise None.
245
+
246
+ Raises:
247
+ ValueError: If the starting date is greater than the ending date.
248
+
249
+ Notes:
250
+ The Datetime for this method is in UTC timezone.
233
251
  """
234
252
  df = self._fetch_data(date_from, date_to)
235
253
  if save_csv and df is not None:
@@ -14,7 +14,7 @@ from bbstrader.metatrader.utils import (
14
14
  )
15
15
 
16
16
  # Configure the logger
17
- logger = config_logger('trade.log', console_log=True)
17
+ logger = config_logger('trade.log', console_log=False)
18
18
 
19
19
  class Trade(RiskManagement):
20
20
  """
@@ -160,7 +160,7 @@ class Trade(RiskManagement):
160
160
  print()
161
161
  self.risk_managment()
162
162
  print(
163
- f">>> Everything is OK, @{self.expert_name} is Running ....>>>\n")
163
+ f">>> Everything is OK, @{self.expert_name} is Running ...>>>\n")
164
164
 
165
165
  def initialize(self):
166
166
  """
@@ -416,7 +416,7 @@ class Trade(RiskManagement):
416
416
  request["action"] = Mt5.TRADE_ACTION_PENDING
417
417
  request["type"] = self._order_type()[action][0]
418
418
 
419
- self.break_even(comment)
419
+ self.break_even(mm=mm)
420
420
  if self.check(comment):
421
421
  self.request_result(_price, request, action),
422
422
 
@@ -767,7 +767,7 @@ class Trade(RiskManagement):
767
767
  return True
768
768
  return False
769
769
 
770
- def break_even(self, id: Optional[int] = None):
770
+ def break_even(self, mm=True, id: Optional[int] = None):
771
771
  """
772
772
  Checks if it's time to put the break even,
773
773
  if so , it will sets the break even ,and if the break even was already set,
@@ -776,8 +776,11 @@ class Trade(RiskManagement):
776
776
 
777
777
  Args:
778
778
  id (int): The strategy Id or Expert Id
779
+ mm (bool): Weither to manage the position or not
779
780
  """
780
781
  time.sleep(0.1)
782
+ if not mm:
783
+ return
781
784
  Id = id if id is not None else self.expert_id
782
785
  positions = self.get_positions(symbol=self.symbol)
783
786
  be = self.get_break_even()
@@ -1111,13 +1114,13 @@ class Trade(RiskManagement):
1111
1114
 
1112
1115
  if len(tickets) == 0:
1113
1116
  logger.info(
1114
- f"ALL {position_type.upper()} Positions closed, SYMBOL={self.symbol}.")
1117
+ f"ALL {pos_type.upper()} Positions closed, SYMBOL={self.symbol}.")
1115
1118
  else:
1116
1119
  logger.info(
1117
- f"{len(tickets)} {position_type.upper()} Positions not closed, SYMBOL={self.symbol}")
1120
+ f"{len(tickets)} {pos_type.upper()} Positions not closed, SYMBOL={self.symbol}")
1118
1121
  else:
1119
1122
  logger.info(
1120
- f"No {position_type.upper()} Positions to close, SYMBOL={self.symbol}.")
1123
+ f"No {pos_type.upper()} Positions to close, SYMBOL={self.symbol}.")
1121
1124
 
1122
1125
  def get_stats(self) -> Tuple[Dict[str, Any]]:
1123
1126
  """
@@ -1,3 +1,4 @@
1
+ from datetime import datetime
1
2
  import MetaTrader5 as MT5
2
3
  import logging
3
4
  from typing import List, NamedTuple, Optional
@@ -216,7 +217,7 @@ class SymbolInfo(NamedTuple):
216
217
  volume: int
217
218
  volumehigh: int
218
219
  volumelow: int
219
- time: int
220
+ time: datetime
220
221
  digits: int
221
222
  spread: int
222
223
  spread_float: bool
@@ -316,7 +317,7 @@ class TickInfo(NamedTuple):
316
317
  * flags: Tick flags
317
318
  * volume_real: Volume for the current Last price with greater accuracy
318
319
  """
319
- time: int
320
+ time: datetime
320
321
  bid: float
321
322
  ask: float
322
323
  last: float
@@ -165,10 +165,13 @@ def sma_trading(
165
165
  time.sleep((60 * iter_time) - 1.5)
166
166
  if iter_time == 1:
167
167
  time_intervals += 1
168
- elif iter_time == trade_time:
169
- time_intervals += trade_time
168
+ elif trade_time % iter_time == 0:
169
+ time_intervals += iter_time
170
170
  else:
171
- time_intervals += (trade_time/iter_time)
171
+ raise ValueError(
172
+ f"iter_time must be a multiple of the {tf} !!!"
173
+ f"(e.g; if time_frame is 15m, iter_time must be 1.5, 3, 3, 15 etc)"
174
+ )
172
175
  if period.lower() == 'month':
173
176
  if trade.days_end() and today != 'Friday':
174
177
  sleep_time = trade.sleep_time()
@@ -484,10 +487,13 @@ def pair_trading(
484
487
 
485
488
  if iter_time == 1:
486
489
  time_intervals += 1
487
- elif iter_time == trade_time:
488
- time_intervals += trade_time
490
+ elif trade_time % iter_time == 0:
491
+ time_intervals += iter_time
489
492
  else:
490
- time_intervals += (trade_time/iter_time)
493
+ raise ValueError(
494
+ f"iter_time must be a multiple of the {tf} !!!"
495
+ f"(e.g; if time_frame is 15m, iter_time must be 1.5, 3, 3, 15 etc)"
496
+ )
491
497
 
492
498
  if period.lower() == 'month':
493
499
  if p0.days_end() and today != 'Friday':
@@ -706,10 +712,13 @@ def ou_trading(
706
712
  time.sleep((60 * iter_time) - 1.5)
707
713
  if iter_time == 1:
708
714
  time_intervals += 1
709
- elif iter_time == trade_time:
710
- time_intervals += trade_time
715
+ elif trade_time % iter_time == 0:
716
+ time_intervals += iter_time
711
717
  else:
712
- time_intervals += (trade_time/iter_time)
718
+ raise ValueError(
719
+ f"iter_time must be a multiple of the {tf} !!!"
720
+ f"(e.g; if time_frame is 15m, iter_time must be 1.5, 3, 3, 15 etc)"
721
+ )
713
722
 
714
723
  if period.lower() == 'month':
715
724
  if trade.days_end() and today != 'Friday':
@@ -918,10 +927,13 @@ def arch_trading(
918
927
  time.sleep((60 * iter_time) - 1.5)
919
928
  if iter_time == 1:
920
929
  time_intervals += 1
921
- elif iter_time == trade_time:
922
- time_intervals += trade_time
930
+ elif trade_time % iter_time == 0:
931
+ time_intervals += iter_time
923
932
  else:
924
- time_intervals += (trade_time/iter_time)
933
+ raise ValueError(
934
+ f"iter_time must be a multiple of the {tf} !!!"
935
+ f"(e.g; if time_frame is 15m, iter_time must be 1.5, 3, 3, 15 etc)"
936
+ )
925
937
 
926
938
  if period.lower() == 'month':
927
939
  if trade.days_end() and today != 'Friday':
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: bbstrader
3
- Version: 0.1.5
3
+ Version: 0.1.6
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/
@@ -7,7 +7,7 @@ if sys.version_info < (3, 10):
7
7
  with open("README.md", encoding="utf-8") as fh:
8
8
  long_description = fh.read()
9
9
 
10
- VERSION = '0.1.05'
10
+ VERSION = '0.1.06'
11
11
  DESCRIPTION = 'Simplified Investment & Trading Toolkit'
12
12
 
13
13
  KEYWORDS = [
File without changes
File without changes
File without changes