diff --git a/CHANGELOG.md b/CHANGELOG.md index cc539b92..8ccc748b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,9 @@ All notable changes to the **TradingAgents** project will be documented in this - **Payload Size Error**: Implemented input truncation (max 9000 chars) in `memory.py`. - **Display Layer De-Anonymization**: Added `deanonymize_text` to `TickerAnonymizer` and patched `cli/main.py` to reverse-map "ASSET_XXX" to real company names in the final report, effectively resolving "[Company Name]" placeholders for the user while keeping the internal system blind. - **Alpaca Integration**: Added `tradingagents/dataflows/alpaca.py` to support `get_stock_data` via Alpaca Data API v2. Registered as a vendor option in `interface.py` and `default_config.py`. Requires `ALPACA_API_KEY` and `ALPACA_API_SECRET` in `.env`. +- **CRITICAL FIX: Memory Leak**: Implemented `FinancialSituationMemory.clear()` and `TradingAgentsGraph.reset_memory()` to wipe agent context between runs. This prevents hallucinations from bleeding across days in long simulations. +- **CRITICAL FIX: Blind Logs**: Updated `_log_state` to explicitly capture `market_regime` and `regime_metrics`, ensuring we can audit decision logic relative to market conditions. +- **CRITICAL FIX: Crash Prevention**: Added guard logic in `propagate()` to handle "Dead State" (Rejected Trades) gracefully, preventing crashes when `process_signal` tries to read non-existent buy prices. diff --git a/tradingagents/agents/utils/memory.py b/tradingagents/agents/utils/memory.py index 2665c62e..bd5e25bf 100644 --- a/tradingagents/agents/utils/memory.py +++ b/tradingagents/agents/utils/memory.py @@ -95,6 +95,14 @@ class FinancialSituationMemory: return matched_results + def clear(self): + """Clear the memory by deleting and recreating the collection.""" + try: + self.chroma_client.delete_collection(self.situation_collection.name) + self.situation_collection = self.chroma_client.create_collection(name=self.situation_collection.name) + except Exception as e: + print(f"Warning: Failed to clear memory {self.situation_collection.name}: {e}") + if __name__ == "__main__": # Example usage matcher = FinancialSituationMemory() diff --git a/tradingagents/graph/trading_graph.py b/tradingagents/graph/trading_graph.py index b53bb21a..9a74929c 100644 --- a/tradingagents/graph/trading_graph.py +++ b/tradingagents/graph/trading_graph.py @@ -165,8 +165,18 @@ class TradingAgentsGraph: ), } + def reset_memory(self): + """Clear all agent memories to prevent context bleeding between runs.""" + self.bull_memory.clear() + self.bear_memory.clear() + self.trader_memory.clear() + self.invest_judge_memory.clear() + self.risk_manager_memory.clear() + def propagate(self, company_name, trade_date): """Run the trading agents graph for a company on a specific date.""" + # 1. FIX MEMORY LEAK + self.reset_memory() self.ticker = company_name @@ -197,14 +207,30 @@ class TradingAgentsGraph: # Log state self._log_state(trade_date, final_state) - # Return decision and processed signal - return final_state, self.process_signal(final_state["final_trade_decision"]) + # 3. FIX CRASH RISK: Handle Dead State gracefully + trade_decision = final_state["final_trade_decision"] + + # If trade was rejected by a Gate (Fact Check or Risk), return raw decision + if trade_decision.get("action") == "HOLD" and "REJECTED" in trade_decision.get("reasoning", ""): + processed_signal = { + "action": "HOLD", + "quantity": 0, + "reason": trade_decision["reasoning"] + } + else: + # Only process if it's a valid attempt + processed_signal = self.process_signal(trade_decision) + + return final_state, processed_signal def _log_state(self, trade_date, final_state): """Log the final state to a JSON file.""" self.log_states_dict[str(trade_date)] = { "company_of_interest": final_state["company_of_interest"], "trade_date": final_state["trade_date"], + # 2. FIX BLIND SPOT: Log the Math + "market_regime": final_state.get("market_regime", "UNKNOWN"), + "regime_metrics": final_state.get("regime_metrics", {}), "market_report": final_state["market_report"], "sentiment_report": final_state["sentiment_report"], "news_report": final_state["news_report"],