bbstrader 0.3.0__py3-none-any.whl → 0.3.1__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.
- bbstrader/__init__.py +1 -1
- bbstrader/__main__.py +17 -13
- bbstrader/core/data.py +92 -29
- bbstrader/metatrader/account.py +4 -10
- bbstrader/metatrader/copier.py +112 -36
- bbstrader/metatrader/scripts.py +26 -12
- bbstrader/metatrader/trade.py +27 -32
- bbstrader/metatrader/utils.py +1 -0
- bbstrader/models/nlp.py +117 -74
- bbstrader/trading/execution.py +58 -37
- {bbstrader-0.3.0.dist-info → bbstrader-0.3.1.dist-info}/METADATA +2 -5
- {bbstrader-0.3.0.dist-info → bbstrader-0.3.1.dist-info}/RECORD +16 -16
- {bbstrader-0.3.0.dist-info → bbstrader-0.3.1.dist-info}/WHEEL +0 -0
- {bbstrader-0.3.0.dist-info → bbstrader-0.3.1.dist-info}/entry_points.txt +0 -0
- {bbstrader-0.3.0.dist-info → bbstrader-0.3.1.dist-info}/licenses/LICENSE +0 -0
- {bbstrader-0.3.0.dist-info → bbstrader-0.3.1.dist-info}/top_level.txt +0 -0
bbstrader/trading/execution.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import multiprocessing as mp
|
|
2
|
+
import sys
|
|
2
3
|
import time
|
|
3
4
|
from datetime import date, datetime
|
|
4
|
-
from typing import Dict, List, Literal, Optional
|
|
5
|
+
from typing import Callable, Dict, List, Literal, Optional
|
|
5
6
|
|
|
6
7
|
import pandas as pd
|
|
7
8
|
from loguru import logger as log
|
|
@@ -88,7 +89,7 @@ NON_EXEC_RETCODES = {
|
|
|
88
89
|
log.add(
|
|
89
90
|
f"{BBSTRADER_DIR}/logs/execution.log",
|
|
90
91
|
enqueue=True,
|
|
91
|
-
level="
|
|
92
|
+
level="DEBUG",
|
|
92
93
|
format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {name} | {message}",
|
|
93
94
|
)
|
|
94
95
|
|
|
@@ -171,10 +172,11 @@ class Mt5ExecutionEngine:
|
|
|
171
172
|
self,
|
|
172
173
|
symbol_list: List[str],
|
|
173
174
|
trades_instances: Dict[str, Trade],
|
|
174
|
-
strategy_cls: Strategy,
|
|
175
|
+
strategy_cls: Strategy | MT5Strategy,
|
|
175
176
|
/,
|
|
176
177
|
mm: bool = True,
|
|
177
178
|
auto_trade: bool = True,
|
|
179
|
+
prompt_callback: Callable = None,
|
|
178
180
|
optimizer: str = "equal",
|
|
179
181
|
trail: bool = True,
|
|
180
182
|
stop_trail: Optional[int] = None,
|
|
@@ -201,6 +203,8 @@ class Mt5ExecutionEngine:
|
|
|
201
203
|
auto_trade : If set to true, when signal are generated by the strategy class,
|
|
202
204
|
the Execution engine will automaticaly open position in other whise it will prompt
|
|
203
205
|
the user for confimation.
|
|
206
|
+
prompt_callback : Callback function to prompt the user for confirmation.
|
|
207
|
+
This is useful when integrating with GUI applications.
|
|
204
208
|
show_positions_orders : Print open positions and orders. Defaults to False.
|
|
205
209
|
iter_time : Interval to check for signals and `mm`. Defaults to 5.
|
|
206
210
|
use_trade_time : Open trades after the time is completed. Defaults to True.
|
|
@@ -244,6 +248,7 @@ class Mt5ExecutionEngine:
|
|
|
244
248
|
self.strategy_cls = strategy_cls
|
|
245
249
|
self.mm = mm
|
|
246
250
|
self.auto_trade = auto_trade
|
|
251
|
+
self.prompt_callback = prompt_callback
|
|
247
252
|
self.optimizer = optimizer
|
|
248
253
|
self.trail = trail
|
|
249
254
|
self.stop_trail = stop_trail
|
|
@@ -267,6 +272,7 @@ class Mt5ExecutionEngine:
|
|
|
267
272
|
|
|
268
273
|
self._initialize_engine(**kwargs)
|
|
269
274
|
self.strategy = self._init_strategy(**kwargs)
|
|
275
|
+
self._running = True
|
|
270
276
|
|
|
271
277
|
def __repr__(self):
|
|
272
278
|
trades = self.trades_instances.keys()
|
|
@@ -308,7 +314,7 @@ class Mt5ExecutionEngine:
|
|
|
308
314
|
def _print_exc(self, msg: str, e: Exception):
|
|
309
315
|
if isinstance(e, KeyboardInterrupt):
|
|
310
316
|
logger.info("Stopping the Execution Engine ...")
|
|
311
|
-
|
|
317
|
+
sys.exit(0)
|
|
312
318
|
if self.debug_mode:
|
|
313
319
|
raise ValueError(msg).with_traceback(e.__traceback__)
|
|
314
320
|
else:
|
|
@@ -332,10 +338,10 @@ class Mt5ExecutionEngine:
|
|
|
332
338
|
expert_ids = [expert_ids]
|
|
333
339
|
return expert_ids
|
|
334
340
|
|
|
335
|
-
def _init_strategy(self, **kwargs):
|
|
341
|
+
def _init_strategy(self, **kwargs) -> Strategy | MT5Strategy:
|
|
336
342
|
try:
|
|
337
343
|
check_mt5_connection(**kwargs)
|
|
338
|
-
strategy
|
|
344
|
+
strategy = self.strategy_cls(
|
|
339
345
|
symbol_list=self.symbols, mode=TradingMode.LIVE, **kwargs
|
|
340
346
|
)
|
|
341
347
|
except Exception as e:
|
|
@@ -367,21 +373,21 @@ class Mt5ExecutionEngine:
|
|
|
367
373
|
).format(**common_data)
|
|
368
374
|
|
|
369
375
|
sigmsg = (
|
|
370
|
-
"SIGNAL={signal}
|
|
371
|
-
"SYMBOL={symbol}
|
|
372
|
-
"TYPE={symbol_type}
|
|
373
|
-
"DESCRIPTION={description}
|
|
374
|
-
"PRICE={price}
|
|
375
|
-
"STOPLIMIT={stoplimit}
|
|
376
|
-
"STRATEGY={strategy}
|
|
377
|
-
"TIMEFRAME={timeframe}
|
|
378
|
-
"BROKER={broker}
|
|
376
|
+
"SIGNAL={signal}\n"
|
|
377
|
+
"SYMBOL={symbol}\n"
|
|
378
|
+
"TYPE={symbol_type}\n"
|
|
379
|
+
"DESCRIPTION={description}\n"
|
|
380
|
+
"PRICE={price}\n"
|
|
381
|
+
"STOPLIMIT={stoplimit}\n"
|
|
382
|
+
"STRATEGY={strategy}\n"
|
|
383
|
+
"TIMEFRAME={timeframe}\n"
|
|
384
|
+
"BROKER={broker}\n"
|
|
379
385
|
"TIMESTAMP={timestamp}"
|
|
380
386
|
).format(
|
|
381
387
|
**common_data,
|
|
382
388
|
symbol_type=account.get_symbol_type(symbol).value,
|
|
383
|
-
description=symbol_info.description,
|
|
384
|
-
price=price,
|
|
389
|
+
description=symbol_info.description if symbol_info else "N/A",
|
|
390
|
+
price=price if price else "MARKET",
|
|
385
391
|
stoplimit=stoplimit,
|
|
386
392
|
broker=account.broker.name,
|
|
387
393
|
timestamp=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
|
@@ -520,7 +526,7 @@ class Mt5ExecutionEngine:
|
|
|
520
526
|
def _daily_end_checks(self, today, closing, sessionmsg):
|
|
521
527
|
self.strategy.perform_period_end_checks()
|
|
522
528
|
if self.period_end_action == "break" and closing:
|
|
523
|
-
exit(0)
|
|
529
|
+
sys.exit(0)
|
|
524
530
|
elif self.period_end_action == "sleep" and today != FRIDAY or not closing:
|
|
525
531
|
self._sleep_over_night(sessionmsg)
|
|
526
532
|
elif self.period_end_action == "sleep" and today == FRIDAY:
|
|
@@ -532,7 +538,7 @@ class Mt5ExecutionEngine:
|
|
|
532
538
|
elif today == FRIDAY:
|
|
533
539
|
self.strategy.perform_period_end_checks()
|
|
534
540
|
if self.period_end_action == "break" and closing:
|
|
535
|
-
exit(0)
|
|
541
|
+
sys.exit(0)
|
|
536
542
|
elif self.period_end_action == "sleep" or not closing:
|
|
537
543
|
self._sleep_over_weekend(sessionmsg)
|
|
538
544
|
|
|
@@ -541,7 +547,7 @@ class Mt5ExecutionEngine:
|
|
|
541
547
|
self._sleep_over_night(sessionmsg)
|
|
542
548
|
elif today == FRIDAY and self._is_month_ends() and closing:
|
|
543
549
|
self.strategy.perform_period_end_checks()
|
|
544
|
-
exit(0)
|
|
550
|
+
sys.exit(0)
|
|
545
551
|
else:
|
|
546
552
|
self._sleep_over_weekend(sessionmsg)
|
|
547
553
|
|
|
@@ -621,17 +627,30 @@ class Mt5ExecutionEngine:
|
|
|
621
627
|
)
|
|
622
628
|
pass
|
|
623
629
|
|
|
624
|
-
def
|
|
625
|
-
self, signal, symbol, id, trade: Trade, price, stoplimit, sigmsg, msg, comment
|
|
626
|
-
):
|
|
630
|
+
def _handle_auto_trade(self, sigmsg, symbol) -> bool:
|
|
627
631
|
if self.notify:
|
|
628
632
|
self._send_notification(sigmsg, symbol)
|
|
633
|
+
if self.auto_trade:
|
|
634
|
+
return True
|
|
629
635
|
if not self.auto_trade:
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
636
|
+
prompt = f"{sigmsg} \n Enter Y/Yes to accept or N/No to reject this order : "
|
|
637
|
+
if self.prompt_callback is not None:
|
|
638
|
+
auto_trade = self.prompt_callback(prompt)
|
|
639
|
+
else:
|
|
640
|
+
auto_trade = input(prompt)
|
|
633
641
|
if not auto_trade.upper().startswith("Y"):
|
|
634
|
-
|
|
642
|
+
info = f"Order Rejected !!! SYMBOL={symbol}, STRATEGY={self.STRATEGY} , ACCOUNT={self.ACCOUNT}"
|
|
643
|
+
logger.info(info)
|
|
644
|
+
if self.notify:
|
|
645
|
+
self._send_notification(info, symbol)
|
|
646
|
+
return False
|
|
647
|
+
return True
|
|
648
|
+
|
|
649
|
+
def _open_buy(
|
|
650
|
+
self, signal, symbol, id, trade: Trade, price, stoplimit, sigmsg, msg, comment
|
|
651
|
+
):
|
|
652
|
+
if not self._handle_auto_trade(sigmsg, symbol):
|
|
653
|
+
return
|
|
635
654
|
if not self._check_retcode(trade, "BMKT"):
|
|
636
655
|
logger.info(msg)
|
|
637
656
|
trade.open_buy_position(
|
|
@@ -647,14 +666,8 @@ class Mt5ExecutionEngine:
|
|
|
647
666
|
def _open_sell(
|
|
648
667
|
self, signal, symbol, id, trade: Trade, price, stoplimit, sigmsg, msg, comment
|
|
649
668
|
):
|
|
650
|
-
if self.
|
|
651
|
-
|
|
652
|
-
if not self.auto_trade:
|
|
653
|
-
auto_trade = input(
|
|
654
|
-
f"{sigmsg} \n\n Please enter Y/Yes to accept this order or N/No to reject it :"
|
|
655
|
-
)
|
|
656
|
-
if not auto_trade.upper().startswith("Y"):
|
|
657
|
-
return
|
|
669
|
+
if not self._handle_auto_trade(sigmsg, symbol):
|
|
670
|
+
return
|
|
658
671
|
if not self._check_retcode(trade, "SMKT"):
|
|
659
672
|
logger.info(msg)
|
|
660
673
|
trade.open_sell_position(
|
|
@@ -905,7 +918,7 @@ class Mt5ExecutionEngine:
|
|
|
905
918
|
pass
|
|
906
919
|
|
|
907
920
|
def run(self):
|
|
908
|
-
while
|
|
921
|
+
while self._running:
|
|
909
922
|
try:
|
|
910
923
|
check_mt5_connection(**self.kwargs)
|
|
911
924
|
positions_orders = self._check_positions_orders()
|
|
@@ -927,13 +940,21 @@ class Mt5ExecutionEngine:
|
|
|
927
940
|
self._sleep()
|
|
928
941
|
self._handle_period_end_actions(today)
|
|
929
942
|
except KeyboardInterrupt:
|
|
930
|
-
logger.info(
|
|
943
|
+
logger.info(
|
|
944
|
+
f"Stopping Execution Engine for {self.STRATEGY} on {self.ACCOUNT}"
|
|
945
|
+
)
|
|
931
946
|
break
|
|
932
947
|
except Exception as e:
|
|
933
948
|
msg = f"Running Execution Engine, STRATEGY={self.STRATEGY} , ACCOUNT={self.ACCOUNT}"
|
|
934
949
|
self._print_exc(msg, e)
|
|
935
950
|
continue
|
|
936
951
|
|
|
952
|
+
def stop(self):
|
|
953
|
+
"""Stops the execution engine."""
|
|
954
|
+
self._running = False
|
|
955
|
+
logger.info(f"Stopping Execution Engine for {self.STRATEGY} on {self.ACCOUNT}")
|
|
956
|
+
logger.info("Execution Engine stopped successfully.")
|
|
957
|
+
|
|
937
958
|
|
|
938
959
|
def RunMt5Engine(account_id: str, **kwargs):
|
|
939
960
|
"""Starts an MT5 execution engine for a given account.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bbstrader
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Simplified Investment & Trading Toolkit
|
|
5
5
|
Home-page: https://github.com/bbalouki/bbstrader
|
|
6
6
|
Download-URL: https://pypi.org/project/bbstrader/
|
|
@@ -172,10 +172,7 @@ To begin using `bbstrader`, please ensure your system meets the following prereq
|
|
|
172
172
|
* [Admirals Group AS](https://cabinet.a-partnership.com/visit/?bta=35537&brand=admiralmarkets) (for Stocks, ETFs, Indices, Commodities, Futures, Forex)
|
|
173
173
|
* [Just Global Markets Ltd.](https://one.justmarkets.link/a/tufvj0xugm/registration/trader) (for Stocks, Crypto, Indices, Commodities, Forex)
|
|
174
174
|
* [FTMO](https://trader.ftmo.com/?affiliates=JGmeuQqepAZLMcdOEQRp) (Proprietary Firm)
|
|
175
|
-
|
|
176
|
-
* Interactive Brokers Trader Workstation (TWS) or IB Gateway must be installed and running.
|
|
177
|
-
* An active account with Interactive Brokers.
|
|
178
|
-
* The Python client library for the IBKR API: `ibapi`.
|
|
175
|
+
|
|
179
176
|
|
|
180
177
|
### Installation
|
|
181
178
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
bbstrader/__init__.py,sha256=
|
|
2
|
-
bbstrader/__main__.py,sha256=
|
|
1
|
+
bbstrader/__init__.py,sha256=Uh0J99UbUAi55zL3HVBiYCmP1QP3F2Jw_8XQQJ5NNF0,581
|
|
2
|
+
bbstrader/__main__.py,sha256=zG8MuvuCSsFsnwqJDG9PU4t7EpYTqcx3cGNslLyRk5E,2168
|
|
3
3
|
bbstrader/compat.py,sha256=djbHMvTvy0HYm1zyZ6Ttp_LMwP2PqTSVw1r7pqbz7So,487
|
|
4
4
|
bbstrader/config.py,sha256=c2nCUw-bYWf5kkyFls5Nqld8HdMczexSilTni7rYUBw,3973
|
|
5
5
|
bbstrader/tseries.py,sha256=IMoo_8Ov5m1Q2NJ3_RW8lzok8I8vUP5Ks7YGXwEZYsw,68385
|
|
@@ -13,35 +13,35 @@ bbstrader/btengine/portfolio.py,sha256=z98M65HQeCyma8gMZkAxspxBA9jtIhzxMyJUHPPj3
|
|
|
13
13
|
bbstrader/btengine/scripts.py,sha256=8o66dq4Ex4DsH4s8xvJqUOFjLzZJSnbBvvNBzohtzoE,4837
|
|
14
14
|
bbstrader/btengine/strategy.py,sha256=kLw4ipCTE06y2IdEysqI5VGlJDPEIR_OrJ3Qitm_yPw,32398
|
|
15
15
|
bbstrader/core/__init__.py,sha256=GIFzFSStPfE0XM2j7mDeZZQeMTh_AwPsDOQXwMVJLgw,97
|
|
16
|
-
bbstrader/core/data.py,sha256=
|
|
16
|
+
bbstrader/core/data.py,sha256=u8hhAF1Gf9fr_2Ci4mv7Smr1dt3E_KDAHXC3BFbis0Q,25055
|
|
17
17
|
bbstrader/core/scripts.py,sha256=orPtnQlNNNFV-yfqFGRAIGFtWgilykZ3xyTx6d4DLsk,3963
|
|
18
18
|
bbstrader/core/utils.py,sha256=WjuabzBjhY65ku2KL-f7CMalE2x-wrX-6mCA_qhhFPE,2728
|
|
19
19
|
bbstrader/ibkr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
20
|
bbstrader/ibkr/utils.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
21
|
bbstrader/metatrader/__init__.py,sha256=A5Ye9tpc2sp9Xk5qjKw-EfYsoRcZtAt8nqvC3tCtZs8,333
|
|
22
|
-
bbstrader/metatrader/account.py,sha256=
|
|
22
|
+
bbstrader/metatrader/account.py,sha256=5vi-InhcAHEDeuVz9WIY6Y-7LnWfKrhGMtbvRPsm1j8,56891
|
|
23
23
|
bbstrader/metatrader/analysis.py,sha256=pnZWdvEnf9wkPT0vhEIbZipnN5EUf6TaQftplYP9jRs,3564
|
|
24
|
-
bbstrader/metatrader/copier.py,sha256=
|
|
24
|
+
bbstrader/metatrader/copier.py,sha256=Vp8NbjUwUoQ_mOl9gGG1w43UsR_c8GiWtZA6V3Ph8xA,34948
|
|
25
25
|
bbstrader/metatrader/rates.py,sha256=6B-5AHguyDhicw3GqJ33FfFpdCKzxgUdlrHyPFuOLZg,20784
|
|
26
26
|
bbstrader/metatrader/risk.py,sha256=jOuMcK3Y7AeglHegPC-igB4ucMILB3BVm7o5sdits5I,27248
|
|
27
|
-
bbstrader/metatrader/scripts.py,sha256=
|
|
28
|
-
bbstrader/metatrader/trade.py,sha256=
|
|
29
|
-
bbstrader/metatrader/utils.py,sha256=
|
|
27
|
+
bbstrader/metatrader/scripts.py,sha256=z0dBNo3PTtDobk3U7iFYyALiwAictdNQ9eNSXOA0QQ0,2574
|
|
28
|
+
bbstrader/metatrader/trade.py,sha256=Ta1yc9g-GNKu1PI-qbFckbWWWm6NOYGnj6pyqyATktc,80419
|
|
29
|
+
bbstrader/metatrader/utils.py,sha256=aFP1XHxLkRfJFp7jKgY2BUlKKwZvCKnvC5qtY_vjgHg,19645
|
|
30
30
|
bbstrader/models/__init__.py,sha256=s2mJrtKePXQaw_PvcrtPCD2mPCdVXP4Myzg0MlLVipo,547
|
|
31
31
|
bbstrader/models/factors.py,sha256=Y1rjwhWU4aiSRd-jFOLnLZczFCY0bJUxauCo17HvOFY,12791
|
|
32
32
|
bbstrader/models/ml.py,sha256=t8SOm5Wavv5CKXLJ7YtE99OwwQuJAlarlngdXQEjFjs,47602
|
|
33
|
-
bbstrader/models/nlp.py,sha256=
|
|
33
|
+
bbstrader/models/nlp.py,sha256=0WuUREjk1N3IXwdzUKBK-aTvfD_TxmZ8gCNPrPYl4a4,31446
|
|
34
34
|
bbstrader/models/optimization.py,sha256=vnks6uxFZdqXgxaZJNxq8z0IH45KZ8yaXo38JhIVKGc,6399
|
|
35
35
|
bbstrader/models/portfolio.py,sha256=r-47Zrn2r7iKCHm5YVtwkbBJXAZGM3QYy-rXCWY9-Bg,8079
|
|
36
36
|
bbstrader/models/risk.py,sha256=JQOXPshMOru6Eb0AMU6oKCNzg6mlGfL6_tN90lWcVBE,14878
|
|
37
37
|
bbstrader/trading/__init__.py,sha256=ycLyuuxN5SujqtzR9X0Q74UQfK93q2va-GGAXdr-KS8,457
|
|
38
|
-
bbstrader/trading/execution.py,sha256=
|
|
38
|
+
bbstrader/trading/execution.py,sha256=h8XoO1Ihi7sioy25L5545JFtdht3hqBQ-86StGsyYMs,38963
|
|
39
39
|
bbstrader/trading/scripts.py,sha256=Tf5q33WqqygjpIv43_8nA82VZ3GM0qgb4Ggo3fHJ_wg,5744
|
|
40
40
|
bbstrader/trading/strategies.py,sha256=BZEN8NwO9VxXDqSGc3o2OYaFyD9g5JKtEz_CyvxOOQM,37110
|
|
41
41
|
bbstrader/trading/utils.py,sha256=57dKF9dcRu04oU2VRqydRrzW39dCW2wlDWhVt-sZdRw,1857
|
|
42
|
-
bbstrader-0.3.
|
|
43
|
-
bbstrader-0.3.
|
|
44
|
-
bbstrader-0.3.
|
|
45
|
-
bbstrader-0.3.
|
|
46
|
-
bbstrader-0.3.
|
|
47
|
-
bbstrader-0.3.
|
|
42
|
+
bbstrader-0.3.1.dist-info/licenses/LICENSE,sha256=ZwC_RqqGmOPBUiMDKqLyJZ5HBeHq53LpL7TMRzrJY8c,1094
|
|
43
|
+
bbstrader-0.3.1.dist-info/METADATA,sha256=Xi3741OMjW9hZmeep3Kvv1ucWWHC1wHr0IdCHczEv-4,26984
|
|
44
|
+
bbstrader-0.3.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
45
|
+
bbstrader-0.3.1.dist-info/entry_points.txt,sha256=0yDCbhbgHswOzJnY5wRSM_FjjyMHGvY7lJpSSVh0xtI,54
|
|
46
|
+
bbstrader-0.3.1.dist-info/top_level.txt,sha256=Wwj322jZmxGZ6gD_TdaPiPLjED5ReObm5omerwlmZIg,10
|
|
47
|
+
bbstrader-0.3.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|