Skip to content

Commit 3d5b05d

Browse files
committed
add extra validation to optimizer/backtester and when importing, made progress bar jump to 100 if successful
1 parent 7ede6ce commit 3d5b05d

File tree

4 files changed

+67
-28
lines changed

4 files changed

+67
-28
lines changed

algobot/__main__.py

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -264,34 +264,60 @@ def export_optimizer(self, file_type: str):
264264
else:
265265
create_popup(self, 'No table rows found because optimizer has not been run yet.')
266266

267-
def validate_optimizer(self, combos: dict):
267+
def validate_optimizer_or_backtest(self, caller):
268268
"""
269-
Validate optimizer to ensure it is correctly setup.
269+
Validate optimizer/backtester to ensure it is correctly setup.
270270
:return: False if not validated and true if validated.
271271
"""
272-
if not self.validate_ticker(OPTIMIZER):
272+
config = self.configuration
273+
noun = 'optimizer' if caller == OPTIMIZER else 'backtester'
274+
275+
if not self.validate_ticker(caller):
273276
return False
274277

275-
if self.configuration.optimizer_backtest_dict[OPTIMIZER]['data'] is None:
276-
create_popup(self, "No data setup yet for optimizer. Please configure them in settings first.")
278+
if config.optimizer_backtest_dict[caller]['data'] is None:
279+
create_popup(self, f"No data setup yet for {noun}. Please download or import data in settings first.")
277280
return False
278281

279-
if self.configuration.get_optimizer_settings()['strategies'] == {}:
280-
create_popup(self, "No strategies found. Make sure you have some strategies for optimization.")
282+
selected_symbol = self.interfaceDictionary[caller]['configuration']['ticker'].text()
283+
download_symbol = config.optimizer_backtest_dict[caller]['dataType']
284+
285+
if selected_symbol != download_symbol and download_symbol.lower() != 'imported':
286+
create_popup(self, f"{noun.capitalize()} symbol ({selected_symbol}) does not match downloaded symbol "
287+
f"({download_symbol}). Change your ticker to ({download_symbol}) "
288+
f"or download ({selected_symbol}) data to get rid of this error.")
281289
return False
282290

283-
if not self.check_combos(combos['strategies']):
284-
create_popup(self, "Please configure your strategies correctly.")
291+
selected_interval = self.interfaceDictionary[caller]['configuration']['interval'].currentText().lower()
292+
download_interval = config.optimizer_backtest_dict[caller]['dataInterval'].lower()
293+
294+
if selected_interval != download_interval and download_symbol.lower() != 'imported':
295+
create_popup(self, f"{noun.capitalize()} interval ({selected_interval}) does not match downloaded interval "
296+
f"({download_interval}). Change your data interval to ({download_interval}) "
297+
f"or download ({selected_interval}) data to get rid of this error.")
285298
return False
286299

300+
if download_symbol.lower() == 'imported':
301+
# TODO: Add verification for imports.
302+
create_popup(self, "You are using imported data. Please ensure they logically work correctly with your "
303+
"inputs. Algobot currently does not support imported data verification.")
304+
287305
return True
288306

289307
def initiate_optimizer(self):
290308
"""
291309
Main function to begin optimization.
292310
"""
311+
if not self.validate_optimizer_or_backtest(caller=OPTIMIZER):
312+
return
313+
293314
combos = self.configuration.get_optimizer_settings()
294-
if not self.validate_optimizer(combos=combos):
315+
if combos['strategies'] == {}:
316+
create_popup(self, "No strategies found. Make sure you have some strategies for optimization.")
317+
return
318+
319+
if not self.check_combos(combos['strategies']):
320+
create_popup(self, "Please configure your strategies correctly.")
295321
return
296322

297323
self.threads[OPTIMIZER] = optimizerThread.OptimizerThread(gui=self, logger=self.logger, combos=combos)
@@ -322,11 +348,7 @@ def initiate_backtest(self):
322348
"""
323349
Initiates backtest based on settings configured. If there is no data configured, prompts user to configure data.
324350
"""
325-
if not self.validate_ticker(BACKTEST):
326-
return
327-
328-
if self.configuration.optimizer_backtest_dict[BACKTEST]['data'] is None:
329-
create_popup(self, "No data setup yet for backtesting. Please configure them in settings first.")
351+
if not self.validate_optimizer_or_backtest(caller=BACKTEST):
330352
return
331353

332354
if not self.check_strategies(BACKTEST):
@@ -500,7 +522,8 @@ def validate_ticker(self, caller: int):
500522
create_popup(self, "Please specify a ticker. No ticker found.")
501523
return False
502524
if selected_ticker not in self.tickers:
503-
create_popup(self, "Invalid ticker specified. Please check your settings and configure it correctly.")
525+
create_popup(self, f'Invalid ticker "{selected_ticker}" provided. If it is valid, '
526+
f'then try updating your tickers in the configuration settings.')
504527
return False
505528
return True
506529

