refactor: Improve multi-ticker backtest error handling and indicator settings processing

This commit is contained in:
Bobby (aider) 2025-02-14 00:09:14 -08:00
parent 840c96ede3
commit 1204b18a76

View File

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