bbstrader 0.1.8__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,24 +1,39 @@
1
1
  import time
2
2
  from datetime import datetime
3
3
  from bbstrader.metatrader.trade import Trade
4
- from bbstrader.trading.strategies import Strategy
5
- from typing import Optional, Literal, List, Tuple, Dict
6
- import MetaTrader5 as mt5
7
- from bbstrader.metatrader.account import INIT_MSG
8
- from bbstrader.metatrader.utils import raise_mt5_error
4
+ from bbstrader.btengine.strategy import(
5
+ Strategy,
6
+ MT5Strategy
7
+ )
8
+ from bbstrader.trading.scripts import send_message
9
+ from bbstrader.metatrader.account import check_mt5_connection
10
+ from typing import (
11
+ Optional,
12
+ Literal,
13
+ Tuple,
14
+ List,
15
+ Dict
16
+ )
9
17
 
18
+ __all__ = [
19
+ 'MT5ExecutionEngine',
20
+ 'TWSExecutionEngine'
21
+ ]
10
22
 
11
23
  _TF_MAPPING = {
12
- '1m': 1,
13
- '3m': 3,
14
- '5m': 5,
15
- '10m': 10,
16
- '15m': 15,
17
- '30m': 30,
18
- '1h': 60,
19
- '2h': 120,
20
- '4h': 240,
21
- '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
22
37
  }
23
38
 
24
39
  TradingDays = [
@@ -29,133 +44,205 @@ TradingDays = [
29
44
  'friday'
30
45
  ]
31
46
 
32
- def _check_mt5_connection():
33
- try:
34
- init = mt5.initialize()
35
- if not init:
36
- raise_mt5_error(INIT_MSG)
37
- except Exception:
38
- raise_mt5_error(INIT_MSG)
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
+
39
78
 
40
79
  def _mt5_execution(
41
80
  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,
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,
44
83
  comment, **kwargs):
45
- symbols = symbol_list.copy()
84
+ symbols = symbol_list.copy()
46
85
  STRATEGY = kwargs.get('strategy_name')
47
- _max_trades = kwargs.get('max_trades')
48
- logger = kwargs.get('logger')
49
- 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}
50
99
  if comment is None:
51
100
  trade = trades_instances[symbols[0]]
52
101
  comment = f"{trade.expert_name}@{trade.version}"
53
102
 
54
- def check(buys: List, sells: List, symbol: str):
103
+ def check(buys, sells, symbol):
55
104
  if not mm:
56
105
  return
57
- if buys is not None:
106
+ if buys is not None or sells is not None:
58
107
  logger.info(
59
108
  f"Checking for Break even, SYMBOL={symbol}...STRATEGY={STRATEGY}")
60
109
  trades_instances[symbol].break_even(
61
110
  mm=mm, trail=trail, stop_trail=stop_trail,
62
111
  trail_after_points=trail_after_points, be_plus_points=be_plus_points)
63
- if sells is not None:
64
- logger.info(
65
- f"Checking for Break even, SYMBOL={symbol}...STRATEGY={STRATEGY}")
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)
69
- num_days = 0
70
- time_intervals = 0
71
- trade_time = _TF_MAPPING[time_frame]
72
-
73
- long_market = {symbol: False for symbol in symbols}
74
- short_market = {symbol: False for symbol in symbols}
112
+ num_days = 0
113
+ time_intervals = 0
114
+ trade_time = _TF_MAPPING[time_frame]
75
115
 
116
+ long_market = {symbol: False for symbol in symbols}
117
+ short_market = {symbol: False for symbol in symbols}
118
+ try:
119
+ check_mt5_connection()
120
+ strategy: MT5Strategy = strategy_cls(symbol_list=symbols, mode='live', **kwargs)
121
+ except Exception as e:
122
+ logger.error(f"Error initializing strategy, {e}, STRATEGY={STRATEGY}")
123
+ return
76
124
  logger.info(
77
125
  f'Running {STRATEGY} Strategy on {symbols} in {time_frame} Interval ...')
78
- strategy: Strategy = strategy_cls(
79
- symbol_list=symbols, mode='live', **kwargs)
80
-
126
+
81
127
  while True:
82
128
  try:
83
- _check_mt5_connection()
129
+ check_mt5_connection()
84
130
  current_date = datetime.now()
