7
7
import time
8
8
import webbrowser
9
9
from datetime import datetime
10
- from typing import Dict , List , Union
10
+ from typing import Dict , List , Optional , Union
11
11
12
12
from PyQt5 import QtCore , uic
13
13
from PyQt5 .QtCore import QRunnable , QThreadPool
36
36
show_and_bring_window_to_front )
37
37
from algobot .news_scraper import scrape_news
38
38
from algobot .slots import initiate_slots
39
- from algobot .telegram_bot import TelegramBot
39
+ from algobot .telegram_bot . bot import TelegramBot
40
40
from algobot .threads import backtest_thread , bot_thread , optimizer_thread , worker_thread
41
41
from algobot .traders .backtester import Backtester
42
42
from algobot .traders .real_trader import RealTrader
@@ -68,7 +68,7 @@ def __init__(self, parent=None):
68
68
self .strategy_manager = StrategyManager (self )
69
69
self .statistics = Statistics (self ) # Loading statistics
70
70
self .thread_pool = QThreadPool (self ) # Initiating threading pool
71
- self .threads : Dict [int , QRunnable or None ] = {BACKTEST : None , SIMULATION : None , LIVE : None , OPTIMIZER : None }
71
+ self .threads : Dict [str , QRunnable or None ] = {BACKTEST : None , SIMULATION : None , LIVE : None , OPTIMIZER : None }
72
72
self .graphs = (
73
73
{'graph' : self .simulationGraph , 'plots' : [], 'label' : self .simulationCoordinates , 'enable' : True },
74
74
{'graph' : self .backtestGraph , 'plots' : [], 'label' : self .backtestCoordinates , 'enable' : True },
@@ -81,15 +81,20 @@ def __init__(self, parent=None):
81
81
82
82
self .interface_dictionary = get_interface_dictionary (self )
83
83
self .advanced_logging = False
84
+
85
+ # TODO: Why do we need these? Deduce from trader and threads?
84
86
self .running_live = False
85
87
self .simulation_running_live = False
88
+
86
89
self .optimizer : Union [Backtester , None ] = None
87
90
self .backtester : Union [Backtester , None ] = None
88
91
self .trader : Union [RealTrader , None ] = None
89
92
self .simulation_trader : Union [SimulationTrader , None ] = None
90
- self . simulation_lower_interval_data : Union [ Data , None ] = None
93
+
91
94
self .lower_interval_data : Union [Data , None ] = None
92
- self .telegram_bot = None
95
+ self .simulation_lower_interval_data : Union [Data , None ] = None
96
+
97
+ self .telegram_bot : Optional [TelegramBot ] = None
93
98
self .tickers = [] # All available tickers.
94
99
95
100
if algobot .CURRENT_VERSION == UNKNOWN :
@@ -128,7 +133,7 @@ def inform_telegram(self, message: str, stop_bot: bool = False):
128
133
try :
129
134
if self .telegram_bot is None :
130
135
api_key = self .configuration .telegramApiKey .text ()
131
- self .telegram_bot = TelegramBot (gui = self , token = api_key , bot_thread = None )
136
+ self .telegram_bot = TelegramBot (gui = self , token = api_key )
132
137
133
138
chat_id = self .configuration .telegramChatID .text ()
134
139
if self .configuration .chat_pass :
@@ -350,8 +355,7 @@ def initiate_optimizer(self):
350
355
worker .signals .restore .connect (lambda : self .set_optimizer_buttons (running = False , clear = False ))
351
356
worker .signals .error .connect (lambda x : create_popup (self , x ))
352
357
if self .configuration .enabledOptimizerNotification .isChecked ():
353
- worker .signals .finished .connect (lambda : self .inform_telegram ('Optimizer has finished running.' ,
354
- stop_bot = True ))
358
+ worker .signals .finished .connect (lambda : self .inform_telegram ('Optimizer has finished running.' ))
355
359
worker .signals .activity .connect (lambda data : add_to_table (self .optimizerTableWidget , data = data ,
356
360
insert_date = False ))
357
361
self .thread_pool .start (worker )
@@ -532,7 +536,7 @@ def setup_backtester(self, configuration_dictionary: dict):
532
536
self .update_backtest_configuration_gui (configuration_dictionary )
533
537
self .add_to_backtest_monitor (f"Started backtest with { symbol } data and { interval .lower ()} interval periods." )
534
538
535
- def check_strategies (self , caller : int ) -> bool :
539
+ def check_strategies (self , caller : str ) -> bool :
536
540
"""
537
541
Checks if strategies exist based on the caller provided and prompts an appropriate message.
538
542
"""
@@ -549,7 +553,7 @@ def check_strategies(self, caller: int) -> bool:
549
553
return confirm_message_box (message , self )
550
554
return True
551
555
552
- def validate_ticker (self , caller : int ):
556
+ def validate_ticker (self , caller : str ):
553
557
"""
554
558
Validate ticker provided before running a bot.
555
559
"""
@@ -563,7 +567,7 @@ def validate_ticker(self, caller: int):
563
567
return False
564
568
return True
565
569
566
- def initiate_bot_thread (self , caller : int ):
570
+ def initiate_bot_thread (self , caller : str ):
567
571
"""
568
572
Main function that initiates bot thread and handles all data-view logic.
569
573
:param caller: Caller that decides whether a live bot or simulation bot is run.
@@ -574,7 +578,7 @@ def initiate_bot_thread(self, caller: int):
574
578
return
575
579
576
580
self .disable_interface (True , caller )
577
- worker = bot_thread .BotThread (gui = self , caller = caller , logger = self .logger )
581
+ worker = self . threads [ caller ] = bot_thread .BotThread (gui = self , caller = caller , logger = self .logger )
578
582
worker .signals .small_error .connect (lambda x : create_popup (self , x ))
579
583
worker .signals .error .connect (self .end_crash_bot_and_create_popup )
580
584
worker .signals .activity .connect (self .add_to_monitor )
@@ -585,14 +589,14 @@ def initiate_bot_thread(self, caller: int):
585
589
worker .signals .restore .connect (lambda : self .disable_interface (disable = False , caller = caller ))
586
590
587
591
# All these below are for Telegram.
588
- worker .signals .force_long .connect (lambda : self .force_long ( LIVE ) )
589
- worker .signals .force_short .connect (lambda : self .force_short ( LIVE ) )
590
- worker .signals .exit_position .connect (lambda : self .exit_position ( LIVE ) )
591
- worker .signals .wait_override .connect (lambda : self .exit_position (LIVE , False ))
592
- worker .signals .pause .connect (lambda : self .pause_or_resume_bot ( LIVE ) )
593
- worker .signals .resume .connect (lambda : self .pause_or_resume_bot ( LIVE ) )
592
+ worker .signals .force_long .connect (self .force_long )
593
+ worker .signals .force_short .connect (self .force_short )
594
+ worker .signals .exit_position .connect (self .exit_position )
595
+ worker .signals .wait_override .connect (lambda * _args : self .exit_position (caller , False ))
596
+ worker .signals .resume .connect (self .pause_or_resume_bot )
597
+ worker .signals .pause .connect (self .pause_or_resume_bot )
594
598
worker .signals .set_custom_stop_loss .connect (self .set_custom_stop_loss )
595
- worker .signals .remove_custom_stop_loss .connect (lambda : self .set_custom_stop_loss (LIVE , False ))
599
+ worker .signals .remove_custom_stop_loss .connect (lambda * _args : self .set_custom_stop_loss (caller , False ))
596
600
self .thread_pool .start (worker )
597
601
598
602
def download_progress_update (self , value : int , message : str , caller ):
@@ -627,10 +631,15 @@ def add_end_bot_status(self, caller):
627
631
Adds a status update to let user know that bot has been ended.
628
632
:param caller: Caller that'll determine which monitor gets updated.
629
633
"""
634
+ self .threads [caller ] = None
630
635
if caller == SIMULATION :
631
- self . add_to_monitor ( caller , "Killed simulation bot." )
636
+ msg = "Killed simulation bot."
632
637
else :
633
- self .add_to_monitor (caller , "Killed bot." )
638
+ msg = "Killed bot."
639
+
640
+ self .add_to_monitor (caller , msg )
641
+ if self .telegram_bot is not None :
642
+ self .inform_telegram (msg )
634
643
635
644
def reset_bot_interface (self , caller ):
636
645
"""
@@ -677,9 +686,6 @@ def end_bot_gracefully(self, caller, callback=None):
677
686
678
687
if self .configuration .chat_pass :
679
688
self .telegram_bot .send_message (self .configuration .telegramChatID .text (), "Bot has been ended." )
680
- if self .telegram_bot :
681
- self .telegram_bot .stop ()
682
- self .telegram_bot = None
683
689
684
690
while not self .trader .completed_loop :
685
691
self .running_live = False
@@ -703,7 +709,7 @@ def end_bot_gracefully(self, caller, callback=None):
703
709
if callback :
704
710
callback .emit ("Dumped all new data to database." )
705
711
706
- def end_crash_bot_and_create_popup (self , caller : int , msg : str ):
712
+ def end_crash_bot_and_create_popup (self , caller : str , msg : str ):
707
713
"""
708
714
Function that force ends bot in the event that it crashes.
709
715
"""
@@ -789,7 +795,7 @@ def update_interface_info(self, caller, value_dict: dict, grouped_dict: dict):
789
795
self .handle_position_buttons (caller = caller )
790
796
self .handle_custom_stop_loss_buttons (caller = caller )
791
797
792
- def update_interface_text (self , caller : int , value_dict : dict ):
798
+ def update_interface_text (self , caller : str , value_dict : dict ):
793
799
"""
794
800
Updates interface text based on caller and value dictionary provided.
795
801
:param caller: Caller that decides which interface gets updated.
@@ -805,7 +811,7 @@ def update_interface_text(self, caller: int, value_dict: dict):
805
811
main_interface_dictionary ['tickerValue' ].setText (value_dict ['tickerValue' ])
806
812
main_interface_dictionary ['positionValue' ].setText (value_dict ['currentPositionValue' ])
807
813
808
- def update_main_interface_and_graphs (self , caller : int , value_dict : dict ):
814
+ def update_main_interface_and_graphs (self , caller : str , value_dict : dict ):
809
815
"""
810
816
Updates main interface GUI elements based on caller.
811
817
:param value_dict: Dictionary with trader values in formatted data types.
@@ -1097,7 +1103,7 @@ def get_activity_table(self, caller):
1097
1103
else :
1098
1104
raise ValueError ("Invalid type of caller specified." )
1099
1105
1100
- def add_to_monitor (self , caller : int , message : str ):
1106
+ def add_to_monitor (self , caller : str , message : str ):
1101
1107
"""
1102
1108
Adds message to the monitor based on caller.
1103
1109
:param caller: Caller that determines which table gets the message.
@@ -1261,7 +1267,7 @@ def get_preferred_symbol(self) -> Union[None, str]:
1261
1267
else :
1262
1268
return None
1263
1269
1264
- def open_binance (self , caller : int = None ):
1270
+ def open_binance (self , caller : str = None ):
1265
1271
"""
1266
1272
Opens Binance hyperlink.
1267
1273
:param caller: If provided, it'll open the link to the caller's symbol's link on Binance. By default, if no
@@ -1278,7 +1284,7 @@ def open_binance(self, caller: int = None):
1278
1284
symbol = f"USDT_{ symbol [4 :]} " if index == 0 else f"{ symbol [:index ]} _USDT"
1279
1285
webbrowser .open (f"https://www.binance.com/en/trade/{ symbol } " )
1280
1286
1281
- def open_trading_view (self , caller : int = None ):
1287
+ def open_trading_view (self , caller : str = None ):
1282
1288
"""
1283
1289
Opens TradingView hyperlink.
1284
1290
:param caller: If provided, it'll open the link to the caller's symbol's link on TradingView.
@@ -1352,7 +1358,7 @@ def import_trades(self, caller):
1352
1358
label .setText ("Could not import trade history due to data corruption or no file being selected." )
1353
1359
self .logger .exception (str (e ))
1354
1360
1355
- def create_popup_and_emit_message (self , caller : int , message : str ):
1361
+ def create_popup_and_emit_message (self , caller : str , message : str ):
1356
1362
"""
1357
1363
Creates a popup and emits message simultaneously with caller and messages provided.
1358
1364
:param caller: Caller activity monitor to add message to.
@@ -1361,7 +1367,7 @@ def create_popup_and_emit_message(self, caller: int, message: str):
1361
1367
self .add_to_monitor (caller , message )
1362
1368
create_popup (self , message )
1363
1369
1364
- def get_lower_interval_data (self , caller : int ) -> Data :
1370
+ def get_lower_interval_data (self , caller : str ) -> Data :
1365
1371
"""
1366
1372
Returns interface's lower interval data object.
1367
1373
:param caller: Caller that determines which lower interval data object gets returned.
@@ -1374,7 +1380,7 @@ def get_lower_interval_data(self, caller: int) -> Data:
1374
1380
else :
1375
1381
raise TypeError ("Invalid type of caller specified." )
1376
1382
1377
- def get_trader (self , caller : int ) -> Union [SimulationTrader , Backtester ]:
1383
+ def get_trader (self , caller : str ) -> Union [SimulationTrader , Backtester ]:
1378
1384
"""
1379
1385
Returns a trader object.
1380
1386
:param caller: Caller that decides which trader object gets returned.
0 commit comments