import json from os import getenv from typing import Optional, Literal, Any from phi.tools import Toolkit from phi.utils.log import logger try: from openbb import obb as openbb_app except ImportError: raise ImportError("`openbb` not installed. Please install using `pip install 'openbb[all]'`.") class OpenBBTools(Toolkit): def __init__( self, obb: Optional[Any] = None, openbb_pat: Optional[str] = None, provider: Literal["benzinga", "fmp", "intrinio", "polygon", "tiingo", "tmx", "yfinance"] = "yfinance", stock_price: bool = True, search_symbols: bool = False, company_news: bool = False, company_profile: bool = False, price_targets: bool = False, ): super().__init__(name="yfinance_tools") self.obb = obb or openbb_app try: if openbb_pat or getenv("OPENBB_PAT"): self.obb.account.login(pat=openbb_pat or getenv("OPENBB_PAT")) # type: ignore except Exception as e: logger.error(f"Error logging into OpenBB: {e}") self.provider: Literal["benzinga", "fmp", "intrinio", "polygon", "tiingo", "tmx", "yfinance"] = provider if stock_price: self.register(self.get_stock_price) if search_symbols: self.register(self.search_company_symbol) if company_news: self.register(self.get_company_news) if company_profile: self.register(self.get_company_profile) if price_targets: self.register(self.get_price_targets) def get_stock_price(self, symbol: str) -> str: """Use this function to get the current stock price for a stock symbol or list of symbols. Args: symbol (str): The stock symbol or list of stock symbols. Eg: "AAPL" or "AAPL,MSFT,GOOGL" Returns: str: The current stock prices or error message. """ try: result = self.obb.equity.price.quote(symbol=symbol, provider=self.provider).to_polars() # type: ignore clean_results = [] for row in result.to_dicts(): clean_results.append( { "symbol": row.get("symbol"), "last_price": row.get("last_price"), "currency": row.get("currency"), "name": row.get("name"), "high": row.get("high"), "low": row.get("low"), "open": row.get("open"), "close": row.get("close"), "prev_close": row.get("prev_close"), "volume": row.get("volume"), "ma_50d": row.get("ma_50d"), "ma_200d": row.get("ma_200d"), } ) return json.dumps(clean_results, indent=2, default=str) except Exception as e: return f"Error fetching current price for {symbol}: {e}" def search_company_symbol(self, company_name: str) -> str: """Use this function to get a list of ticker symbols for a company. Args: company_name (str): The name of the company. Returns: str: A JSON string containing the ticker symbols. """ logger.debug(f"Search ticker for {company_name}") result = self.obb.equity.search(company_name).to_polars() # type: ignore clean_results = [] if len(result) > 0: for row in result.to_dicts(): clean_results.append({"symbol": row.get("symbol"), "name": row.get("name")}) return json.dumps(clean_results, indent=2, default=str) def get_price_targets(self, symbol: str) -> str: """Use this function to get consensus price target and recommendations for a stock symbol or list of symbols. Args: symbol (str): The stock symbol or list of stock symbols. Eg: "AAPL" or "AAPL,MSFT,GOOGL" Returns: str: JSON containing consensus price target and recommendations. """ try: result = self.obb.equity.estimates.consensus(symbol=symbol, provider=self.provider).to_polars() # type: ignore return json.dumps(result.to_dicts(), indent=2, default=str) except Exception as e: return f"Error fetching company news for {symbol}: {e}" def get_company_news(self, symbol: str, num_stories: int = 10) -> str: """Use this function to get company news for a stock symbol or list of symbols. Args: symbol (str): The stock symbol or list of stock symbols. Eg: "AAPL" or "AAPL,MSFT,GOOGL" num_stories (int): The number of news stories to return. Defaults to 10. Returns: str: JSON containing company news and press releases. """ try: result = self.obb.news.company(symbol=symbol, provider=self.provider, limit=num_stories).to_polars() # type: ignore clean_results = [] if len(result) > 0: for row in result.to_dicts(): row.pop("images") clean_results.append(row) return json.dumps(clean_results[:num_stories], indent=2, default=str) except Exception as e: return f"Error fetching company news for {symbol}: {e}" def get_company_profile(self, symbol: str) -> str: """Use this function to get company profile and overview for a stock symbol or list of symbols. Args: symbol (str): The stock symbol or list of stock symbols. Eg: "AAPL" or "AAPL,MSFT,GOOGL" Returns: str: JSON containing company profile and overview. """ try: result = self.obb.equity.profile(symbol=symbol, provider=self.provider).to_polars() # type: ignore return json.dumps(result.to_dicts(), indent=2, default=str) except Exception as e: return f"Error fetching company profile for {symbol}: {e}"