From 0bd3741e8a48b481c14846a029e42fc4c2505495 Mon Sep 17 00:00:00 2001 From: Andrew Kaszubski Date: Thu, 25 Dec 2025 15:23:28 +1100 Subject: [PATCH] feat: Add Anthropic LLM support and fix data vendor compatibility - Switch to Anthropic Claude as LLM provider in main.py - Add get_google_global_news() wrapper for proper signature matching - Add get_fundamentals() to yfinance vendor - Register yfinance and google as vendors for fundamentals and global news - Make memory system fail gracefully when OpenAI embeddings unavailable --- main.py | 12 +++++---- tradingagents/agents/utils/memory.py | 39 +++++++++++++++++----------- tradingagents/dataflows/google.py | 10 +++++++ tradingagents/dataflows/interface.py | 6 +++-- tradingagents/dataflows/y_finance.py | 37 ++++++++++++++++++++++++++ 5 files changed, 82 insertions(+), 22 deletions(-) diff --git a/main.py b/main.py index a85ee6ec..150af1fe 100644 --- a/main.py +++ b/main.py @@ -8,16 +8,18 @@ load_dotenv() # Create a custom config config = DEFAULT_CONFIG.copy() -config["deep_think_llm"] = "gpt-4o-mini" # Use a different model -config["quick_think_llm"] = "gpt-4o-mini" # Use a different model -config["max_debate_rounds"] = 1 # Increase debate rounds +config["llm_provider"] = "anthropic" +config["deep_think_llm"] = "claude-sonnet-4-20250514" +config["quick_think_llm"] = "claude-sonnet-4-20250514" +config["backend_url"] = "https://api.anthropic.com" +config["max_debate_rounds"] = 1 # debate rounds # Configure data vendors (default uses yfinance and alpha_vantage) config["data_vendors"] = { "core_stock_apis": "yfinance", # Options: yfinance, alpha_vantage, local "technical_indicators": "yfinance", # Options: yfinance, alpha_vantage, local - "fundamental_data": "alpha_vantage", # Options: openai, alpha_vantage, local - "news_data": "alpha_vantage", # Options: openai, alpha_vantage, google, local + "fundamental_data": "yfinance", # Options: openai, alpha_vantage, yfinance, local + "news_data": "google", # Options: openai, alpha_vantage, google, local } # Initialize with custom config diff --git a/tradingagents/agents/utils/memory.py b/tradingagents/agents/utils/memory.py index 69b8ab8c..4ecb8a28 100644 --- a/tradingagents/agents/utils/memory.py +++ b/tradingagents/agents/utils/memory.py @@ -46,25 +46,34 @@ class FinancialSituationMemory: def get_memories(self, current_situation, n_matches=1): """Find matching recommendations using OpenAI embeddings""" - query_embedding = self.get_embedding(current_situation) + try: + # Skip if collection is empty + if self.situation_collection.count() == 0: + return [] - results = self.situation_collection.query( - query_embeddings=[query_embedding], - n_results=n_matches, - include=["metadatas", "documents", "distances"], - ) + query_embedding = self.get_embedding(current_situation) - matched_results = [] - for i in range(len(results["documents"][0])): - matched_results.append( - { - "matched_situation": results["documents"][0][i], - "recommendation": results["metadatas"][0][i]["recommendation"], - "similarity_score": 1 - results["distances"][0][i], - } + results = self.situation_collection.query( + query_embeddings=[query_embedding], + n_results=n_matches, + include=["metadatas", "documents", "distances"], ) - return matched_results + matched_results = [] + for i in range(len(results["documents"][0])): + matched_results.append( + { + "matched_situation": results["documents"][0][i], + "recommendation": results["metadatas"][0][i]["recommendation"], + "similarity_score": 1 - results["distances"][0][i], + } + ) + + return matched_results + except Exception as e: + # Return empty if embedding fails (e.g., no OpenAI quota) + print(f"Memory lookup skipped (embedding unavailable): {e}") + return [] if __name__ == "__main__": diff --git a/tradingagents/dataflows/google.py b/tradingagents/dataflows/google.py index 3fe20f3c..92cf2cc6 100644 --- a/tradingagents/dataflows/google.py +++ b/tradingagents/dataflows/google.py @@ -4,6 +4,16 @@ from dateutil.relativedelta import relativedelta from .googlenews_utils import getNewsData +def get_google_global_news( + curr_date: Annotated[str, "Current date in yyyy-mm-dd format"], + look_back_days: Annotated[int, "Number of days to look back"] = 7, + limit: Annotated[int, "Maximum number of articles to return"] = 5, +) -> str: + """Wrapper for global news that uses Google News with market/economy query.""" + query = "stock+market+economy+finance" + return get_google_news(query, curr_date, look_back_days) + + def get_google_news( query: Annotated[str, "Query to search with"], curr_date: Annotated[str, "Curr date in yyyy-mm-dd format"], diff --git a/tradingagents/dataflows/interface.py b/tradingagents/dataflows/interface.py index 4cd5ddef..d1359c17 100644 --- a/tradingagents/dataflows/interface.py +++ b/tradingagents/dataflows/interface.py @@ -2,8 +2,8 @@ from typing import Annotated # Import from vendor-specific modules from .local import get_YFin_data, get_finnhub_news, get_finnhub_company_insider_sentiment, get_finnhub_company_insider_transactions, get_simfin_balance_sheet, get_simfin_cashflow, get_simfin_income_statements, get_reddit_global_news, get_reddit_company_news -from .y_finance import get_YFin_data_online, get_stock_stats_indicators_window, get_balance_sheet as get_yfinance_balance_sheet, get_cashflow as get_yfinance_cashflow, get_income_statement as get_yfinance_income_statement, get_insider_transactions as get_yfinance_insider_transactions -from .google import get_google_news +from .y_finance import get_YFin_data_online, get_stock_stats_indicators_window, get_balance_sheet as get_yfinance_balance_sheet, get_cashflow as get_yfinance_cashflow, get_income_statement as get_yfinance_income_statement, get_insider_transactions as get_yfinance_insider_transactions, get_fundamentals as get_yfinance_fundamentals +from .google import get_google_news, get_google_global_news from .openai import get_stock_news_openai, get_global_news_openai, get_fundamentals_openai from .alpha_vantage import ( get_stock as get_alpha_vantage_stock, @@ -78,6 +78,7 @@ VENDOR_METHODS = { # fundamental_data "get_fundamentals": { "alpha_vantage": get_alpha_vantage_fundamentals, + "yfinance": get_yfinance_fundamentals, "openai": get_fundamentals_openai, }, "get_balance_sheet": { @@ -104,6 +105,7 @@ VENDOR_METHODS = { }, "get_global_news": { "openai": get_global_news_openai, + "google": get_google_global_news, "local": get_reddit_global_news }, "get_insider_sentiment": { diff --git a/tradingagents/dataflows/y_finance.py b/tradingagents/dataflows/y_finance.py index da7273d5..2fae623f 100644 --- a/tradingagents/dataflows/y_finance.py +++ b/tradingagents/dataflows/y_finance.py @@ -383,6 +383,43 @@ def get_income_statement( return f"Error retrieving income statement for {ticker}: {str(e)}" +def get_fundamentals( + ticker: Annotated[str, "ticker symbol of the company"], + curr_date: str = None +): + """Get company fundamentals from yfinance.""" + try: + ticker_obj = yf.Ticker(ticker.upper()) + info = ticker_obj.info + + if not info: + return f"No fundamental data found for symbol '{ticker}'" + + # Extract key metrics (keep it concise to avoid context overflow) + key_fields = [ + 'longName', 'sector', 'industry', 'marketCap', 'enterpriseValue', + 'trailingPE', 'forwardPE', 'pegRatio', 'priceToBook', 'priceToSalesTrailing12Months', + 'profitMargins', 'operatingMargins', 'returnOnEquity', 'returnOnAssets', + 'revenueGrowth', 'earningsGrowth', 'currentRatio', 'debtToEquity', + 'totalRevenue', 'grossProfits', 'ebitda', 'netIncomeToCommon', + 'totalCash', 'totalDebt', 'freeCashflow', + 'dividendYield', 'payoutRatio', 'beta', 'fiftyTwoWeekHigh', 'fiftyTwoWeekLow', + 'targetMeanPrice', 'recommendationKey', 'numberOfAnalystOpinions' + ] + + result = f"# Company Fundamentals for {ticker.upper()}\n" + result += f"# Data retrieved on: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n" + + for field in key_fields: + if field in info and info[field] is not None: + result += f"{field}: {info[field]}\n" + + return result + + except Exception as e: + return f"Error retrieving fundamentals for {ticker}: {str(e)}" + + def get_insider_transactions( ticker: Annotated[str, "ticker symbol of the company"] ):