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:
parent
13b826a31d
commit
0bd3741e8a
12
main.py
12
main.py
|
|
@ -8,16 +8,18 @@ load_dotenv()
|
||||||
|
|
||||||
# Create a custom config
|
# Create a custom config
|
||||||
config = DEFAULT_CONFIG.copy()
|
config = DEFAULT_CONFIG.copy()
|
||||||
config["deep_think_llm"] = "gpt-4o-mini" # Use a different model
|
config["llm_provider"] = "anthropic"
|
||||||
config["quick_think_llm"] = "gpt-4o-mini" # Use a different model
|
config["deep_think_llm"] = "claude-sonnet-4-20250514"
|
||||||
config["max_debate_rounds"] = 1 # Increase debate rounds
|
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)
|
# Configure data vendors (default uses yfinance and alpha_vantage)
|
||||||
config["data_vendors"] = {
|
config["data_vendors"] = {
|
||||||
"core_stock_apis": "yfinance", # Options: yfinance, alpha_vantage, local
|
"core_stock_apis": "yfinance", # Options: yfinance, alpha_vantage, local
|
||||||
"technical_indicators": "yfinance", # Options: yfinance, alpha_vantage, local
|
"technical_indicators": "yfinance", # Options: yfinance, alpha_vantage, local
|
||||||
"fundamental_data": "alpha_vantage", # Options: openai, alpha_vantage, local
|
"fundamental_data": "yfinance", # Options: openai, alpha_vantage, yfinance, local
|
||||||
"news_data": "alpha_vantage", # Options: openai, alpha_vantage, google, local
|
"news_data": "google", # Options: openai, alpha_vantage, google, local
|
||||||
}
|
}
|
||||||
|
|
||||||
# Initialize with custom config
|
# Initialize with custom config
|
||||||
|
|
|
||||||
|
|
@ -46,25 +46,34 @@ class FinancialSituationMemory:
|
||||||
|
|
||||||
def get_memories(self, current_situation, n_matches=1):
|
def get_memories(self, current_situation, n_matches=1):
|
||||||
"""Find matching recommendations using OpenAI embeddings"""
|
"""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_embedding = self.get_embedding(current_situation)
|
||||||
query_embeddings=[query_embedding],
|
|
||||||
n_results=n_matches,
|
|
||||||
include=["metadatas", "documents", "distances"],
|
|
||||||
)
|
|
||||||
|
|
||||||
matched_results = []
|
results = self.situation_collection.query(
|
||||||
for i in range(len(results["documents"][0])):
|
query_embeddings=[query_embedding],
|
||||||
matched_results.append(
|
n_results=n_matches,
|
||||||
{
|
include=["metadatas", "documents", "distances"],
|
||||||
"matched_situation": results["documents"][0][i],
|
|
||||||
"recommendation": results["metadatas"][0][i]["recommendation"],
|
|
||||||
"similarity_score": 1 - results["distances"][0][i],
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
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__":
|
if __name__ == "__main__":
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,16 @@ from dateutil.relativedelta import relativedelta
|
||||||
from .googlenews_utils import getNewsData
|
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(
|
def get_google_news(
|
||||||
query: Annotated[str, "Query to search with"],
|
query: Annotated[str, "Query to search with"],
|
||||||
curr_date: Annotated[str, "Curr date in yyyy-mm-dd format"],
|
curr_date: Annotated[str, "Curr date in yyyy-mm-dd format"],
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ from typing import Annotated
|
||||||
|
|
||||||
# Import from vendor-specific modules
|
# 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 .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 .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
|
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 .openai import get_stock_news_openai, get_global_news_openai, get_fundamentals_openai
|
||||||
from .alpha_vantage import (
|
from .alpha_vantage import (
|
||||||
get_stock as get_alpha_vantage_stock,
|
get_stock as get_alpha_vantage_stock,
|
||||||
|
|
@ -78,6 +78,7 @@ VENDOR_METHODS = {
|
||||||
# fundamental_data
|
# fundamental_data
|
||||||
"get_fundamentals": {
|
"get_fundamentals": {
|
||||||
"alpha_vantage": get_alpha_vantage_fundamentals,
|
"alpha_vantage": get_alpha_vantage_fundamentals,
|
||||||
|
"yfinance": get_yfinance_fundamentals,
|
||||||
"openai": get_fundamentals_openai,
|
"openai": get_fundamentals_openai,
|
||||||
},
|
},
|
||||||
"get_balance_sheet": {
|
"get_balance_sheet": {
|
||||||
|
|
@ -104,6 +105,7 @@ VENDOR_METHODS = {
|
||||||
},
|
},
|
||||||
"get_global_news": {
|
"get_global_news": {
|
||||||
"openai": get_global_news_openai,
|
"openai": get_global_news_openai,
|
||||||
|
"google": get_google_global_news,
|
||||||
"local": get_reddit_global_news
|
"local": get_reddit_global_news
|
||||||
},
|
},
|
||||||
"get_insider_sentiment": {
|
"get_insider_sentiment": {
|
||||||
|
|
|
||||||
|
|
@ -383,6 +383,43 @@ def get_income_statement(
|
||||||
return f"Error retrieving income statement for {ticker}: {str(e)}"
|
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(
|
def get_insider_transactions(
|
||||||
ticker: Annotated[str, "ticker symbol of the company"]
|
ticker: Annotated[str, "ticker symbol of the company"]
|
||||||
):
|
):
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue