bbstrader 0.1.7__py3-none-any.whl → 0.1.8__py3-none-any.whl

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

Potentially problematic release.


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

@@ -4,6 +4,38 @@ import logging
4
4
  from typing import List, NamedTuple, Optional
5
5
  from enum import Enum
6
6
 
7
+
8
+ __all__ = [
9
+ "TIMEFRAMES",
10
+ "TimeFrame",
11
+ "TerminalInfo",
12
+ "AccountInfo",
13
+ "SymbolInfo",
14
+ "TickInfo",
15
+ "TradeRequest",
16
+ "OrderCheckResult",
17
+ "OrderSentResult",
18
+ "TradeOrder",
19
+ "TradePosition",
20
+ "TradeDeal",
21
+ "InvalidBroker",
22
+ "GenericFail",
23
+ "InvalidParams",
24
+ "HistoryNotFound",
25
+ "InvalidVersion",
26
+ "AuthFailed",
27
+ "UnsupportedMethod",
28
+ "AutoTradingDisabled",
29
+ "InternalFailSend",
30
+ "InternalFailReceive",
31
+ "InternalFailInit",
32
+ "InternalFailConnect",
33
+ "InternalFailTimeout",
34
+ "trade_retcode_message",
35
+ "raise_mt5_error",
36
+ "config_logger",
37
+ ]
38
+
7
39
  def config_logger(log_file: str, console_log=True):
8
40
  # Configure the logger
9
41
  logger = logging.getLogger(__name__)
@@ -55,34 +87,6 @@ class LogLevelFilter(logging.Filter):
55
87
  return record.levelno in self.levels
56
88
 
57
89
 
58
- __all__ = [
59
- "TIMEFRAMES",
60
- "TimeFrame",
61
- "TerminalInfo",
62
- "AccountInfo",
63
- "SymbolInfo",
64
- "TickInfo",
65
- "TradeRequest",
66
- "OrderCheckResult",
67
- "OrderSentResult",
68
- "TradeOrder",
69
- "TradePosition",
70
- "TradeDeal",
71
- "GenericFail",
72
- "InvalidParams",
73
- "HistoryNotFound",
74
- "InvalidVersion",
75
- "AuthFailed",
76
- "UnsupportedMethod",
77
- "AutoTradingDisabled",
78
- "InternalFailSend",
79
- "InternalFailReceive",
80
- "InternalFailInit",
81
- "InternalFailConnect",
82
- "InternalFailTimeout",
83
- "trade_retcode_message",
84
- "raise_mt5_error",
85
- ]
86
90
 
87
91
  # TIMEFRAME is an enumeration with possible chart period values
88
92
  # See https://www.mql5.com/en/docs/python_metatrader5/mt5copyratesfrom_py#timeframe
@@ -137,7 +141,6 @@ class TimeFrame(Enum):
137
141
  W1 = "W1"
138
142
  MN1 = "MN1"
139
143
 
140
-
141
144
  class TerminalInfo(NamedTuple):
142
145
  """
143
146
  Represents general information about the trading terminal.
@@ -467,6 +470,11 @@ class TradeDeal(NamedTuple):
467
470
  comment: str
468
471
  external_id: str
469
472
 
473
+ class InvalidBroker(Exception):
474
+ """Exception raised for invalid broker errors."""
475
+ def __init__(self, message="Invalid broker."):
476
+ super().__init__(message)
477
+
470
478
 
471
479
  class MT5TerminalError(Exception):
472
480
  """Base exception class for trading-related errors."""
@@ -1,4 +1,3 @@
1
- from math import log
2
1
  import time
3
2
  from datetime import datetime
4
3
  from bbstrader.metatrader.trade import Trade
@@ -22,7 +21,7 @@ _TF_MAPPING = {
22
21
  'D1': 1440
23
22
  }
24
23
 
25
- TRADING_DAYS = [
24
+ TradingDays = [
26
25
  'monday',
27
26
  'tuesday',
28
27
  'wednesday',
@@ -31,14 +30,18 @@ TRADING_DAYS = [
31
30
  ]
32
31
 
33
32
  def _check_mt5_connection():
34
- if not mt5.initialize():
33
+ try:
34
+ init = mt5.initialize()
35
+ if not init:
36
+ raise_mt5_error(INIT_MSG)
37
+ except Exception:
35
38
  raise_mt5_error(INIT_MSG)
36
39
 
37
40
  def _mt5_execution(
38
- symbol_list, trades_instances, strategy_cls, /,
39
- mm, time_frame, iter_time, period, trading_days,
40
- comment, **kwargs
41
- ):
41
+ symbol_list, trades_instances, strategy_cls, /,
42
+ mm, trail, stop_trail, trail_after_points, be_plus_points,
43
+ time_frame, iter_time, period, period_end_action, trading_days,
44
+ comment, **kwargs):
42
45
  symbols = symbol_list.copy()
43
46
  STRATEGY = kwargs.get('strategy_name')
44
47
  _max_trades = kwargs.get('max_trades')
@@ -54,11 +57,15 @@ def _mt5_execution(
54
57
  if buys is not None:
55
58
  logger.info(
56
59
  f"Checking for Break even, SYMBOL={symbol}...STRATEGY={STRATEGY}")
57
- trades_instances[symbol].break_even(mm=mm)
60
+ trades_instances[symbol].break_even(
61
+ mm=mm, trail=trail, stop_trail=stop_trail,
62
+ trail_after_points=trail_after_points, be_plus_points=be_plus_points)
58
63
  if sells is not None:
59
64
  logger.info(
60
65
  f"Checking for Break even, SYMBOL={symbol}...STRATEGY={STRATEGY}")
61
- trades_instances[symbol].break_even(mm=mm)
66
+ trades_instances[symbol].break_even(
67
+ mm=mm, trail=trail, stop_trail=stop_trail,
68
+ trail_after_points=trail_after_points, be_plus_points=be_plus_points)
62
69
  num_days = 0
63
70
  time_intervals = 0
64
71
  trade_time = _TF_MAPPING[time_frame]
@@ -169,7 +176,13 @@ def _mt5_execution(
169
176
  f"End of the Day !!! SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
170
177
  trade.statistics(save=True)
171
178
  if trades_instances[symbols[-1]].days_end():
172
- break
179
+ if period_end_action == 'break':
180
+ break
181
+ elif period_end_action == 'sleep':
182
+ sleep_time = trades_instances[symbols[-1]].sleep_time()
183
+ logger.info(f"Sleeping for {sleep_time} minutes ...")
184
+ time.sleep(60 * sleep_time)
185
+ logger.info("STARTING NEW TRADING SESSION ...\n")
173
186
 
174
187
  elif period.lower() == 'week':
175
188
  for symbol in symbols:
@@ -187,9 +200,15 @@ def _mt5_execution(
187
200
  sleep_time = trades_instances[symbols[-1]].sleep_time()
188
201
  logger.info(f"Sleeping for {sleep_time} minutes ...")
189
202
  time.sleep(60 * sleep_time)
190
- logger.info("\nSTARTING NEW TRADING SESSION ...")
203
+ logger.info("STARTING NEW TRADING SESSION ...\n")
191
204
  elif trades_instances[symbols[-1]].days_end() and today == 'friday':
192
- break
205
+ if period_end_action == 'break':
206
+ break
207
+ elif period_end_action == 'sleep':
208
+ sleep_time = trades_instances[symbols[-1]].sleep_time(weekend=True)
209
+ logger.info(f"Sleeping for {sleep_time} minutes ...")
210
+ time.sleep(60 * sleep_time)
211
+ logger.info("STARTING NEW TRADING SESSION ...\n")
193
212
 
194
213
  elif period.lower() == 'month':
195
214
  for symbol in symbols:
@@ -214,14 +233,14 @@ def _mt5_execution(
214
233
  sleep_time = trades_instances[symbols[-1]].sleep_time()
215
234
  logger.info(f"Sleeping for {sleep_time} minutes ...")
216
235
  time.sleep(60 * sleep_time)
217
- logger.info("\nSTARTING NEW TRADING SESSION ...")
236
+ logger.info("STARTING NEW TRADING SESSION ...\n")
218
237
  num_days += 1
219
238
  elif trades_instances[symbols[-1]].days_end() and today == 'friday':
220
239
  sleep_time = trades_instances[symbols[-1]
221
240
  ].sleep_time(weekend=True)
222
241
  logger.info(f"Sleeping for {sleep_time} minutes ...")
223
242
  time.sleep(60 * sleep_time)
224
- logger.info("\nSTARTING NEW TRADING SESSION ...")
243
+ logger.info("STARTING NEW TRADING SESSION ...\n")
225
244
  num_days += 1
226
245
  elif (trades_instances[symbols[-1]].days_end()
227
246
  and today == 'friday'
@@ -317,10 +336,15 @@ class ExecutionEngine():
317
336
  strategy_cls: Strategy,
318
337
  /,
319
338
  mm: Optional[bool] = True,
339
+ trail: Optional[bool] = True,
340
+ stop_trail: Optional[int] = None,
341
+ trail_after_points: Optional[int] = None,
342
+ be_plus_points: Optional[int] = None,
320
343
  time_frame: Optional[str] = '15m',
321
344
  iter_time: Optional[int | float] = 5,
322
345
  period: Literal['day', 'week', 'month'] = 'week',
323
- trading_days: Optional[List[str]] = TRADING_DAYS,
346
+ period_end_action: Literal['break', 'sleep'] = 'break',
347
+ trading_days: Optional[List[str]] = TradingDays,
324
348
  comment: Optional[str] = None,
325
349
  **kwargs
326
350
  ):
@@ -333,6 +357,8 @@ class ExecutionEngine():
333
357
  time_frame : Time frame to trade. Defaults to '15m'.
334
358
  iter_time : Interval to check for signals and `mm`. Defaults to 5.
335
359
  period : Period to trade. Defaults to 'week'.
360
+ period_end_action : Action to take at the end of the period. Defaults to 'break',
361
+ this only applies when period is 'day', 'week'.
336
362
  trading_days : Trading days in a week. Defaults to monday to friday.
337
363
  comment: Comment for trades. Defaults to None.
338
364
  **kwargs: Additional keyword arguments
@@ -341,10 +367,11 @@ class ExecutionEngine():
341
367
  - logger (Optional[logging.Logger]): Logger instance. Defaults to None.
342
368
 
343
369
  Note:
344
- 1. All Strategies must inherit from `bbstrader.btengine.strategy.Strategy` class
370
+ 1. For `trail` , `stop_trail` , `trail_after_points` , `be_plus_points` see `bbstrader.metatrader.trade.Trade.break_even()` .
371
+ 2. All Strategies must inherit from `bbstrader.btengine.strategy.Strategy` class
345
372
  and have a `calculate_signals` method that returns a dictionary of signals for each symbol in symbol_list.
346
373
 
347
- 2. All strategies must have the following arguments in their `__init__` method:
374
+ 3. All strategies must have the following arguments in their `__init__` method:
348
375
  - bars (DataHandler): DataHandler instance default to None
349
376
  - events (Queue): Queue instance default to None
350
377
  - symbol_list (List[str]): List of symbols to trade can be none for backtesting
@@ -354,16 +381,21 @@ class ExecutionEngine():
354
381
  the `Strategy` class, the `DataHandler` class, the `Portfolio` class and the `ExecutionHandler` class.
355
382
  - The `bars` and `events` arguments are used for backtesting only.
356
383
 
357
- 3. All strategies must generate signals for backtesting and live trading.
384
+ 4. All strategies must generate signals for backtesting and live trading.
358
385
  See the `bbstrader.trading.strategies` module for more information on how to create custom strategies.
359
386
  """
