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
This commit is contained in:
Andrew Kaszubski 2025-12-25 15:23:28 +11:00
parent 13b826a31d
commit 0bd3741e8a
5 changed files with 82 additions and 22 deletions

12
main.py
View File

@ -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

View File

@ -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__":

View File

@ -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"],

View File

@ -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": {

View File

@ -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"]
):