From 4feaf51b4c3134bfb25d46700e7f7c06eb66ae7e Mon Sep 17 00:00:00 2001 From: "Bobby (aider)" Date: Thu, 13 Feb 2025 23:19:36 -0800 Subject: [PATCH] feat: Add comprehensive debug logging to trading strategy and optimization --- src/pages/backtesting/backtesting_page.py | 48 ++++++++++++++++++++--- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/src/pages/backtesting/backtesting_page.py b/src/pages/backtesting/backtesting_page.py index 0f11fb1..d8a4224 100644 --- a/src/pages/backtesting/backtesting_page.py +++ b/src/pages/backtesting/backtesting_page.py @@ -71,12 +71,20 @@ class DynamicStrategy(Strategy): def next(self): price = self.data.Close[-1] + # Debug current price and position + if len(self.data.Close) % 20 == 0: # Log every 20th candle to avoid spam + print(f"Current price: {price}, Has position: {bool(self.position)}") + # Example trading logic using indicators for ind_name, ind_config in self.indicator_configs.items(): if ind_config['type'] == 'SMA': - if price > self.indicators[ind_name][-1] and not self.position: + current_sma = self.indicators[ind_name][-1] + print(f"SMA - Price: {price:.2f}, SMA: {current_sma:.2f}") + if price > current_sma and not self.position: + print(f"BUY signal - Price above SMA") self.buy() - elif price < self.indicators[ind_name][-1] and self.position: + elif price < current_sma and self.position: + print(f"SELL signal - Price below SMA") self.position.close() elif ind_config['type'] == 'MACD': @@ -85,36 +93,49 @@ class DynamicStrategy(Strategy): prev_macd = self.indicators[f"{ind_name}_macd"][-2] prev_signal = self.indicators[f"{ind_name}_signal"][-2] + print(f"MACD - MACD: {macd:.2f}, Signal: {signal:.2f}") if macd > signal and prev_macd <= prev_signal and not self.position: + print(f"BUY signal - MACD crossover") self.buy() elif macd < signal and prev_macd >= prev_signal and self.position: + print(f"SELL signal - MACD crossunder") self.position.close() elif ind_config['type'] == 'BB': upper = self.indicators[f"{ind_name}_upper"][-1] lower = self.indicators[f"{ind_name}_lower"][-1] + print(f"BB - Price: {price:.2f}, Upper: {upper:.2f}, Lower: {lower:.2f}") # Buy when price touches lower band if price <= lower and not self.position: + print(f"BUY signal - Price at lower band") self.buy() # Sell when price touches upper band elif price >= upper and self.position: + print(f"SELL signal - Price at upper band") self.position.close() elif ind_config['type'] == 'RSI': rsi = self.indicators[ind_name][-1] + print(f"RSI - Value: {rsi:.2f}") # Buy when RSI is below 30 (oversold) if rsi < 30 and not self.position: + print(f"BUY signal - RSI oversold") self.buy() # Sell when RSI is above 70 (overbought) elif rsi > 70 and self.position: + print(f"SELL signal - RSI overbought") self.position.close() elif ind_config['type'] == 'EMA': - if price > self.indicators[ind_name][-1] and not self.position: + current_ema = self.indicators[ind_name][-1] + print(f"EMA - Price: {price:.2f}, EMA: {current_ema:.2f}") + if price > current_ema and not self.position: + print(f"BUY signal - Price above EMA") self.buy() - elif price < self.indicators[ind_name][-1] and self.position: + elif price < current_ema and self.position: + print(f"SELL signal - Price below EMA") self.position.close() def get_available_indicators() -> Dict: @@ -149,22 +170,31 @@ def get_available_indicators() -> Dict: def prepare_data_for_backtest(df: pd.DataFrame) -> pd.DataFrame: """Prepare the dataframe for backtesting""" + print("\nPreparing data for backtest...") + print(f"Initial dataframe shape: {df.shape}") + print(f"Initial columns: {df.columns.tolist()}") + # Ensure the dataframe has the required columns required_columns = ['Open', 'High', 'Low', 'Close', 'Volume'] # Rename columns if needed df = df.copy() df.columns = [c.capitalize() for c in df.columns] + print(f"Columns after capitalization: {df.columns.tolist()}") # Set the date as index if it's not already if 'Date' in df.columns: df.set_index('Date', inplace=True) + print("Set Date as index") # Verify all required columns exist missing_cols = [col for col in required_columns if col not in df.columns] if missing_cols: + print(f"WARNING: Missing columns: {missing_cols}") raise ValueError(f"Missing required columns: {missing_cols}") + print(f"Final dataframe shape: {df.shape}") + return df def backtesting_page(): @@ -259,9 +289,13 @@ def backtesting_page(): def run_optimization(df: pd.DataFrame, indicator_settings: Dict) -> List: """Run optimization with different parameter combinations""" param_combinations = generate_param_combinations(indicator_settings) + print(f"Generated {len(param_combinations)} parameter combinations") results = [] - for params in param_combinations: + for i, params in enumerate(param_combinations): + print(f"\nTesting combination {i+1}/{len(param_combinations)}") + print(f"Parameters: {params}") + # Configure strategy with current parameters DynamicStrategy.indicator_configs = params @@ -269,6 +303,10 @@ def run_optimization(df: pd.DataFrame, indicator_settings: Dict) -> List: bt = Backtest(df, DynamicStrategy, cash=100000, commission=.002) stats = bt.run() + print(f"Results - Return: {stats['Return [%]']:.2f}%, " + f"Sharpe: {stats['Sharpe Ratio']:.2f}, " + f"Drawdown: {stats['Max. Drawdown [%]']:.2f}%") + results.append({ 'parameters': params, 'Return [%]': stats['Return [%]'],