diff --git a/backend/app/services/trade_executor.py b/backend/app/services/trade_executor.py index 0f4e8e1..56a59a2 100644 --- a/backend/app/services/trade_executor.py +++ b/backend/app/services/trade_executor.py @@ -70,6 +70,11 @@ async def _mcp_validate_buy(stock: dict) -> tuple: async def execute_auto_trade_background(recommendations_response: dict, db: AsyncSession): + # recommendations_response should be a dict containing 'recommendations_by_rule'. + if not isinstance(recommendations_response, dict): + logger.error("recommendations_response is not a dict: %r", recommendations_response) + return + try: trade_results = [] total_processed = 0 @@ -87,7 +92,20 @@ async def execute_auto_trade_background(recommendations_response: dict, db: Asyn unique_stocks = {} for rule_group in recommendations_response.get("recommendations_by_rule", []): - for stock in rule_group["stocks"]: + # make sure we have a dict with expected keys + if not isinstance(rule_group, dict): + logger.warning("Invalid rule_group in recommendations_response: %r", rule_group) + continue + stocks_list = rule_group.get("stocks") + if not isinstance(stocks_list, list): + logger.warning("Invalid stocks list in rule_group: %r", stocks_list) + continue + + for stock in stocks_list: + if not isinstance(stock, dict): + logger.warning("Skipping non-dict stock entry: %r", stock) + continue + stock_code = stock.get("stock_code") if stock_code not in unique_stocks: unique_stocks[stock_code] = stock @@ -100,6 +118,11 @@ async def execute_auto_trade_background(recommendations_response: dict, db: Asyn ) for stock in sorted_stocks[:max_trades]: + # ensure stock is a dict before proceeding + if not isinstance(stock, dict): + logger.warning("Skipping invalid stock entry in sorted_stocks: %r", stock) + continue + try: stock["_position_multiplier"] = position_multiplier stock["_regime_str"] = market_regime.get("regime_200d", "bear") if market_regime else "bear" @@ -112,11 +135,12 @@ async def execute_auto_trade_background(recommendations_response: dict, db: Asyn await asyncio.sleep(0.5) except Exception as e: + # stock is dict so we can safely access get logger.error(f"Error executing auto-trade for {stock.get('stock_code')}: {e}") continue except Exception as e: - logger.error(f"❌ Background auto-trade error: {e}") + logger.error(f"❌ Background auto-trade error: {e} - recommendations_response={recommendations_response!r}") async def _background_sync_portfolio():