360
387
  self.symbol_list = symbol_list
361
388
  self.trades_instances = trades_instances
362
389
  self.strategy_cls = strategy_cls
363
390
  self.mm = mm
391
+ self.trail = trail
392
+ self.stop_trail = stop_trail
393
+ self.trail_after_points = trail_after_points
394
+ self.be_plus_points = be_plus_points
364
395
  self.time_frame = time_frame
365
396
  self.iter_time = iter_time
366
397
  self.period = period
398
+ self.period_end_action = period_end_action
367
399
  self.trading_days = trading_days
368
400
  self.comment = comment
369
401
  self.kwargs = kwargs
@@ -377,9 +409,14 @@ class ExecutionEngine():
377
409
  self.trades_instances,
378
410
  self.strategy_cls,
379
411
  mm=self.mm,
412
+ trail=self.trail,
413
+ stop_trail=self.stop_trail,
414
+ trail_after_points=self.trail_after_points,
415
+ be_plus_points=self.be_plus_points,
380
416
  time_frame=self.time_frame,
381
417
  iter_time=self.iter_time,
382
418
  period=self.period,
419
+ period_end_action=self.period_end_action,
383
420
  trading_days=self.trading_days,
384
421
  comment=self.comment,
385
422
  **self.kwargs
@@ -19,8 +19,8 @@ from bbstrader.btengine.strategy import Strategy
19
19
  from bbstrader.btengine.execution import *
20
20
  from bbstrader.btengine.data import *
21
21
  from bbstrader.tseries import (
22
- KalmanFilterModel, ArimaGarchModel
23
- )
22
+ KalmanFilterModel, ArimaGarchModel)
23
+
24
24
  __all__ = [
25
25
  'SMAStrategy',
26
26
  'ArimaGarchStrategy',
@@ -653,13 +653,15 @@ class StockIndexSTBOTrading(Strategy):
653
653
  position.price_open for position in positions
654
654
  if position.type == 0 and position.magic == self.ID
655
655
  ]
656
+ if len(buy_prices) == 0:
657
+ continue
656
658
  avg_price = sum(buy_prices) / len(buy_prices)
657
659
  if self._calculate_pct_change(
658
660
  current_price, avg_price) >= (self.expeted_return[index]):
659
661
  signals[index] = 'EXIT'
660
662
  self.logger.info(
661
663
  f"SYMBOL={index} - Hp={self.heightest_price[index]} - "
662
- f"Lp={self.lowerst_price[index]} - Cp={current_price} - %change={down_change}"
664
+ f"Lp={self.lowerst_price[index]} - Cp={current_price} - %chg={round(down_change, 2)}"
663
665
  )
664
666
  return signals
665
667