bbstrader 0.1.9__py3-none-any.whl → 0.1.91__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,23 +1,39 @@
1
1
  import time
2
- import MetaTrader5 as mt5
3
2
  from datetime import datetime
4
3
  from bbstrader.metatrader.trade import Trade
5
- from bbstrader.trading.strategies import Strategy
4
+ from bbstrader.btengine.strategy import(
5
+ Strategy,
6
+ MT5Strategy
7
+ )
8
+ from bbstrader.trading.scripts import send_message
6
9
  from bbstrader.metatrader.account import check_mt5_connection
7
- from typing import Optional, Literal, Tuple, List, Dict
10
+ from typing import (
11
+ Optional,
12
+ Literal,
13
+ Tuple,
14
+ List,
15
+ Dict
16
+ )
8
17
 
18
+ __all__ = [
19
+ 'MT5ExecutionEngine',
20
+ 'TWSExecutionEngine'
21
+ ]
9
22
 
10
23
  _TF_MAPPING = {
11
- '1m': 1,
12
- '3m': 3,
13
- '5m': 5,
14
- '10m': 10,
15
- '15m': 15,
16
- '30m': 30,
17
- '1h': 60,
18
- '2h': 120,
19
- '4h': 240,
20
- 'D1': 1440
24
+ '1m': 1,
25
+ '3m': 3,
26
+ '5m': 5,
27
+ '10m': 10,
28
+ '15m': 15,
29
+ '30m': 30,
30
+ '1h': 60,
31
+ '2h': 120,
32
+ '4h': 240,
33
+ '6h': 360,
34
+ '8h': 480,
35
+ '12h': 720,
36
+ 'D1': 1440
21
37
  }
22
38
 
