bbstrader 0.1.9__py3-none-any.whl → 0.1.92__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,50 +44,83 @@ 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(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}")
106
+ if buys is not None or sells is not None:
52
107
  trades_instances[symbol].break_even(
53
108
  mm=mm, trail=trail, stop_trail=stop_trail,
54
109
  trail_after_points=trail_after_points, be_plus_points=be_plus_points)
55
- if sells is not None:
56
- logger.info(
57
- f"Checking for Break even, SYMBOL={symbol}...STRATEGY={STRATEGY}")
58
- trades_instances[symbol].break_even(
59
- mm=mm, trail=trail, stop_trail=stop_trail,
60
- 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]
110
+ num_days = 0
111
+ time_intervals = 0
112
+ trade_time = _TF_MAPPING[time_frame]
64
113
 
65
- long_market = {symbol: False for symbol in symbols}
66
- short_market = {symbol: False for symbol in symbols}
114
+ long_market = {symbol: False for symbol in symbols}
115
+ short_market = {symbol: False for symbol in symbols}
67
116
  try:
68
117
  check_mt5_connection()
69
- strategy: Strategy = strategy_cls(symbol_list=symbols, mode='live', **kwargs)
118
+ strategy: MT5Strategy = strategy_cls(symbol_list=symbols, mode='live', **kwargs)
70
119
  except Exception as e:
71
- logger.error(f"Error initializing strategy, {e}, STRATEGY={STRATEGY}")
120
+ logger.error(f"Initializing strategy, {e}, STRATEGY={STRATEGY}")
72
121
  return
73
122
  logger.info(
74
- f'Running {STRATEGY} Strategy on {symbols} in {time_frame} Interval ...')
123
+ f'Running {STRATEGY} Strategy in {time_frame} Interval ...')
75
124
 
76
125
  while True:
77
126
  try:
@@ -79,27 +128,33 @@ def _mt5_execution(
79
128
  current_date = datetime.now()
80
129
  today = current_date.strftime("%A").lower()
81
130
  time.sleep(0.5)
82
- buys = {
83
- symbol: trades_instances[symbol].get_current_buys()
84
- for symbol in symbols
131
+ positions_orders = {}
132
+ for type in POSITIONS_TYPES + ORDERS_TYPES:
133
+ positions_orders[type] = {}
134
+ for symbol in symbols:
135
+ positions_orders[type][symbol] = None
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
- logger.error(f"{e}, STRATEGY={STRATEGY}")
157
+ logger.error(f"Handling Positions and Orders, {e}, STRATEGY={STRATEGY}")
103
158
  continue
104
159
  time.sleep(0.5)
105
160
  try:
@@ -111,53 +166,85 @@ 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
+ if signal not in EXIT_SIGNAL_ACTIONS:
187
+ logger.info(info)
188
+ if signal in EXIT_SIGNAL_ACTIONS:
189
+ for exit_signal, actions in EXIT_SIGNAL_ACTIONS.items():
190
+ for position_type, order_type in actions.items():
191
+ if positions_orders[position_type][symbol] is not None:
192
+ if notify:
193
+ _send_notification(info)
194
+ if position_type in POSITIONS_TYPES:
195
+ trade.close_positions(position_type=order_type)
196
+ else:
197
+ trade.close_orders(order_type=order_type)
198
+ elif signal in BUYS and not long_market[symbol]:
199
+ if use_trade_time:
200
+ if time_intervals % trade_time == 0 or buys[symbol] is None:
201
+ logger.info(msg)
202
+ if notify:
203
+ _send_notification(info)
204
+ trade.open_buy_position(
205
+ action=signal, price=price, stoplimit=stoplimit, mm=mm, comment=comment)
206
+ else:
207
+ logger.info(tfmsg)
208
+ check(buys[symbol], sells[symbol], symbol)
131
209
  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}")
210
+ logger.info(msg)
211
+ if notify:
212
+ _send_notification(info)
213
+ trade.open_buy_position(
214
+ action=signal, price=price, stoplimit=stoplimit, mm=mm, comment=comment)
215
+ elif signal in BUYS and long_market[symbol]:
216
+ logger.info(riskmsg)
136
217
  check(buys[symbol], sells[symbol], symbol)
137
218
 
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)
219
+ elif signal in SELLS and not short_market[symbol]:
220
+ if use_trade_time:
221
+ if time_intervals % trade_time == 0 or sells[symbol] is None:
222
+ logger.info(msg)
223
+ if notify:
224
+ _send_notification(info)
225
+ trade.open_sell_position(
226
+ action=signal, price=price, stoplimit=stoplimit, mm=mm, comment=comment)
227
+ else:
228
+ logger.info(tfmsg)
229
+ check(buys[symbol], sells[symbol], symbol)
144
230
  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}")