85
131
  today = current_date.strftime("%A").lower()
86
132
  time.sleep(0.5)
87
- buys = {
88
- symbol: trades_instances[symbol].get_current_buys()
89
- 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
90
150
  }
91
- sells = {
92
- symbol: trades_instances[symbol].get_current_sells()
93
- 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
94
154
  }
95
- for symbol in symbols:
96
- if buys[symbol] is not None:
97
- logger.info(
98
- f"Current buy positions SYMBOL={symbol}: {buys[symbol]}, STRATEGY={STRATEGY}")
99
- if sells[symbol] is not None:
100
- logger.info(
101
- f"Current sell positions SYMBOL={symbol}: {sells[symbol]}, STRATEGY={STRATEGY}")
102
- long_market = {symbol: buys[symbol] is not None and len(
103
- buys[symbol]) >= max_trades[symbol] for symbol in symbols}
104
- short_market = {symbol: sells[symbol] is not None and len(
105
- sells[symbol]) >= max_trades[symbol] for symbol in symbols}
155
+
106
156
  except Exception as e:
107
157
  logger.error(f"{e}, STRATEGY={STRATEGY}")
158
+ continue
108
159
  time.sleep(0.5)
160
+ try:
161
+ check_mt5_connection()
162
+ signals = strategy.calculate_signals()
163
+ except Exception as e:
164
+ logger.error(f"Calculating signal, {e}, STRATEGY={STRATEGY}")
165
+ continue
109
166
  for symbol in symbols:
110
167
  try:
111
- trade = trades_instances[symbol]
112
- logger.info(
113
- f"Calculating signal... SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
114
- signal = strategy.calculate_signals()[symbol]
168
+ check_mt5_connection()
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}"
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
115
180
  if trade.trading_time() and today in trading_days:
116
181
  if signal is not None:
