""" Calculate all financial metrics from base numbers Implements all formulas from Step 4 of README """ import json import os from typing import Dict, Any, Optional class FinancialMetricsCalculator: """Calculate financial metrics from raw financial statements""" def __init__(self): self.metrics = {} def parse_yahoo_value(self, value_str: str) -> float: """Parse Yahoo Finance value strings (e.g., '416.16B', '26.92%')""" if not value_str or value_str == 'N/A': return 0 value_str = str(value_str).strip() # Handle percentages if '%' in value_str: return float(value_str.replace('%', '').replace(',', '')) / 100 # Handle large numbers with suffixes multipliers = {'K': 1e3, 'M': 1e6, 'B': 1e9, 'T': 1e12} for suffix, multiplier in multipliers.items(): if value_str.endswith(suffix): return float(value_str[:-1].replace(',', '')) * multiplier # Regular number try: return float(value_str.replace(',', '')) except: return 0 def convert_yahoo_data(self, yahoo_data: Dict[str, Any]) -> Dict[str, Any]: """ Convert Yahoo Finance scraped data to calculator format """ stats = yahoo_data.get('statistics', {}) profile = yahoo_data.get('profile', {}) # Parse all the available data converted = { 'price': profile.get('current_price', 0), 'shares_outstanding': self.parse_yahoo_value(stats.get('shares_outstanding_5', 0)), # Income Statement (TTM) 'revenue': self.parse_yahoo_value(stats.get('revenue_(ttm)', 0)), 'gross_profit': self.parse_yahoo_value(stats.get('gross_profit_(ttm)', 0)), 'net_income': self.parse_yahoo_value(stats.get('net_income_avi_to_common_(ttm)', 0)), 'eps': self.parse_yahoo_value(stats.get('diluted_eps_(ttm)', 0)), 'ebitda': self.parse_yahoo_value(stats.get('ebitda', 0)), # Calculate COGS from revenue and gross profit 'cogs': 0, # Will calculate below # Balance Sheet (MRQ) 'cash': self.parse_yahoo_value(stats.get('total_cash_(mrq)', 0)), 'total_debt': self.parse_yahoo_value(stats.get('total_debt_(mrq)', 0)), 'shareholders_equity': 0, # Will calculate below # Cash Flow (TTM) 'operating_cash_flow': self.parse_yahoo_value(stats.get('operating_cash_flow_(ttm)', 0)), 'free_cash_flow': self.parse_yahoo_value(stats.get('levered_free_cash_flow_(ttm)', 0)), # Dividends 'dividends_per_share': self.parse_yahoo_value(stats.get('trailing_annual_dividend_rate_3', 0)), # Growth rates (already in percentage form) 'revenue_growth_yoy': self.parse_yahoo_value(stats.get('quarterly_revenue_growth_(yoy)', 0)), 'eps_growth_yoy': self.parse_yahoo_value(stats.get('quarterly_earnings_growth_(yoy)', 0)), # Ratios already calculated by Yahoo 'profit_margin': self.parse_yahoo_value(stats.get('profit_margin', 0)), 'operating_margin': self.parse_yahoo_value(stats.get('operating_margin_(ttm)', 0)), 'return_on_assets': self.parse_yahoo_value(stats.get('return_on_assets_(ttm)', 0)), 'return_on_equity': self.parse_yahoo_value(stats.get('return_on_equity_(ttm)', 0)), 'current_ratio': self.parse_yahoo_value(stats.get('current_ratio_(mrq)', 0)), 'book_value_per_share': self.parse_yahoo_value(stats.get('book_value_per_share_(mrq)', 0)), # Additional balance sheet items from Yahoo 'current_liabilities': 0, # Will be calculated from current ratio 'current_assets': 0, # Will be calculated from current ratio } # Calculate derived values revenue = converted['revenue'] gross_profit = converted['gross_profit'] converted['cogs'] = revenue - gross_profit if revenue > 0 and gross_profit > 0 else 0 # Calculate shareholders equity from book value per share shares = converted['shares_outstanding'] book_value_per_share = converted['book_value_per_share'] converted['shareholders_equity'] = book_value_per_share * shares if shares > 0 else 0 # Calculate operating income from operating margin operating_margin = converted['operating_margin'] converted['operating_income'] = revenue * operating_margin if revenue > 0 and operating_margin > 0 else 0 converted['ebit'] = converted['operating_income'] # Estimate assets and liabilities if converted['total_debt'] > 0 and converted['shareholders_equity'] > 0: converted['total_liabilities'] = converted['total_debt'] converted['total_assets'] = converted['shareholders_equity'] + converted['total_liabilities'] # Calculate current assets and liabilities from current ratio # Current Ratio = Current Assets / Current Liabilities # We know: Current Ratio and Cash # Estimate: if current ratio is available, use cash as baseline current_ratio = converted.get('current_ratio', 0) cash = converted.get('cash', 0) if current_ratio > 0 and cash > 0: # Rough estimate: assume cash is ~50% of current assets for tech companies estimated_current_assets = cash * 2 converted['current_assets'] = estimated_current_assets converted['current_liabilities'] = estimated_current_assets / current_ratio return converted def calculate_all_metrics(self, financial_data: Dict[str, Any]) -> Dict[str, Any]: """ Calculate all financial metrics from base financial data Args: financial_data: Dictionary containing: - price: Current stock price - shares_outstanding: Number of shares - income_statement: Revenue, COGS, Operating Income, Net Income, etc. - balance_sheet: Assets, Liabilities, Equity, Cash, Debt, etc. - cash_flow: Operating CF, Investing CF, Financing CF, etc. Returns: Dictionary with all calculated metrics """ metrics = {} # Extract base data price = financial_data.get('price', 0) shares = financial_data.get('shares_outstanding', 0) # Income Statement revenue = financial_data.get('revenue', 0) cogs = financial_data.get('cogs', 0) gross_profit = financial_data.get('gross_profit', revenue - cogs) operating_income = financial_data.get('operating_income', 0) net_income = financial_data.get('net_income', 0) eps = financial_data.get('eps', net_income / shares if shares > 0 else 0) ebit = financial_data.get('ebit', operating_income) ebitda = financial_data.get('ebitda', 0) interest_expense = financial_data.get('interest_expense', 0) taxes = financial_data.get('taxes', 0) # Balance Sheet total_assets = financial_data.get('total_assets', 0) current_assets = financial_data.get('current_assets', 0) total_liabilities = financial_data.get('total_liabilities', 0) current_liabilities = financial_data.get('current_liabilities', 0) total_debt = financial_data.get('total_debt', 0) long_term_debt = financial_data.get('long_term_debt', 0) shareholders_equity = financial_data.get('shareholders_equity', 0) cash = financial_data.get('cash', 0) accounts_receivable = financial_data.get('accounts_receivable', 0) inventory = financial_data.get('inventory', 0) accounts_payable = financial_data.get('accounts_payable', 0) retained_earnings = financial_data.get('retained_earnings', 0) # Cash Flow operating_cf = financial_data.get('operating_cash_flow', 0) investing_cf = financial_data.get('investing_cash_flow', 0) financing_cf = financial_data.get('financing_cash_flow', 0) capex = financial_data.get('capex', 0) free_cash_flow = financial_data.get('free_cash_flow', operating_cf - capex) # Other dividends_per_share = financial_data.get('dividends_per_share', 0) book_value_per_share = shareholders_equity / shares if shares > 0 else 0 # Calculate Market Cap and Enterprise Value market_cap = price * shares enterprise_value = market_cap + total_debt - cash # === VALUATION RATIOS === metrics['pe_ratio'] = price / eps if eps > 0 else None metrics['pb_ratio'] = price / book_value_per_share if book_value_per_share > 0 else None metrics['ps_ratio'] = market_cap / revenue if revenue > 0 else None metrics['price_to_cash_flow'] = price / (operating_cf / shares) if operating_cf > 0 and shares > 0 else None metrics['ev_ebitda'] = enterprise_value / ebitda if ebitda > 0 else None metrics['ev_ebit'] = enterprise_value / ebit if ebit > 0 else None metrics['dividend_yield'] = dividends_per_share / price if price > 0 else None metrics['price_to_fcf'] = price / (free_cash_flow / shares) if free_cash_flow > 0 and shares > 0 else None metrics['ev_to_sales'] = enterprise_value / revenue if revenue > 0 else None # PEG Ratio (requires growth rate from historical data) eps_growth = financial_data.get('eps_growth_yoy', 0) pe_ratio = metrics['pe_ratio'] metrics['peg_ratio'] = pe_ratio / (eps_growth * 100) if pe_ratio and eps_growth > 0 else None # === PROFITABILITY RATIOS === metrics['gross_margin'] = (revenue - cogs) / revenue if revenue > 0 else None metrics['operating_margin'] = operating_income / revenue if revenue > 0 else None metrics['net_margin'] = net_income / revenue if revenue > 0 else None metrics['roe'] = net_income / shareholders_equity if shareholders_equity > 0 else None metrics['roa'] = net_income / total_assets if total_assets > 0 else None metrics['roce'] = ebit / (total_assets - current_liabilities) if (total_assets - current_liabilities) > 0 else None # ROIC = NOPAT / Invested Capital tax_rate = taxes / (net_income + taxes) if (net_income + taxes) > 0 else 0.25 nopat = ebit * (1 - tax_rate) invested_capital = shareholders_equity + total_debt metrics['roic'] = nopat / invested_capital if invested_capital > 0 else None metrics['ebitda_margin'] = ebitda / revenue if revenue > 0 else None # === LEVERAGE RATIOS === metrics['debt_to_equity'] = total_liabilities / shareholders_equity if shareholders_equity > 0 else None metrics['debt_to_assets'] = total_debt / total_assets if total_assets > 0 else None metrics['interest_coverage'] = ebit / interest_expense if interest_expense > 0 else None metrics['financial_leverage'] = total_assets / shareholders_equity if shareholders_equity > 0 else None # === LIQUIDITY RATIOS === metrics['current_ratio'] = current_assets / current_liabilities if current_liabilities > 0 else None quick_assets = cash + accounts_receivable metrics['quick_ratio'] = quick_assets / current_liabilities if current_liabilities > 0 else None metrics['cash_ratio'] = cash / current_liabilities if current_liabilities > 0 else None working_capital = current_assets - current_liabilities metrics['working_capital_ratio'] = working_capital / revenue if revenue > 0 else None # === EFFICIENCY RATIOS === metrics['inventory_turnover'] = cogs / inventory if inventory > 0 else None metrics['asset_turnover'] = revenue / total_assets if total_assets > 0 else None metrics['receivables_turnover'] = revenue / accounts_receivable if accounts_receivable > 0 else None metrics['payables_turnover'] = cogs / accounts_payable if accounts_payable > 0 else None metrics['days_sales_outstanding'] = (accounts_receivable / revenue) * 365 if revenue > 0 else None metrics['days_inventory_outstanding'] = (inventory / cogs) * 365 if cogs > 0 else None metrics['days_payable_outstanding'] = (accounts_payable / cogs) * 365 if cogs > 0 else None # === GROWTH METRICS === (require historical data) metrics['revenue_growth_yoy'] = financial_data.get('revenue_growth_yoy') metrics['eps_growth_yoy'] = financial_data.get('eps_growth_yoy') metrics['net_income_growth_yoy'] = financial_data.get('net_income_growth_yoy') metrics['book_value_growth_yoy'] = financial_data.get('book_value_growth_yoy') # === CASH FLOW METRICS === metrics['fcf_yield'] = free_cash_flow / market_cap if market_cap > 0 else None metrics['operating_cf_ratio'] = operating_cf / current_liabilities if current_liabilities > 0 else None metrics['capex_ratio'] = capex / operating_cf if operating_cf > 0 else None # Add base values for reference metrics['market_cap'] = market_cap metrics['enterprise_value'] = enterprise_value metrics['shares_outstanding'] = shares metrics['book_value_per_share'] = book_value_per_share return metrics def calculate_growth_rates(self, current_data: Dict, historical_data: Dict) -> Dict[str, float]: """Calculate year-over-year growth rates""" growth_rates = {} # Revenue growth current_rev = current_data.get('revenue', 0) prev_rev = historical_data.get('revenue', 0) if prev_rev > 0: growth_rates['revenue_growth_yoy'] = (current_rev - prev_rev) / prev_rev # EPS growth current_eps = current_data.get('eps', 0) prev_eps = historical_data.get('eps', 0) if prev_eps != 0: growth_rates['eps_growth_yoy'] = (current_eps - prev_eps) / abs(prev_eps) # Net income growth current_ni = current_data.get('net_income', 0) prev_ni = historical_data.get('net_income', 0) if prev_ni != 0: growth_rates['net_income_growth_yoy'] = (current_ni - prev_ni) / abs(prev_ni) # Book value growth current_bv = current_data.get('shareholders_equity', 0) prev_bv = historical_data.get('shareholders_equity', 0) if prev_bv > 0: growth_rates['book_value_growth_yoy'] = (current_bv - prev_bv) / prev_bv return growth_rates def format_metrics_for_display(self, metrics: Dict[str, Any]) -> str: """Format metrics for human-readable display""" output = [] output.append("=" * 70) output.append("FINANCIAL METRICS") output.append("=" * 70) # Valuation Ratios output.append("\n[VALUATION RATIOS]") output.append(f" P/E Ratio: {self._format_number(metrics.get('pe_ratio'))}") output.append(f" PEG Ratio: {self._format_number(metrics.get('peg_ratio'))}") output.append(f" P/B Ratio: {self._format_number(metrics.get('pb_ratio'))}") output.append(f" P/S Ratio: {self._format_number(metrics.get('ps_ratio'))}") output.append(f" EV/EBITDA: {self._format_number(metrics.get('ev_ebitda'))}") output.append(f" Dividend Yield: {self._format_percent(metrics.get('dividend_yield'))}") # Profitability Ratios output.append("\n[PROFITABILITY RATIOS]") output.append(f" Gross Margin: {self._format_percent(metrics.get('gross_margin'))}") output.append(f" Operating Margin: {self._format_percent(metrics.get('operating_margin'))}") output.append(f" Net Margin: {self._format_percent(metrics.get('net_margin'))}") output.append(f" ROE: {self._format_percent(metrics.get('roe'))}") output.append(f" ROA: {self._format_percent(metrics.get('roa'))}") output.append(f" ROIC: {self._format_percent(metrics.get('roic'))}") # Leverage Ratios output.append("\n[LEVERAGE RATIOS]") output.append(f" Debt/Equity: {self._format_number(metrics.get('debt_to_equity'))}") output.append(f" Debt/Assets: {self._format_number(metrics.get('debt_to_assets'))}") output.append(f" Interest Coverage: {self._format_number(metrics.get('interest_coverage'))}") # Liquidity Ratios output.append("\n[LIQUIDITY RATIOS]") output.append(f" Current Ratio: {self._format_number(metrics.get('current_ratio'))}") output.append(f" Quick Ratio: {self._format_number(metrics.get('quick_ratio'))}") output.append(f" Cash Ratio: {self._format_number(metrics.get('cash_ratio'))}") # Growth Metrics output.append("\n[GROWTH METRICS (YoY)]") output.append(f" Revenue Growth: {self._format_percent(metrics.get('revenue_growth_yoy'))}") output.append(f" EPS Growth: {self._format_percent(metrics.get('eps_growth_yoy'))}") output.append(f" Net Income Growth: {self._format_percent(metrics.get('net_income_growth_yoy'))}") return "\n".join(output) def _format_number(self, value: Optional[float], decimals: int = 2) -> str: """Format number for display""" if value is None: return "N/A" return f"{value:.{decimals}f}" def _format_percent(self, value: Optional[float], decimals: int = 2) -> str: """Format percentage for display""" if value is None: return "N/A" return f"{value * 100:.{decimals}f}%" def example_usage(): """Example of how to use the calculator""" # Example financial data financial_data = { 'price': 50.00, 'shares_outstanding': 10_000_000, 'revenue': 100_000_000, 'cogs': 60_000_000, 'operating_income': 15_000_000, 'net_income': 10_000_000, 'eps': 1.00, 'ebit': 15_000_000, 'ebitda': 20_000_000, 'total_assets': 200_000_000, 'current_assets': 50_000_000, 'total_liabilities': 80_000_000, 'current_liabilities': 30_000_000, 'total_debt': 40_000_000, 'shareholders_equity': 120_000_000, 'cash': 20_000_000, 'operating_cash_flow': 18_000_000, 'capex': 5_000_000, 'free_cash_flow': 13_000_000, 'dividends_per_share': 0.50, 'eps_growth_yoy': 0.15, 'revenue_growth_yoy': 0.10 } calculator = FinancialMetricsCalculator() metrics = calculator.calculate_all_metrics(financial_data) print(calculator.format_metrics_for_display(metrics)) # Save to JSON with open('example_metrics.json', 'w') as f: json.dump(metrics, f, indent=2) if __name__ == "__main__": example_usage()