refactor: Improve multi-ticker backtest error handling and indicator settings processing
This commit is contained in:
parent
840c96ede3
commit
1204b18a76
@ -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)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user