algobot/algodict.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,13 @@ def get_interface_dictionary(parent, caller: int = None):
106106
'mainConfigurationTabWidget': parent.configuration.backtestConfigurationTabWidget,
107107
'precision': parent.configuration.backtestPrecisionSpinBox,
108108
'ticker': parent.configuration.backtestTickerLineEdit,
109+
'interval': parent.configuration.backtestIntervalComboBox,
109110
},
110111
},
111112
OPTIMIZER: {
112113
'configuration': {
113114
'ticker': parent.configuration.optimizerTickerLineEdit,
115+
'interval': parent.configuration.optimizerIntervalComboBox,
114116
},
115117
}
116118
}

algobot/interface/config_utils/data_utils.py

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,21 @@ def import_data(config_obj, caller: int = BACKTEST):
1515
:param config_obj: Configuration QDialog object (from configuration.py)
1616
:param caller: Caller that'll determine who called this function -> OPTIMIZER or BACKTEST
1717
"""
18-
config_obj.optimizer_backtest_dict[caller]['infoLabel'].setText("Importing data...")
18+
inner_dict = config_obj.optimizer_backtest_dict[caller]
19+
action = 'backtest' if caller == BACKTEST else 'optimization'
20+
21+
inner_dict['infoLabel'].setText("Importing data...")
1922
filePath, _ = QFileDialog.getOpenFileName(config_obj, 'Open file', helpers.ROOT_DIR, "CSV (*.csv)")
2023
if filePath == '':
21-
config_obj.optimizer_backtest_dict[caller]['infoLabel'].setText("Data not imported.")
24+
inner_dict['infoLabel'].setText("Data not imported.")
25+
inner_dict['downloadProgress'].setValue(0)
2226
else:
23-
config_obj.optimizer_backtest_dict[caller]['data'] = helpers.load_from_csv(filePath, descending=False)
24-
config_obj.optimizer_backtest_dict[caller]['dataType'] = "Imported"
25-
config_obj.optimizer_backtest_dict[caller]['infoLabel'].setText("Imported data successfully.")
26-
config_obj.optimizer_backtest_dict[caller]['dataLabel'].setText('Using imported data to conduct backtest.')
27+
inner_dict['data'] = helpers.load_from_csv(filePath, descending=False)
28+
inner_dict['dataType'] = "Imported"
29+
inner_dict['dataInterval'] = inner_dict['dataIntervalComboBox'].currentText()
30+
inner_dict['infoLabel'].setText("Imported data successfully.")
31+
inner_dict['dataLabel'].setText(f'Using imported data to conduct {action}.')
32+
inner_dict['downloadProgress'].setValue(100)
2733
setup_calendar(config_obj=config_obj, caller=caller)
2834

2935

@@ -59,13 +65,17 @@ def set_downloaded_data(config_obj, data, caller: int = BACKTEST):
5965
:param caller: Caller that'll determine which caller was used.
6066
:param data: Data to be used for backtesting.
6167
"""
62-
symbol = config_obj.optimizer_backtest_dict[caller]['tickers'].text()
63-
interval = config_obj.optimizer_backtest_dict[caller]['intervals'].currentText().lower()
68+
inner_dict = config_obj.optimizer_backtest_dict[caller]
69+
action = 'backtest' if caller == BACKTEST else 'optimization'
70+
71+
symbol = inner_dict['tickers'].text()
72+
interval = inner_dict['intervals'].currentText().lower()
6473

65-
config_obj.optimizer_backtest_dict[caller]['data'] = data
66-
config_obj.optimizer_backtest_dict[caller]['dataType'] = symbol
67-
config_obj.optimizer_backtest_dict[caller]['infoLabel'].setText(f"Downloaded {interval} {symbol} data.")
68-
config_obj.optimizer_backtest_dict[caller]['dataLabel'].setText(f'Using {interval} {symbol} data to run backtest.')
74+
inner_dict['dataInterval'] = inner_dict['dataIntervalComboBox'].currentText()
75+
inner_dict['data'] = data
76+
inner_dict['dataType'] = symbol
77+
inner_dict['infoLabel'].setText(f"Downloaded {interval} {symbol} data.")
78+
inner_dict['dataLabel'].setText(f'Using {interval} {symbol} data to run {action}.')
6979
setup_calendar(config_obj=config_obj, caller=caller)
7080

7181

algobot/interface/configuration.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ def __init__(self, parent: QMainWindow, logger: Logger = None):
3939
'tickers': self.backtestTickerLineEdit,
4040
'intervals': self.backtestIntervalComboBox,
4141
'data': None,
42+
'dataIntervalComboBox': self.backtestIntervalComboBox,
43+
'dataInterval': None,
4244
'dataType': None,
4345
'infoLabel': self.backtestInfoLabel,
4446
'dataLabel': self.backtestDataLabel,
@@ -55,6 +57,8 @@ def __init__(self, parent: QMainWindow, logger: Logger = None):
5557
'tickers': self.optimizerTickerLineEdit,
5658
'intervals': self.optimizerIntervalComboBox,
5759
'data': None,
60+
'dataIntervalComboBox': self.optimizerIntervalComboBox,
61+
'dataInterval': None,
5862
'dataType': None,
5963
'infoLabel': self.optimizerInfoLabel,
6064
'dataLabel': self.optimizerDataLabel,

0 commit comments

Comments
 (0)