231
+ logger.info(msg)
232
+ if notify:
233
+ _send_notification(info)
234
+ trade.open_sell_position(
235
+ action=signal, price=price, stoplimit=stoplimit, mm=mm, comment=comment)
236
+ elif signal in SELLS and short_market[symbol]:
237
+ logger.info(riskmsg)
149
238
  check(buys[symbol], sells[symbol], symbol)
150
239
  else:
151
- logger.info(
152
- f"There is no signal !! SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
153
240
  check(buys[symbol], sells[symbol], symbol)
154
241
  else:
155
242
  logger.info(
156
- f"Sorry It is Not trading Time !!! SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
243
+ f"Not trading Time !!! SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
157
244
  check(buys[symbol], sells[symbol], symbol)
158
245
 
159
246
  except Exception as e:
160
- logger.error(f"{e}, SYMBOL={symbol}, STRATEGY={STRATEGY}")
247
+ logger.error(f"Handling Signals {e}, SYMBOL={symbol}, STRATEGY={STRATEGY}")
161
248
  continue
162
249
  time.sleep((60 * iter_time) - 1.0)
163
250
  if iter_time == 1:
@@ -169,87 +256,94 @@ def _mt5_execution(
169
256
  f"iter_time must be a multiple of the {time_frame} !!!"
170
257
  f"(e.g; if time_frame is 15m, iter_time must be 1.5, 3, 3, 15 etc)"
171
258
  )
172
- print()
173
259
  try:
260
+ FRIDAY = 'friday'
174
261
  check_mt5_connection()
175
262
  day_end = all(trade.days_end() for trade in trades_instances.values())
263
+ if closing_pnl is not None:
264
+ closing = all(trade.positive_profit(id=trade.expert_id, th=closing_pnl)
265
+ for trade in trades_instances.values())
266
+ else: closing = True
267
+ logmsg = lambda period: logger.info(
268
+ f"End of the {period} !!! SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
269
+ sleepmsg = lambda sleep_time: logger.info(
270
+ f"Sleeping for {sleep_time} minutes ...\n")
271
+ sessionmsg = f"STARTING NEW TRADING SESSION ...\n"
176
272
  if period.lower() == 'day':
177
273
  for symbol in symbols:
178
274
  trade = trades_instances[symbol]
179
- if trade.days_end():
275
+ if trade.days_end() and closing:
180
276
  trade.close_positions(position_type='all', comment=comment)
181
- logger.info(
182
- f"End of the Day !!! SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
277
+ logmsg("Day")
183
278
  trade.statistics(save=True)
184
279
  if day_end:
185
- if period_end_action == 'break':
280
+ if period_end_action == 'break' and closing:
186
281
  break
187
- elif period_end_action == 'sleep':
282
+ elif period_end_action == 'sleep' and today != FRIDAY or not closing:
188
283
  sleep_time = trades_instances[symbols[-1]].sleep_time()
189
- logger.info(f"Sleeping for {sleep_time} minutes ...\n")
284
+ sleepmsg(sleep_time)
190
285
  time.sleep(60 * sleep_time)
191
- logger.info("STARTING NEW TRADING SESSION ...\n")
286
+ logger.info(sessionmsg)
287
+ elif period_end_action == 'sleep' and today == FRIDAY:
288
+ sleep_time = trades_instances[symbols[-1]].sleep_time(weekend=True)
289
+ sleepmsg(sleep_time)
290
+ time.sleep(60 * sleep_time)
291
+ logger.info(sessionmsg)
192
292
 
193
293
  elif period.lower() == 'week':
194
294
  for symbol in symbols:
195
295
  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}")
296
+ if trade.days_end() and today != FRIDAY:
297
+ logmsg("Day")
199
298
 
200
- elif trade.days_end() and today == 'friday':
299
+ elif trade.days_end() and today == FRIDAY and closing:
201
300
  trade.close_positions(position_type='all', comment=comment)
202
- logger.info(
203
- f"End of the Week !!! SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
301
+ logmsg("Week")
204
302
  trade.statistics(save=True)
205
- if day_end and today != 'friday':
303
+ if day_end and today != FRIDAY:
206
304
  sleep_time = trades_instances[symbols[-1]].sleep_time()
207
- logger.info(f"Sleeping for {sleep_time} minutes ...\n")
305
+ sleepmsg(sleep_time)
208
306
  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':
307
+ logger.info(sessionmsg)
308
+ elif day_end and today == FRIDAY:
309
+ if period_end_action == 'break' and closing:
212
310
  break
213
- elif period_end_action == 'sleep':
311
+ elif period_end_action == 'sleep' or not closing:
214
312
  sleep_time = trades_instances[symbols[-1]].sleep_time(weekend=True)
215
- logger.info(f"Sleeping for {sleep_time} minutes ...\n")
313
+ sleepmsg(sleep_time)
216
314
  time.sleep(60 * sleep_time)
217
- logger.info("STARTING NEW TRADING SESSION ...\n")
315
+ logger.info(sessionmsg)
218
316
 
219
317
  elif period.lower() == 'month':
220
318
  for symbol in symbols:
221
319
  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}")
320
+ if trade.days_end() and today != FRIDAY:
321
+ logmsg("Day")
322
+ elif trade.days_end() and today == FRIDAY:
323
+ logmsg("Week")
229
324
  elif (
230
325
  trade.days_end()
231
- and today == 'friday'
326
+ and today == FRIDAY
232
327
  and num_days/len(symbols) >= 20
233
- ):
328
+ ) and closing:
234
329
  trade.close_positions(position_type='all', comment=comment)
235
- logger.info(
236
- f"End of the Month !!! SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
330
+ logmsg("Month")
237
331
  trade.statistics(save=True)
238
- if day_end and today != 'friday':
332
+ if day_end and today != FRIDAY:
239
333
  sleep_time = trades_instances[symbols[-1]].sleep_time()
240
- logger.info(f"Sleeping for {sleep_time} minutes ...\n")
334
+ sleepmsg(sleep_time)
241
335
  time.sleep(60 * sleep_time)
242
- logger.info("STARTING NEW TRADING SESSION ...\n")
336
+ logger.info(sessionmsg)
243
337
  num_days += 1
244
- elif day_end and today == 'friday':
338
+ elif day_end and today == FRIDAY:
245
339
  sleep_time = trades_instances[symbols[-1]
246
340
  ].sleep_time(weekend=True)
247
- logger.info(f"Sleeping for {sleep_time} minutes ...\n")
341
+ sleepmsg(sleep_time)
248
342
  time.sleep(60 * sleep_time)
249
- logger.info("STARTING NEW TRADING SESSION ...\n")
343
+ logger.info(sessionmsg)
250
344
  num_days += 1
251
345
  elif (day_end
252
- and today == 'friday'
346
+ and today == FRIDAY
253
347
  and num_days/len(symbols) >= 20
254
348
  ):
255
349
  break
@@ -261,33 +355,30 @@ def _mt5_execution(
261
355
  def _tws_execution(*args, **kwargs):
262
356
  raise NotImplementedError("TWS Execution is not yet implemented !!!")
263
357
 
264
- _TERMINALS = {
265
- 'MT5': _mt5_execution,
266
- 'TWS': _tws_execution
267
- }
268
- class ExecutionEngine():
358
+
359
+ class MT5ExecutionEngine():
269
360
  """
270
- The `ExecutionEngine` class serves as the central hub for executing your trading strategies within the `bbstrader` framework.
361
+ The `MT5ExecutionEngine` class serves as the central hub for executing your trading strategies within the `bbstrader` framework.
271
362
  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).
363
+ trading platform.
273
364
 
274
365
  Key Features
275
366
  ------------
276
367
 
277
- - **Strategy Execution:** The `ExecutionEngine` is responsible for running your strategy, retrieving signals, and executing trades based on those signals.
368
+ - **Strategy Execution:** The `MT5ExecutionEngine` is responsible for running your strategy, retrieving signals, and executing trades based on those signals.
278
369
  - **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
370
  - **Trade Period Control:** Define whether your strategy runs for a day, a week, or a month, allowing for flexible trading durations.
280
371
  - **Money Management:** The engine supports optional money management features, allowing you to control risk and optimize your trading performance.
281
372
  - **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.
373
+ - **Platform Integration:** The `MT5ExecutionEngine` is currently designed to work with MT5.
283
374
 
284
375
  Examples
285
376
  --------
286
377
 
287
378
  >>> from bbstrader.metatrader import create_trade_instance
288
- >>> from bbstrader.trading.execution import ExecutionEngine
379
+ >>> from bbstrader.trading.execution import MT5ExecutionEngine
289
380
  >>> from bbstrader.trading.strategies import StockIndexCFDTrading
290
- >>> from bbstrader.metatrader.utils import config_logger
381
+ >>> from bbstrader.config import config_logger
291
382
  >>>
292
383
  >>> if __name__ == '__main__':
293
384
  >>> logger = config_logger(index_trade.log, console_log=True)
@@ -325,7 +416,7 @@ class ExecutionEngine():
325
416
  ... logger=logger,
326
417
  ... )
327
418
  >>>
328
- >>> engine = ExecutionEngine(
419
+ >>> engine = MT5ExecutionEngine(
329
420
  ... symbol_list,
330
421
  ... trades_instances,
331
422
  ... StockIndexCFDTrading,
@@ -336,7 +427,7 @@ class ExecutionEngine():
336
427
  ... comment='bbs_SISTBO_@2.0',
337
428
  ... **strategy_kwargs
338
429
  ... )
339
- >>> engine.run(terminal='MT5')
430
+ >>> engine.run()
340
431
  """
341
432
 
342
433
  def __init__(self,
@@ -344,15 +435,18 @@ class ExecutionEngine():
344
435
  trades_instances: Dict[str, Trade],
345
436
  strategy_cls: Strategy,
346
437
  /,
347
- mm: Optional[bool] = True,
348
- trail: Optional[bool] = True,
438
+ mm: bool = True,
439
+ trail: bool = True,
349
440
  stop_trail: Optional[int] = None,
350
441
  trail_after_points: Optional[int] = None,
351
442
  be_plus_points: Optional[int] = None,
352
- time_frame: Optional[str] = '15m',
353
- iter_time: Optional[int | float] = 5,
443
+ show_positions_orders: bool = False,
444
+ time_frame: str = '15m',
445
+ iter_time: int | float = 5,
446
+ use_trade_time: bool = True,
354
447
  period: Literal['day', 'week', 'month'] = 'week',
355
448
  period_end_action: Literal['break', 'sleep'] = 'break',
449
+ closing_pnl: Optional[float] = None,
356
450
  trading_days: Optional[List[str]] = TradingDays,
357
451
  comment: Optional[str] = None,
358
452
  **kwargs
@@ -363,20 +457,27 @@ class ExecutionEngine():
363
457
  trades_instances : Dictionary of Trade instances
364
458
  strategy_cls : Strategy class to use for trading
365
459
  mm : Enable Money Management. Defaults to False.
460
+ show_positions_orders : Print open positions and orders. Defaults to False.
366
461
  time_frame : Time frame to trade. Defaults to '15m'.
367
462
  iter_time : Interval to check for signals and `mm`. Defaults to 5.
463
+ use_trade_time : Open trades after the time is completed. Defaults to True.
368
464
  period : Period to trade. Defaults to 'week'.
369
465
  period_end_action : Action to take at the end of the period. Defaults to 'break',
370
466
  this only applies when period is 'day', 'week'.
467
+ closing_pnl : Minimum profit in percentage of target profit to close positions. Defaults to -0.001.
371
468
  trading_days : Trading days in a week. Defaults to monday to friday.
372
469
  comment: Comment for trades. Defaults to None.
373
470
  **kwargs: Additional keyword arguments
374
471
  - strategy_name (Optional[str]): Strategy name. Defaults to None.
375
472
  - max_trades (Dict[str, int]): Maximum trades per symbol. Defaults to None.
473
+ - notify (bool): Enable notifications. Defaults to False.
474
+ - telegram (bool): Enable telegram notifications. Defaults to False.
475
+ - bot_token (str): Telegram bot token. Defaults to None.
476
+ - chat_id (Union[int, str, List] ): Telegram chat id. Defaults to None.
376
477
 
377
478
  Note:
378
479
  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
480
+ 2. All Strategies must inherit from `bbstrader.btengine.strategy.Strategy` or `bbstrader.btengine.strategy.MT5Strategy` class
380
481
  and have a `calculate_signals` method that returns a dictionary of signals for each symbol in symbol_list.
381
482
 
382
483
  3. All strategies must have the following arguments in their `__init__` method:
@@ -385,7 +486,7 @@ class ExecutionEngine():
385
486
  - symbol_list (List[str]): List of symbols to trade can be none for backtesting
386
487
  - mode (str): Mode of the strategy. Must be either 'live' or 'backtest'
387
488
  - **kwargs: Additional keyword arguments
388
- The keyword arguments are all the additional arguments passed to the `ExecutionEngine` class,
489
+ The keyword arguments are all the additional arguments passed to the `MT5ExecutionEngine` class,
389
490
  the `Strategy` class, the `DataHandler` class, the `Portfolio` class and the `ExecutionHandler` class.
390
491
  - The `bars` and `events` arguments are used for backtesting only.
391
492
 
@@ -400,34 +501,39 @@ class ExecutionEngine():
400
501
  self.stop_trail = stop_trail
401
502
  self.trail_after_points = trail_after_points
402
503
  self.be_plus_points = be_plus_points
504
+ self.show_positions_orders = show_positions_orders
403
505
  self.time_frame = time_frame
404
506
  self.iter_time = iter_time
507
+ self.use_trade_time = use_trade_time
405
508
  self.period = period
406
509
  self.period_end_action = period_end_action
510
+ self.closing_pnl = closing_pnl
407
511
  self.trading_days = trading_days
408
512
  self.comment = comment
409
513
  self.kwargs = kwargs
410
514
 
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
- )
515
+ def run(self):
516
+ check_mt5_connection()
517
+ _mt5_execution(
518
+ self.symbol_list,
519
+ self.trades_instances,
520
+ self.strategy_cls,
521
+ mm=self.mm,
522
+ trail=self.trail,
523
+ stop_trail=self.stop_trail,
524
+ trail_after_points=self.trail_after_points,
525
+ be_plus_points=self.be_plus_points,
526
+ show_positions_orders=self.show_positions_orders,
527
+ time_frame=self.time_frame,
528
+ iter_time=self.iter_time,
529
+ use_trade_time=self.use_trade_time,
530
+ period=self.period,
531
+ period_end_action=self.period_end_action,
532
+ closing_pnl=self.closing_pnl,
533
+ trading_days=self.trading_days,
534
+ comment=self.comment,
535
+ **self.kwargs
536
+ )
537
+
538
+
539
+ class TWSExecutionEngine():...