23
39
  TradingDays = [
@@ -28,45 +44,80 @@ TradingDays = [
28
44
  'friday'
29
45
  ]
30
46
 
47
+ BUYS = ['BMKT', 'BLMT', 'BSTP', 'BSTPLMT']
48
+ SELLS = ['SMKT', 'SLMT', 'SSTP', 'SSTPLMT']
49
+
50
+ ORDERS_TYPES = ["orders","buy_stops", "sell_stops", "buy_limits",
51
+ "sell_limits", "buy_stop_limits", "sell_stop_limits"]
52
+ POSITIONS_TYPES = ["positions", "buys", "sells", "profitables", "losings"]
53
+
54
+ ACTIONS = ["buys", "sells"]
55
+ STOPS = ["buy_stops", "sell_stops"]
56
+ LIMITS = ["buy_limits", "sell_limits"]
57
+ STOP_LIMITS = ["buy_stop_limits", "sell_stop_limits"]
58
+
59
+ EXIT_SIGNAL_ACTIONS = {
60
+ "EXIT": {a: a[:-1] for a in ACTIONS},
61
+ "EXIT_LONG": {"buys": "buy"},
62
+ "EXIT_SHORT": {"sells": "sell"},
63
+ "EXIT_STOP": {stop: stop for stop in STOPS},
64
+ "EXIT_LONG_STOP": {"buy_stops": "buy_stops"},
65
+ "EXIT_SHORT_STOP": {"sell_stops": "sell_stops"},
66
+ "EXIT_LIMIT": {limit: limit for limit in LIMITS},
67
+ "EXIT_LONG_LIMIT": {"buy_limits": "buy_limits"},
68
+ "EXIT_SHORT_LIMIT": {"sell_limits": "sell_limits"},
69
+ "EXIT_STOP_LIMIT": {sl: sl for sl in STOP_LIMITS},
70
+ "EXIT_LONG_STOP_LIMIT": {STOP_LIMITS[0]: STOP_LIMITS[0]},
71
+ "EXIT_SHORT_STOP_LIMIT":{STOP_LIMITS[1]: STOP_LIMITS[1]},
72
+ "EXIT_PROFITABLES": {"profitables": "profitables"},
73
+ "EXIT_LOSINGS": {"losings": "losings"},
74
+ "EXIT_ALL_POSITIONS": {"positions": "all"},
75
+ "EXIT_ALL_ORDERS": {"orders": "all"}
76
+ }
77
+
31
78
 
32
79
  def _mt5_execution(
33
80
  symbol_list, trades_instances, strategy_cls, /,
34
- mm, trail, stop_trail, trail_after_points, be_plus_points,
35
- time_frame, iter_time, period, period_end_action, trading_days,
81
+ mm, trail, stop_trail, trail_after_points, be_plus_points, show_positions_orders,
82
+ time_frame, iter_time, use_trade_time, period, period_end_action, closing_pnl, trading_days,
36
83
  comment, **kwargs):
37
- symbols = symbol_list.copy()
84
+ symbols = symbol_list.copy()
38
85
  STRATEGY = kwargs.get('strategy_name')
39
- _max_trades = kwargs.get('max_trades')
40
- logger = trades_instances[symbols[0]].logger
41
- max_trades = {symbol: _max_trades[symbol] for symbol in symbols}
86
+ mtrades = kwargs.get('max_trades')
87
+ notify = kwargs.get('notify', False)
88
+ if notify:
89
+ telegram = kwargs.get('telegram', False)
90
+ bot_token = kwargs.get('bot_token')
91
+ chat_id = kwargs.get('chat_id')
92
+
93
+ def _send_notification(self, signal):
94
+ send_message(message=signal, notify_me=notify,
95
+ telegram=telegram, token=bot_token, chat_id=chat_id)
96
+
97
+ logger = trades_instances[symbols[0]].logger
98
+ max_trades = {symbol: mtrades[symbol] for symbol in symbols}
42
99
  if comment is None:
43
100
  trade = trades_instances[symbols[0]]
44
101
  comment = f"{trade.expert_name}@{trade.version}"
45
102
 
46
- def check(buys: List, sells: List, symbol: str):
103
+ def check(buys, sells, symbol):
47
104
  if not mm:
48
105
  return
49
- if buys is not None:
50
- logger.info(
51
- f"Checking for Break even, SYMBOL={symbol}...STRATEGY={STRATEGY}")
52
- trades_instances[symbol].break_even(
53
- mm=mm, trail=trail, stop_trail=stop_trail,
54
- trail_after_points=trail_after_points, be_plus_points=be_plus_points)
55
- if sells is not None:
106
+ if buys is not None or sells is not None:
56
107
  logger.info(
57
108
  f"Checking for Break even, SYMBOL={symbol}...STRATEGY={STRATEGY}")
58
109
  trades_instances[symbol].break_even(
59
110
  mm=mm, trail=trail, stop_trail=stop_trail,
60
111
  trail_after_points=trail_after_points, be_plus_points=be_plus_points)
61
- num_days = 0
62
- time_intervals = 0
63
- trade_time = _TF_MAPPING[time_frame]
112
+ num_days = 0
113
+ time_intervals = 0
114
+ trade_time = _TF_MAPPING[time_frame]
64
115
 
65
- long_market = {symbol: False for symbol in symbols}
66
- short_market = {symbol: False for symbol in symbols}
116
+ long_market = {symbol: False for symbol in symbols}
117
+ short_market = {symbol: False for symbol in symbols}
67
118
  try:
68
119
  check_mt5_connection()
69
- strategy: Strategy = strategy_cls(symbol_list=symbols, mode='live', **kwargs)
120
+ strategy: MT5Strategy = strategy_cls(symbol_list=symbols, mode='live', **kwargs)
70
121
  except Exception as e:
71
122
  logger.error(f"Error initializing strategy, {e}, STRATEGY={STRATEGY}")
72
123
  return
@@ -79,25 +130,29 @@ def _mt5_execution(
79
130
  current_date = datetime.now()
80
131
  today = current_date.strftime("%A").lower()
81
132
  time.sleep(0.5)
82
- buys = {
83
- symbol: trades_instances[symbol].get_current_buys()
84
- for symbol in symbols
133
+ positions_orders = {}
134
+ for type in POSITIONS_TYPES + ORDERS_TYPES:
135
+ for symbol in symbols:
136
+ func = getattr(trades_instances[symbol], f"get_current_{type}")
137
+ positions_orders[type][symbol] = func()
138
+ buys = positions_orders['buys']
139
+ sells = positions_orders['sells']
140
+ for symbol in symbols:
141
+ for type in POSITIONS_TYPES + ORDERS_TYPES:
142
+ if positions_orders[type][symbol] is not None:
143
+ if show_positions_orders:
144
+ logger.info(
145
+ f"Current {type.upper()} SYMBOL={symbol}: \
146
+ {positions_orders[type][symbol]}, STRATEGY={STRATEGY}")
147
+ long_market = {
148
+ symbol: buys[symbol] is not None
149
+ and len(buys[symbol]) >= max_trades[symbol] for symbol in symbols
85
150
  }
86
- sells = {
87
- symbol: trades_instances[symbol].get_current_sells()
88
- for symbol in symbols
151
+ short_market = {
152
+ symbol: sells[symbol] is not None
153
+ and len(sells[symbol]) >= max_trades[symbol] for symbol in symbols
89
154
  }
90
- for symbol in symbols:
91
- if buys[symbol] is not None:
92
- logger.info(
93
- f"Current buy positions SYMBOL={symbol}: {buys[symbol]}, STRATEGY={STRATEGY}")
94
- if sells[symbol] is not None:
95
- logger.info(
96
- f"Current sell positions SYMBOL={symbol}: {sells[symbol]}, STRATEGY={STRATEGY}")
97
- long_market = {symbol: buys[symbol] is not None and len(
98
- buys[symbol]) >= max_trades[symbol] for symbol in symbols}
99
- short_market = {symbol: sells[symbol] is not None and len(
100
- sells[symbol]) >= max_trades[symbol] for symbol in symbols}
155
+
101
156
  except Exception as e:
102
157
  logger.error(f"{e}, STRATEGY={STRATEGY}")
103
158
  continue
@@ -111,49 +166,78 @@ def _mt5_execution(
111
166
  for symbol in symbols:
112
167
  try:
113
168
  check_mt5_connection()
114
- trade = trades_instances[symbol]
115
- logger.info(
116
- f"Calculating signal... SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
169
+ trade: Trade = trades_instances[symbol]
170
+ tfmsg = f"Time Frame Not completed !!! SYMBOL={trade.symbol}, STRATEGY={STRATEGY}"
171
+ riskmsg = f"Risk not allowed !!! SYMBOL={trade.symbol}, STRATEGY={STRATEGY}"
117
172
  signal = signals[symbol]
173
+ if isinstance(signal, dict):
174
+ signal = signal['action']
175
+ price = signal['price']
176
+ stoplimit = signal.get('stoplimit')
177
+ elif isinstance(signal, str):
178
+ price = None
179
+ stoplimit = None
118
180
  if trade.trading_time() and today in trading_days:
119
181
  if signal is not None:
120
- logger.info(
121
- f"SIGNAL = {signal}, SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
122
- if signal in ("EXIT", "EXIT_LONG") and long_market[symbol]:
123
- trade.close_positions(position_type='buy')
124
- elif signal in ("EXIT", "EXIT_SHORT") and short_market[symbol]:
125
- trade.close_positions(position_type='sell')
126
- elif signal == "LONG" and not long_market[symbol]:
127
- if time_intervals % trade_time == 0 or buys[symbol] is None:
128
- logger.info(
129
- f"Sending buy Order ... SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
130
- trade.open_buy_position(mm=mm, comment=comment)
182
+ signal = 'BMKT' if signal == 'LONG' else signal
183
+ signal = 'SMKT' if signal == 'SHORT' else signal
184
+ info = f"SIGNAL = {signal}, SYMBOL={trade.symbol}, STRATEGY={STRATEGY}"
185
+ msg = f"Sending {signal} Order ... SYMBOL={trade.symbol}, STRATEGY={STRATEGY}"
186
+ logger.info(info)
187
+ if signal in EXIT_SIGNAL_ACTIONS:
188
+ for exit_signal, actions in EXIT_SIGNAL_ACTIONS.items():
189
+ for position_type, order_type in actions.items():
190
+ if positions_orders[position_type][symbol] is not None:
191
+ if notify:
192
+ _send_notification(info)
193
+ if position_type in POSITIONS_TYPES:
194
+ trade.close_positions(position_type=order_type)
195
+ else:
196
+ trade.close_orders(order_type=order_type)
197
+ elif signal in BUYS and not long_market[symbol]:
198
+ if use_trade_time:
199
+ if time_intervals % trade_time == 0 or buys[symbol] is None:
200
+ logger.info(msg)
201
+ if notify:
202
+ _send_notification(info)
203
+ trade.open_buy_position(
204
+ action=signal, price=price, stoplimit=stoplimit, mm=mm, comment=comment)
205
+ else:
206
+ logger.info(tfmsg)
207
+ check(buys[symbol], sells[symbol], symbol)
131
208
  else:
132
- check(buys[symbol], sells[symbol], symbol)
133
- elif signal == "LONG" and long_market[symbol]:
134
- logger.info(
135
- f"Sorry Risk not allowed !!! SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
209
+ logger.info(msg)
210
+ if notify:
211
+ _send_notification(info)
212
+ trade.open_buy_position(
213
+ action=signal, price=price, stoplimit=stoplimit, mm=mm, comment=comment)
214
+ elif signal in BUYS and long_market[symbol]:
215
+ logger.info(riskmsg)
136
216
  check(buys[symbol], sells[symbol], symbol)
137
217
 
138
- elif signal == "SHORT" and not short_market[symbol]:
139
- if time_intervals % trade_time == 0 or sells[symbol] is None:
140
- logger.info(
141
- f"Sending sell Order ... SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
142
- trade.open_sell_position(
143
- mm=mm, comment=comment)
218
+ elif signal in SELLS and not short_market[symbol]:
219
+ if use_trade_time:
220
+ if time_intervals % trade_time == 0 or sells[symbol] is None:
221
+ logger.info(msg)
222
+ if notify:
223
+ _send_notification(info)
224
+ trade.open_sell_position(
225
+ action=signal, price=price, stoplimit=stoplimit, mm=mm, comment=comment)
226
+ else:
227
+ logger.info(tfmsg)
228
+ check(buys[symbol], sells[symbol], symbol)
144
229
  else:
145
- check(buys[symbol], sells[symbol], symbol)
146
- elif signal == "SHORT" and short_market[symbol]:
147
- logger.info(
148
- f"Sorry Risk not allowed !!! SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
230
+ logger.info(msg)
231
+ if notify:
232
+ _send_notification(info)
233
+ trade.open_sell_position(
234
+ action=signal, price=price, stoplimit=stoplimit, mm=mm, comment=comment)
235
+ elif signal in SELLS and short_market[symbol]:
236
+ logger.info(riskmsg)
149
237
  check(buys[symbol], sells[symbol], symbol)
150
- else:
151
- logger.info(
152
- f"There is no signal !! SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
153
- check(buys[symbol], sells[symbol], symbol)
154
238
  else:
155
239
  logger.info(
156
- f"Sorry It is Not trading Time !!! SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
240
+ f"Not trading Time !!! SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
157
241
  check(buys[symbol], sells[symbol], symbol)
158
242
 
159
243
  except Exception as e:
@@ -171,85 +255,93 @@ def _mt5_execution(
171
255
  )
172
256
  print()
173
257
  try:
258
+ FRIDAY = 'friday'
174
259
  check_mt5_connection()
175
260
  day_end = all(trade.days_end() for trade in trades_instances.values())
261
+ if closing_pnl is not None:
262
+ closing = all(trade.positive_profit(id=trade.expert_id, th=closing_pnl)
263
+ for trade in trades_instances.values())
264
+ else: closing = True
265
+ logmsg = lambda period: logger.info(
266
+ f"End of the {period} !!! SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
267
+ sleepmsg = lambda sleep_time: logger.info(
268
+ f"Sleeping for {sleep_time} minutes ...\n")
269
+ sessionmsg = f"STARTING NEW TRADING SESSION ...\n"
176
270
  if period.lower() == 'day':
177
271
  for symbol in symbols:
178
272
  trade = trades_instances[symbol]
179
- if trade.days_end():
273
+ if trade.days_end() and closing:
180
274
  trade.close_positions(position_type='all', comment=comment)
181
- logger.info(
182
- f"End of the Day !!! SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
275
+ logmsg("Day")
183
276
  trade.statistics(save=True)
184
277
  if day_end:
185
- if period_end_action == 'break':
278
+ if period_end_action == 'break' and closing:
186
279
  break
187
- elif period_end_action == 'sleep':
280
+ elif period_end_action == 'sleep' and today != FRIDAY or not closing:
188
281
  sleep_time = trades_instances[symbols[-1]].sleep_time()
189
- logger.info(f"Sleeping for {sleep_time} minutes ...\n")
282
+ sleepmsg(sleep_time)
190
283
  time.sleep(60 * sleep_time)
191
- logger.info("STARTING NEW TRADING SESSION ...\n")
284
+ logger.info(sessionmsg)
285
+ elif period_end_action == 'sleep' and today == FRIDAY:
286
+ sleep_time = trades_instances[symbols[-1]].sleep_time(weekend=True)
287
+ sleepmsg(sleep_time)
288
+ time.sleep(60 * sleep_time)
289
+ logger.info(sessionmsg)
192
290
 
193
291
  elif period.lower() == 'week':
194
292
  for symbol in symbols:
195
293
  trade = trades_instances[symbol]
196
- if trade.days_end() and today != 'friday':
197
- logger.info(
198
- f"End of the Day !!! SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
294
+ if trade.days_end() and today != FRIDAY:
295
+ logmsg("Day")
199
296
 
200
- elif trade.days_end() and today == 'friday':
297
+ elif trade.days_end() and today == FRIDAY and closing:
201
298
  trade.close_positions(position_type='all', comment=comment)
202
- logger.info(
203
- f"End of the Week !!! SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
299
+ logmsg("Week")
204
300
  trade.statistics(save=True)
205
- if day_end and today != 'friday':
301
+ if day_end and today != FRIDAY:
206
302
  sleep_time = trades_instances[symbols[-1]].sleep_time()
207
- logger.info(f"Sleeping for {sleep_time} minutes ...\n")
303
+ sleepmsg(sleep_time)
208
304
  time.sleep(60 * sleep_time)
209
- logger.info("STARTING NEW TRADING SESSION ...\n")
210
- elif day_end and today == 'friday':
211
- if period_end_action == 'break':
305
+ logger.info(sessionmsg)
306
+ elif day_end and today == FRIDAY:
307
+ if period_end_action == 'break' and closing:
212
308
  break
213
- elif period_end_action == 'sleep':
309
+ elif period_end_action == 'sleep' or not closing:
214
310
  sleep_time = trades_instances[symbols[-1]].sleep_time(weekend=True)
215
- logger.info(f"Sleeping for {sleep_time} minutes ...\n")
311
+ sleepmsg(sleep_time)
216
312
  time.sleep(60 * sleep_time)
217
- logger.info("STARTING NEW TRADING SESSION ...\n")
313
+ logger.info(sessionmsg)
218
314
 
219
315
  elif period.lower() == 'month':
220
316
  for symbol in symbols:
221
317
  trade = trades_instances[symbol]
222
- if trade.days_end() and today != 'friday':
223
- logger.info(
224
- f"End of the Day !!! SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
225
-
226
- elif trade.days_end() and today == 'friday':
227
- logger.info(
228
- f"End of the Week !!! SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
318
+ if trade.days_end() and today != FRIDAY:
319
+ logmsg("Day")
320
+ elif trade.days_end() and today == FRIDAY:
321
+ logmsg("Week")
229
322
  elif (
230
323
  trade.days_end()
231
- and today == 'friday'
324
+ and today == FRIDAY
232
325
  and num_days/len(symbols) >= 20
233
- ):
326
+ ) and closing:
234
327
  trade.close_positions(position_type='all', comment=comment)
235
- logger.info(
236
- f"End of the Month !!! SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
328
+ logmsg("Month")
237
329
  trade.statistics(save=True)
238
- if day_end and today != 'friday':
330
+ if day_end and today != FRIDAY:
239
331
  sleep_time = trades_instances[symbols[-1]].sleep_time()
240
- logger.info(f"Sleeping for {sleep_time} minutes ...\n")
332
+ sleepmsg(sleep_time)
241
333
  time.sleep(60 * sleep_time)
242
- logger.info("STARTING NEW TRADING SESSION ...\n")
334
+ logger.info(sessionmsg)
243
335
  num_days += 1
244
- elif day_end and today == 'friday':
336
+ elif day_end and today == FRIDAY:
245
337
  sleep_time = trades_instances[symbols[-1]
246
338
  ].sleep_time(weekend=True)
247
- logger.info(f"Sleeping for {sleep_time} minutes ...\n")
339
+ sleepmsg(sleep_time)
248
340
  time.sleep(60 * sleep_time)
249
- logger.info("STARTING NEW TRADING SESSION ...\n")
341
+ logger.info(sessionmsg)
250
342
  num_days += 1
251
343
  elif (day_end
252
- and today == 'friday'
344
+ and today == FRIDAY
253
345
  and num_days/len(symbols) >= 20
254
346
  ):
255
347
  break
@@ -261,33 +353,30 @@ def _mt5_execution(
261
353
  def _tws_execution(*args, **kwargs):
262
354
  raise NotImplementedError("TWS Execution is not yet implemented !!!")
263
355
 
264
- _TERMINALS = {
265
- 'MT5': _mt5_execution,
266
- 'TWS': _tws_execution
267
- }
268
- class ExecutionEngine():
356
+
357
+ class MT5ExecutionEngine():
269
358
  """
270
- The `ExecutionEngine` class serves as the central hub for executing your trading strategies within the `bbstrader` framework.
359
+ The `MT5ExecutionEngine` class serves as the central hub for executing your trading strategies within the `bbstrader` framework.
271
360
  It orchestrates the entire trading process, ensuring seamless interaction between your strategies, market data, and your chosen
272
- trading platform (currently MetaTrader 5 (MT5) and Interactive Brokers TWS).
361
+ trading platform.
273
362
 
274
363
  Key Features
275
364
  ------------
276
365
 
277
- - **Strategy Execution:** The `ExecutionEngine` is responsible for running your strategy, retrieving signals, and executing trades based on those signals.
366
+ - **Strategy Execution:** The `MT5ExecutionEngine` is responsible for running your strategy, retrieving signals, and executing trades based on those signals.
278
367
  - **Time Management:** You can define a specific time frame for your trades and set the frequency with which the engine checks for signals and manages trades.
279
368
  - **Trade Period Control:** Define whether your strategy runs for a day, a week, or a month, allowing for flexible trading durations.
280
369
  - **Money Management:** The engine supports optional money management features, allowing you to control risk and optimize your trading performance.
281
370
  - **Trading Day Configuration:** You can customize the days of the week your strategy will execute, providing granular control over your trading schedule.
282
- - **Platform Integration:** The `ExecutionEngine` is currently designed to work with both MT5 and TWS platforms, ensuring compatibility and flexibility in your trading environment.
371
+ - **Platform Integration:** The `MT5ExecutionEngine` is currently designed to work with MT5.
283
372
 
284
373
  Examples
285
374
  --------
286
375
 
287
376
  >>> from bbstrader.metatrader import create_trade_instance
288
- >>> from bbstrader.trading.execution import ExecutionEngine
377
+ >>> from bbstrader.trading.execution import MT5ExecutionEngine
289
378
  >>> from bbstrader.trading.strategies import StockIndexCFDTrading
290
- >>> from bbstrader.metatrader.utils import config_logger
379
+ >>> from bbstrader.config import config_logger
291
380
  >>>
292
381
  >>> if __name__ == '__main__':
293
382
  >>> logger = config_logger(index_trade.log, console_log=True)
@@ -325,7 +414,7 @@ class ExecutionEngine():
325
414
  ... logger=logger,
326
415
  ... )
327
416
  >>>
328
- >>> engine = ExecutionEngine(
417
+ >>> engine = MT5ExecutionEngine(
329
418
  ... symbol_list,
330
419
  ... trades_instances,
331
420
  ... StockIndexCFDTrading,
@@ -336,7 +425,7 @@ class ExecutionEngine():
336
425
  ... comment='bbs_SISTBO_@2.0',
337
426
  ... **strategy_kwargs
338
427
  ... )
339
- >>> engine.run(terminal='MT5')
428
+ >>> engine.run()
340
429
  """
341
430
 
342
431
  def __init__(self,
@@ -344,15 +433,18 @@ class ExecutionEngine():
344
433
  trades_instances: Dict[str, Trade],
345
434
  strategy_cls: Strategy,
346
435
  /,
347
- mm: Optional[bool] = True,
348
- trail: Optional[bool] = True,
436
+ mm: bool = True,
437
+ trail: bool = True,
349
438
  stop_trail: Optional[int] = None,
350
439
  trail_after_points: Optional[int] = None,
351
440
  be_plus_points: Optional[int] = None,
352
- time_frame: Optional[str] = '15m',
353
- iter_time: Optional[int | float] = 5,
441
+ show_positions_orders: bool = False,
442
+ time_frame: str = '15m',
443
+ iter_time: int | float = 5,
444
+ use_trade_time: bool = True,
354
445
  period: Literal['day', 'week', 'month'] = 'week',
355
446
  period_end_action: Literal['break', 'sleep'] = 'break',
447
+ closing_pnl: Optional[float] = None,
356
448
  trading_days: Optional[List[str]] = TradingDays,
357
449
  comment: Optional[str] = None,
358
450
  **kwargs
@@ -363,20 +455,27 @@ class ExecutionEngine():
363
455
  trades_instances : Dictionary of Trade instances
364
456
  strategy_cls : Strategy class to use for trading
365
457
  mm : Enable Money Management. Defaults to False.
458
+ show_positions_orders : Print open positions and orders. Defaults to False.
366
459
  time_frame : Time frame to trade. Defaults to '15m'.
367
460
  iter_time : Interval to check for signals and `mm`. Defaults to 5.
461
+ use_trade_time : Open trades after the time is completed. Defaults to True.
368
462
  period : Period to trade. Defaults to 'week'.
369
463
  period_end_action : Action to take at the end of the period. Defaults to 'break',
370
464
  this only applies when period is 'day', 'week'.
465
+ closing_pnl : Minimum profit in percentage of target profit to close positions. Defaults to -0.001.
371
466
  trading_days : Trading days in a week. Defaults to monday to friday.
372
467
  comment: Comment for trades. Defaults to None.
373
468
  **kwargs: Additional keyword arguments
374
469
  - strategy_name (Optional[str]): Strategy name. Defaults to None.
375
470
  - max_trades (Dict[str, int]): Maximum trades per symbol. Defaults to None.
471
+ - notify (bool): Enable notifications. Defaults to False.
472
+ - telegram (bool): Enable telegram notifications. Defaults to False.
473
+ - bot_token (str): Telegram bot token. Defaults to None.
474
+ - chat_id (Union[int, str, List] ): Telegram chat id. Defaults to None.
376
475
 
377
476
  Note:
378
477
  1. For `trail` , `stop_trail` , `trail_after_points` , `be_plus_points` see `bbstrader.metatrader.trade.Trade.break_even()` .
379
- 2. All Strategies must inherit from `bbstrader.btengine.strategy.Strategy` class
478
+ 2. All Strategies must inherit from `bbstrader.btengine.strategy.Strategy` or `bbstrader.btengine.strategy.MT5Strategy` class
380
479
  and have a `calculate_signals` method that returns a dictionary of signals for each symbol in symbol_list.
381
480
 
382
481
  3. All strategies must have the following arguments in their `__init__` method:
@@ -385,7 +484,7 @@ class ExecutionEngine():
385
484
  - symbol_list (List[str]): List of symbols to trade can be none for backtesting
386
485
  - mode (str): Mode of the strategy. Must be either 'live' or 'backtest'
387
486
  - **kwargs: Additional keyword arguments
388
- The keyword arguments are all the additional arguments passed to the `ExecutionEngine` class,
487
+ The keyword arguments are all the additional arguments passed to the `MT5ExecutionEngine` class,
389
488
  the `Strategy` class, the `DataHandler` class, the `Portfolio` class and the `ExecutionHandler` class.
390
489
  - The `bars` and `events` arguments are used for backtesting only.
391
490
 
@@ -400,34 +499,39 @@ class ExecutionEngine():
400
499
  self.stop_trail = stop_trail
401
500
  self.trail_after_points = trail_after_points
402
501
  self.be_plus_points = be_plus_points
502
+ self.show_positions_orders = show_positions_orders
403
503
  self.time_frame = time_frame
404
504
  self.iter_time = iter_time
505
+ self.use_trade_time = use_trade_time
405
506
  self.period = period
406
507
  self.period_end_action = period_end_action
508
+ self.closing_pnl = closing_pnl
407
509
  self.trading_days = trading_days
408
510
  self.comment = comment
409
511
  self.kwargs = kwargs
410
512
 
411
- def run(self, terminal: Literal['MT5', 'TWS']):
412
- if terminal not in _TERMINALS:
413
- raise ValueError(
414
- f"Invalid terminal: {terminal}. Must be either 'MT5' or 'TWS'")
415
- elif terminal == 'MT5':
416
- check_mt5_connection()
417
- _TERMINALS[terminal](
418
- self.symbol_list,
419
- self.trades_instances,
420
- self.strategy_cls,
421
- mm=self.mm,
422
- trail=self.trail,
423
- stop_trail=self.stop_trail,
424
- trail_after_points=self.trail_after_points,
425
- be_plus_points=self.be_plus_points,
426
- time_frame=self.time_frame,
427
- iter_time=self.iter_time,
428
- period=self.period,
429
- period_end_action=self.period_end_action,
430
- trading_days=self.trading_days,
431
- comment=self.comment,
432
- **self.kwargs
433
- )
513
+ def run(self):
514
+ check_mt5_connection()
515
+ _mt5_execution(
516
+ self.symbol_list,
517
+ self.trades_instances,
518
+ self.strategy_cls,
519
+ mm=self.mm,
520
+ trail=self.trail,
521
+ stop_trail=self.stop_trail,
522
+ trail_after_points=self.trail_after_points,
523
+ be_plus_points=self.be_plus_points,
524
+ show_positions_orders=self.show_positions_orders,
525
+ time_frame=self.time_frame,
526
+ iter_time=self.iter_time,
527
+ use_trade_time=self.use_trade_time,
528
+ period=self.period,
529
+ period_end_action=self.period_end_action,
530
+ closing_pnl=self.closing_pnl,
531
+ trading_days=self.trading_days,
532
+ comment=self.comment,
533
+ **self.kwargs
534
+ )
535
+
536
+
537
+ class TWSExecutionEngine():...