import logging import yfinance as yf class FundamentalAnalysis: def __init__( self, ticker: str, debug: bool=False): """ Initialize FundamentalAnalysis object. Args: ticker : str Stock ticker to analyze. debug : bool, optional, default: False Whether to run in debug mode, so that logging is produced at debug level. """ # set up logging if debug: self.logger_level = logging.DEBUG else: self.logger_level = logging.INFO self.logger = logging.getLogger(__name__) logging.basicConfig(level=self.logger_level) # filename='FundamentalAnalysis.log', # input args self.ticker = ticker # done initializing self.logger.info(f'Initialized TechnicalAnalysis object for ticker: {ticker}') def run( self ) -> dict: """ Runs the fundamental analysis for the given ticker. Returns: dict: A dictionary with fundamental metrics for the given ticker. """ self.info = self.fetch_stock_info() if len(self.info) > 0: result = self.get_stock_fundamentals() else: result = {} return result def fetch_stock_info( self ) -> dict: """ Fetches all available information for a given stock ticker using yfinance. Returns: dict: A dictionary of key-value pairs with available information for the given ticker. """ try: stock = yf.Ticker(self.ticker) info = stock.info # Fetch all available info except Exception as e: self.logger.error(f"Error fetching stock info for {self.ticker}: {str(e)}") info = {} if len(info) == 1: info = {} else: self.logger.info(f"Fetched stock info for {self.ticker}") return info def get_stock_fundamentals( self ) -> dict: """ Attempts to fetches key fundamental metrics for a given stock ticker using yfinance. Returns: dict: Dictionary of fundamental metrics with human-readable keys """ # Define a safe fetch method to avoid entire function failure def safe_get(key, transform=None): value = self.info.get(key, "Metric not available") if value != "Metric not available" and transform: try: return transform(value) except Exception: return "metric not available" return value # Extract key financial metrics fundamentals = { "Company Name": safe_get("longName"), "Sector": safe_get("sector"), "Industry": safe_get("industry"), "Market Capitalization": safe_get("marketCap"), # Valuation Metrics "P/E Ratio (Trailing)": safe_get("trailingPE"), "P/E Ratio (Forward)": safe_get("forwardPE"), "P/B Ratio (Price to Book)": safe_get("priceToBook"), "P/S Ratio (Price to Sales)": safe_get("priceToSalesTrailing12Months"), "Dividend Yield (%)": safe_get("dividendYield", lambda x: round(x * 100, 2)), # Profitability Metrics "Earnings Per Share (EPS)": safe_get("trailingEps"), "Return on Equity (ROE)": safe_get("returnOnEquity"), "Return on Assets (ROA)": safe_get("returnOnAssets"), "Gross Margin (%)": safe_get("grossMargins", lambda x: round(x * 100, 2)), "Operating Margin (%)": safe_get("operatingMargins", lambda x: round(x * 100, 2)), # Financial Health Metrics "Debt-to-Equity Ratio": safe_get("debtToEquity"), "Current Ratio": safe_get("currentRatio"), "Quick Ratio": safe_get("quickRatio"), "Interest Coverage Ratio": safe_get( "ebitda", lambda ebitda: round(ebitda / self.info["totalDebt"], 2) if self.info.get("totalDebt") else "Metric not available" ), # Growth Metrics "Revenue Growth (%)": safe_get("revenueGrowth", lambda x: round(x * 100, 2)), "EPS Growth (%)": safe_get("earningsGrowth", lambda x: round(x * 100, 2)), # Market & Ownership "Institutional Ownership (%)": safe_get("heldPercentInstitutions", lambda x: round(x * 100, 2)), "Insider Ownership (%)": safe_get("heldPercentInsiders", lambda x: round(x * 100, 2)), } return fundamentals if __name__ == '__main__': dict_fundamentals = FundamentalAnalysis(ticker="GOOG").run() if len(dict_fundamentals) > 0: for key, value in dict_fundamentals.items(): print(f"{key}: {value}") else: print("No fundamental data available.")