117
- logger.info(
118
- f"SIGNAL = {signal}, SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
119
- if signal in ("EXIT", "EXIT_LONG") and long_market[symbol]:
120
- trade.close_positions(position_type='buy')
121
- elif signal in ("EXIT", "EXIT_SHORT") and short_market[symbol]:
122
- trade.close_positions(position_type='sell')
123
- elif signal == "LONG" and not long_market[symbol]:
124
- if time_intervals % trade_time == 0 or buys[symbol] is None:
125
- logger.info(
126
- f"Sending buy Order ... SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
127
- 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)
128
208
  else:
129
- check(buys[symbol], sells[symbol], symbol)
130
- elif signal == "LONG" and long_market[symbol]:
131
- logger.info(
132
- 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)
133
216
  check(buys[symbol], sells[symbol], symbol)
134
217
 
135
- elif signal == "SHORT" and not short_market[symbol]:
136
- if time_intervals % trade_time == 0 or sells[symbol] is None:
137
- logger.info(
138
- f"Sending sell Order ... SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
139
- trade.open_sell_position(
140
- 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)
141
229
  else:
142
- check(buys[symbol], sells[symbol], symbol)
143
- elif signal == "SHORT" and short_market[symbol]:
144
- logger.info(
145
- 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)
146
237
  check(buys[symbol], sells[symbol], symbol)
147
- else:
148
- logger.info(
149
- f"There is no signal !! SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
150
- check(buys[symbol], sells[symbol], symbol)
151
238
  else:
152
239
  logger.info(
153
- f"Sorry It is Not trading Time !!! SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
240
+ f"Not trading Time !!! SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
154
241
  check(buys[symbol], sells[symbol], symbol)
155
242
 
156
243
  except Exception as e:
157
244
  logger.error(f"{e}, SYMBOL={symbol}, STRATEGY={STRATEGY}")
158
-
245
+ continue
159
246
  time.sleep((60 * iter_time) - 1.0)
160
247
  if iter_time == 1:
161
248
  time_intervals += 1
@@ -167,118 +254,129 @@ def _mt5_execution(
167
254
  f"(e.g; if time_frame is 15m, iter_time must be 1.5, 3, 3, 15 etc)"
168
255
  )
169
256
  print()
170
- if period.lower() == 'day':
171
- for symbol in symbols:
172
- trade = trades_instances[symbol]
173
- if trade.days_end():
174
- trade.close_positions(position_type='all', comment=comment)
175
- logger.info(
176
- f"End of the Day !!! SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
177
- trade.statistics(save=True)
178
- if trades_instances[symbols[-1]].days_end():
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")
257
+ try:
258
+ FRIDAY = 'friday'
259
+ check_mt5_connection()
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"
270
+ if period.lower() == 'day':
271
+ for symbol in symbols:
272
+ trade = trades_instances[symbol]
273
+ if trade.days_end() and closing:
274
+ trade.close_positions(position_type='all', comment=comment)
275
+ logmsg("Day")
276
+ trade.statistics(save=True)
277
+ if day_end:
278
+ if period_end_action == 'break' and closing:
279
+ break
280
+ elif period_end_action == 'sleep' and today != FRIDAY or not closing:
281
+ sleep_time = trades_instances[symbols[-1]].sleep_time()
282
+ sleepmsg(sleep_time)
283
+ time.sleep(60 * sleep_time)
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)
186
290
 
187
- elif period.lower() == 'week':
188
- for symbol in symbols:
189
- trade = trades_instances[symbol]
190
- if trade.days_end() and today != 'friday':
191
- logger.info(
192
- f"End of the Day !!! SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
291
+ elif period.lower() == 'week':
292
+ for symbol in symbols:
293
+ trade = trades_instances[symbol]
294
+ if trade.days_end() and today != FRIDAY:
295
+ logmsg("Day")
193
296
 
194
- elif trade.days_end() and today == 'friday':
195
- trade.close_positions(position_type='all', comment=comment)
196
- logger.info(
197
- f"End of the Week !!! SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
198
- trade.statistics(save=True)
199
- if trades_instances[symbols[-1]].days_end() and today != 'friday':
200
- sleep_time = trades_instances[symbols[-1]].sleep_time()
201
- logger.info(f"Sleeping for {sleep_time} minutes ...")
202
- time.sleep(60 * sleep_time)
203
- logger.info("STARTING NEW TRADING SESSION ...\n")
204
- elif trades_instances[symbols[-1]].days_end() and today == 'friday':
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 ...")
297
+ elif trade.days_end() and today == FRIDAY and closing:
298
+ trade.close_positions(position_type='all', comment=comment)
299
+ logmsg("Week")
300
+ trade.statistics(save=True)
301
+ if day_end and today != FRIDAY:
302
+ sleep_time = trades_instances[symbols[-1]].sleep_time()
303
+ sleepmsg(sleep_time)
210
304
  time.sleep(60 * sleep_time)
211
- logger.info("STARTING NEW TRADING SESSION ...\n")
212
-
213
- elif period.lower() == 'month':
214
- for symbol in symbols:
215
- trade = trades_instances[symbol]
216
- if trade.days_end() and today != 'friday':
217
- logger.info(
218
- f"End of the Day !!! SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
305
+ logger.info(sessionmsg)
306
+ elif day_end and today == FRIDAY:
307
+ if period_end_action == 'break' and closing:
308
+ break
309
+ elif period_end_action == 'sleep' or not closing:
310
+ sleep_time = trades_instances[symbols[-1]].sleep_time(weekend=True)
311
+ sleepmsg(sleep_time)
312
+ time.sleep(60 * sleep_time)
313
+ logger.info(sessionmsg)
219
314
 
220
- elif trade.days_end() and today == 'friday':
221
- logger.info(
222
- f"End of the Week !!! SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
223
- elif (
224
- trade.days_end()
225
- and today == 'friday'
226
- and num_days/len(symbols) >= 20
227
- ):
228
- trade.close_positions(position_type='all', comment=comment)
229
- logger.info(
230
- f"End of the Month !!! SYMBOL={trade.symbol}, STRATEGY={STRATEGY}")
231
- trade.statistics(save=True)
232
- if trades_instances[symbols[-1]].days_end() and today != 'friday':
233
- sleep_time = trades_instances[symbols[-1]].sleep_time()
234
- logger.info(f"Sleeping for {sleep_time} minutes ...")
235
- time.sleep(60 * sleep_time)
236
- logger.info("STARTING NEW TRADING SESSION ...\n")
237
- num_days += 1
238
- elif trades_instances[symbols[-1]].days_end() and today == 'friday':
239
- sleep_time = trades_instances[symbols[-1]
240
- ].sleep_time(weekend=True)
241
- logger.info(f"Sleeping for {sleep_time} minutes ...")
242
- time.sleep(60 * sleep_time)
243
- logger.info("STARTING NEW TRADING SESSION ...\n")
244
- num_days += 1
245
- elif (trades_instances[symbols[-1]].days_end()
246
- and today == 'friday'
247
- and num_days/len(symbols) >= 20
248
- ):
249
- break
315
+ elif period.lower() == 'month':
316
+ for symbol in symbols:
317
+ trade = trades_instances[symbol]
318
+ if trade.days_end() and today != FRIDAY:
319
+ logmsg("Day")
320
+ elif trade.days_end() and today == FRIDAY:
321
+ logmsg("Week")
322
+ elif (
323
+ trade.days_end()
324
+ and today == FRIDAY
325
+ and num_days/len(symbols) >= 20
326
+ ) and closing:
327
+ trade.close_positions(position_type='all', comment=comment)
328
+ logmsg("Month")
329
+ trade.statistics(save=True)
330
+ if day_end and today != FRIDAY:
331
+ sleep_time = trades_instances[symbols[-1]].sleep_time()
332
+ sleepmsg(sleep_time)
333
+ time.sleep(60 * sleep_time)
334
+ logger.info(sessionmsg)
335
+ num_days += 1
336
+ elif day_end and today == FRIDAY:
337
+ sleep_time = trades_instances[symbols[-1]
338
+ ].sleep_time(weekend=True)
339
+ sleepmsg(sleep_time)
340
+ time.sleep(60 * sleep_time)
341
+ logger.info(sessionmsg)
342
+ num_days += 1
343
+ elif (day_end
344
+ and today == FRIDAY
345
+ and num_days/len(symbols) >= 20
346
+ ):
347
+ break
348
+ except Exception as e:
349
+ logger.error(f"Handling period end actions, {e}, STRATEGY={STRATEGY}")
350
+ continue
250
351
 
251
352
 
252
353
  def _tws_execution(*args, **kwargs):
253
354
  raise NotImplementedError("TWS Execution is not yet implemented !!!")
254
355
 
255
- _TERMINALS = {
256
- 'MT5': _mt5_execution,
257
- 'TWS': _tws_execution
258
- }
259
- class ExecutionEngine():
356
+
357
+ class MT5ExecutionEngine():
260
358
  """
261
- 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.
262
360
  It orchestrates the entire trading process, ensuring seamless interaction between your strategies, market data, and your chosen
263
- trading platform (currently MetaTrader 5 (MT5) and Interactive Brokers TWS).
361
+ trading platform.
264
362
 
265
363
  Key Features
266
364
  ------------
267
365
 
268
- - **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.
269
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.
270
368
  - **Trade Period Control:** Define whether your strategy runs for a day, a week, or a month, allowing for flexible trading durations.
271
369
  - **Money Management:** The engine supports optional money management features, allowing you to control risk and optimize your trading performance.
272
370
  - **Trading Day Configuration:** You can customize the days of the week your strategy will execute, providing granular control over your trading schedule.
273
- - **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.
274
372
 
275
373
  Examples
276
374
  --------
277
375
 
278
376
  >>> from bbstrader.metatrader import create_trade_instance
279
- >>> from bbstrader.trading.execution import ExecutionEngine
377
+ >>> from bbstrader.trading.execution import MT5ExecutionEngine
280
378
  >>> from bbstrader.trading.strategies import StockIndexCFDTrading
281
- >>> from bbstrader.metatrader.utils import config_logger
379
+ >>> from bbstrader.config import config_logger
282
380
  >>>
283
381
  >>> if __name__ == '__main__':
284
382
  >>> logger = config_logger(index_trade.log, console_log=True)
@@ -316,7 +414,7 @@ class ExecutionEngine():
316
414
  ... logger=logger,
317
415
  ... )
318
416
  >>>
319
- >>> engine = ExecutionEngine(
417
+ >>> engine = MT5ExecutionEngine(
320
418
  ... symbol_list,
321
419
  ... trades_instances,
322
420
  ... StockIndexCFDTrading,
@@ -327,7 +425,7 @@ class ExecutionEngine():
327
425
  ... comment='bbs_SISTBO_@2.0',
328
426
  ... **strategy_kwargs
329
427
  ... )
330
- >>> engine.run(terminal='MT5')
428
+ >>> engine.run()
331
429
  """
332
430
 
333
431
  def __init__(self,
@@ -335,15 +433,18 @@ class ExecutionEngine():
335
433
  trades_instances: Dict[str, Trade],
336
434
  strategy_cls: Strategy,
337
435
  /,
338
- mm: Optional[bool] = True,
339
- trail: Optional[bool] = True,
436
+ mm: bool = True,
437
+ trail: bool = True,
340
438
  stop_trail: Optional[int] = None,
341
439
  trail_after_points: Optional[int] = None,
342
440
  be_plus_points: Optional[int] = None,
343
- time_frame: Optional[str] = '15m',
344
- 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,
345
445
  period: Literal['day', 'week', 'month'] = 'week',
346
446
  period_end_action: Literal['break', 'sleep'] = 'break',
447
+ closing_pnl: Optional[float] = None,
347
448
  trading_days: Optional[List[str]] = TradingDays,
348
449
  comment: Optional[str] = None,
349
450
  **kwargs
@@ -354,21 +455,27 @@ class ExecutionEngine():
354
455
  trades_instances : Dictionary of Trade instances
355
456
  strategy_cls : Strategy class to use for trading
356
457
  mm : Enable Money Management. Defaults to False.
458
+ show_positions_orders : Print open positions and orders. Defaults to False.
357
459
  time_frame : Time frame to trade. Defaults to '15m'.
358
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.
359
462
  period : Period to trade. Defaults to 'week'.
360
463
  period_end_action : Action to take at the end of the period. Defaults to 'break',
361
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.
362
466
  trading_days : Trading days in a week. Defaults to monday to friday.
363
467
  comment: Comment for trades. Defaults to None.
364
468
  **kwargs: Additional keyword arguments
365
469
  - strategy_name (Optional[str]): Strategy name. Defaults to None.
366
470
  - max_trades (Dict[str, int]): Maximum trades per symbol. Defaults to None.
367
- - logger (Optional[logging.Logger]): Logger instance. 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.
368
475
 
369
476
  Note:
370
477
  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
478
+ 2. All Strategies must inherit from `bbstrader.btengine.strategy.Strategy` or `bbstrader.btengine.strategy.MT5Strategy` class
372
479
  and have a `calculate_signals` method that returns a dictionary of signals for each symbol in symbol_list.
373
480
 
374
481
  3. All strategies must have the following arguments in their `__init__` method:
@@ -377,7 +484,7 @@ class ExecutionEngine():
377
484
  - symbol_list (List[str]): List of symbols to trade can be none for backtesting
378
485
  - mode (str): Mode of the strategy. Must be either 'live' or 'backtest'
379
486
  - **kwargs: Additional keyword arguments
380
- 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,
381
488
  the `Strategy` class, the `DataHandler` class, the `Portfolio` class and the `ExecutionHandler` class.
382
489
  - The `bars` and `events` arguments are used for backtesting only.
383
490
 
@@ -392,32 +499,39 @@ class ExecutionEngine():
392
499
  self.stop_trail = stop_trail
393
500
  self.trail_after_points = trail_after_points
394
501
  self.be_plus_points = be_plus_points
502
+ self.show_positions_orders = show_positions_orders
395
503
  self.time_frame = time_frame
396
504
  self.iter_time = iter_time
505
+ self.use_trade_time = use_trade_time
397
506
  self.period = period
398
507
  self.period_end_action = period_end_action
508
+ self.closing_pnl = closing_pnl
399
509
  self.trading_days = trading_days
400
510
  self.comment = comment
401
511
  self.kwargs = kwargs
402
512
 
403
- def run(self, terminal: Literal['MT5', 'TWS']):
404
- if terminal not in _TERMINALS:
405
- raise ValueError(
406
- f"Invalid terminal: {terminal}. Must be either 'MT5' or 'TWS'")
407
- _TERMINALS[terminal](
408
- self.symbol_list,
409
- self.trades_instances,
410
- self.strategy_cls,
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,
416
- time_frame=self.time_frame,
417
- iter_time=self.iter_time,
418
- period=self.period,
419
- period_end_action=self.period_end_action,
420
- trading_days=self.trading_days,
421
- comment=self.comment,
422
- **self.kwargs
423
- )
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():...