diff --git a/src/pages/backtesting/backtesting_page.py b/src/pages/backtesting/backtesting_page.py index 801f862..adfedbe 100644 --- a/src/pages/backtesting/backtesting_page.py +++ b/src/pages/backtesting/backtesting_page.py @@ -461,19 +461,28 @@ def run_multi_ticker_backtest(tickers: list, start_date: datetime, end_date: dat """Run backtest across multiple tickers and aggregate results""" all_results = [] + if not indicator_settings: + print("Error: No indicators selected") + raise ValueError("Please select at least one indicator") + # Convert indicator settings to the correct format processed_settings = {} for ind_name, ind_config in indicator_settings.items(): + processed_settings[ind_name] = { + 'type': ind_config['type'], + 'params': {} + } + # Handle both optimization and non-optimization cases if isinstance(ind_config['params'], dict): - # If params is a dictionary of parameter values (non-optimization mode) - processed_settings[ind_name] = { - 'type': ind_config['type'], - 'params': {k: float(v) if isinstance(v, (int, float)) else v - for k, v in ind_config['params'].items()} - } - else: - # If params is a dictionary of parameter ranges (optimization mode) - processed_settings[ind_name] = ind_config + for param_name, param_value in ind_config['params'].items(): + # Handle both direct values and range dictionaries + if isinstance(param_value, dict): + # Use the minimum value from the range for non-optimization run + processed_settings[ind_name]['params'][param_name] = float(param_value['min']) + else: + processed_settings[ind_name]['params'][param_name] = float(param_value) + + print(f"Processed indicator settings: {processed_settings}") for ticker in tickers: try: @@ -499,21 +508,24 @@ def run_multi_ticker_backtest(tickers: list, start_date: datetime, end_date: dat # Run backtest try: + # Set the indicator configs before creating the Backtest instance DynamicStrategy.indicator_configs = processed_settings + print(f"Strategy configured with settings: {processed_settings}") + bt = Backtest(df, DynamicStrategy, cash=100000, commission=.002) stats = bt.run() - + print(f"Backtest completed for {ticker}") print("Available stats keys:", stats.keys()) - + # Store results with error handling for each metric result = { 'Ticker': ticker, - 'Return [%]': float(stats.get('Return [%]', stats.get('Return', 0))), - 'Sharpe Ratio': float(stats.get('Sharpe Ratio', 0)), - 'Max Drawdown [%]': float(stats.get('Max. Drawdown [%]', stats.get('Max Drawdown', 0))), - 'Win Rate [%]': float(stats.get('Win Rate [%]', stats.get('Win Rate', 0))), - 'Number of Trades': int(stats.get('# Trades', stats.get('Number of Trades', 0))) + 'Return [%]': float(stats['Return [%]']), # Use direct key access + 'Sharpe Ratio': float(stats['Sharpe Ratio']), + 'Max Drawdown [%]': float(stats['Max. Drawdown [%]']), + 'Win Rate [%]': float(stats['Win Rate [%]']), + 'Number of Trades': int(stats['# Trades']) } # Verify results are valid @@ -537,12 +549,10 @@ def run_multi_ticker_backtest(tickers: list, start_date: datetime, end_date: dat continue if not all_results: - print("No valid results were generated. Check the following:") - print("1. Are the tickers valid?") - print("2. Is there data available for the selected date range?") - print("3. Are the indicator settings valid?") - print("4. Are there any errors in the strategy logic?") - print(f"Last used indicator settings: {processed_settings}") + print("\nNo valid results were generated. Debug information:") + print(f"Number of tickers processed: {len(tickers)}") + print(f"Indicator settings used: {processed_settings}") + print(f"Date range: {start_date} to {end_date}") raise ValueError("No valid results were generated from any ticker") return pd.DataFrame(all_results)