""" Individual metric calculation functions. Each function is self-contained and follows formulas from stock_evaluation_formulas_and_process.md """ import pandas as pd import numpy as np from typing import Dict, Optional, Tuple # ============================================================================ # 1. BUILDING BLOCKS & DATA EXTRACTION # ============================================================================ def get_diluted_shares(info: Dict, financials: Dict) -> Optional[float]: """Get diluted shares outstanding""" return info.get('sharesOutstanding') or financials.get('shares_outstanding') def get_eps_ttm(info: Dict) -> Optional[float]: """Earnings Per Share - Trailing Twelve Months""" return info.get('trailingEps') def get_eps_forward(info: Dict) -> Optional[float]: """Earnings Per Share - Forward (Next 12 months)""" return info.get('forwardEps') def get_book_value_per_share(info: Dict) -> Optional[float]: """Book Value Per Share""" return info.get('bookValue') # ============================================================================ # 2. VALUATION METRICS # ============================================================================ def calculate_market_cap(price: float, diluted_shares: float) -> Optional[float]: """ Market Capitalization Formula: MktCap = Share Price × Diluted Shares """ if type(price) in [int, float] and type(diluted_shares) in [int, float]: return price * diluted_shares return None def calculate_enterprise_value(market_cap: float, total_debt: float, cash: float, minority_interest: float = 0, preferred_stock: float = 0) -> Optional[float]: """ Enterprise Value Formula: EV = MktCap + Total Debt + Minority Interest + Preferred Stock − Cash Explanation: Total value of firm's operating assets available to all capital providers """ if type(market_cap) in [int, float] and type(total_debt) in [int, float] and \ type(cash) in [int, float] and type(minority_interest) in [int, float] and \ type(preferred_stock) in [int, float]: return market_cap + total_debt + minority_interest + preferred_stock - cash return None def calculate_pe_ratio(price: float, eps: float) -> Optional[float]: """ Price-to-Earnings Ratio Formula: P/E = Share Price / EPS Explanation: How many dollars investors pay per dollar of earnings """ if type(price) in [int, float] and type(eps) in [int, float]: if eps and eps > 0: return price / eps return None def calculate_peg_ratio(pe_ratio: float, eps_growth_rate: float) -> Optional[float]: """ PEG Ratio (Price/Earnings-to-Growth) Formula: PEG = P/E / (Expected EPS annual growth rate in %) Explanation: Normalizes PE by growth. PEG ≈ 1 implies valuation equals growth Threshold: PEG < 1 suggests value, PEG > 1.5 suggests expensive """ if type(pe_ratio) in [int, float] and type(eps_growth_rate) in [int, float]: if pe_ratio and eps_growth_rate and eps_growth_rate > 0: return pe_ratio / (eps_growth_rate * 100) return None def calculate_ev_ebitda(enterprise_value: float, ebitda: float) -> Optional[float]: """ EV/EBITDA Multiple Formula: EV/EBITDA = Enterprise Value / EBITDA Explanation: Capital-structure neutral valuation, useful for comparing companies with different leverage """ if type(enterprise_value) in [int, float] and type(ebitda) in [int, float]: if ebitda and ebitda > 0: return enterprise_value / ebitda return None def calculate_price_to_fcf(market_cap: float, free_cash_flow: float) -> Optional[float]: """ Price to Free Cash Flow Formula: P/FCF = Market Cap / Free Cash Flow Explanation: How many dollars investors pay per dollar of free cash flow """ if type(market_cap) in [int, float] and type(free_cash_flow) in [int, float]: if free_cash_flow and free_cash_flow > 0: return market_cap / free_cash_flow return None def calculate_fcf_yield_equity(free_cash_flow: float, market_cap: float) -> Optional[float]: """ Free Cash Flow Yield (Equity basis) Formula: FCF yield = Free Cash Flow / Market Cap Explanation: Cash return on equity investment Threshold: > 6% = attractive, < 3% = expensive Priority: HIGHEST - measures actual cash generation """ if type(free_cash_flow) in [int, float] and type(market_cap) in [int, float]: if market_cap and market_cap > 0: return (free_cash_flow / market_cap) * 100 # Return as percentage return None def calculate_fcf_yield_enterprise(free_cash_flow: float, enterprise_value: float) -> Optional[float]: """ Free Cash Flow Yield (Enterprise basis) Formula: FCF yield = Free Cash Flow / EV Explanation: Cash return on enterprise value (preferred for comparing leveraged vs unleveraged firms) Threshold: > 6% = Buy candidate, 4-6% = Hold, < 3% = Unattractive """ if type(free_cash_flow) in [int, float] and type(enterprise_value) in [int, float]: if enterprise_value and enterprise_value > 0: return (free_cash_flow / enterprise_value) * 100 # Return as percentage return None def calculate_price_to_book(price: float, book_value_per_share: float) -> Optional[float]: """ Price-to-Book Ratio Formula: P/B = Price / Book Value per Share Explanation: Valuation relative to net asset value """ if type(price) in [int, float] and type(book_value_per_share) in [int, float]: if book_value_per_share and book_value_per_share > 0: return price / book_value_per_share return None # ============================================================================ # 3. PROFITABILITY & MARGIN METRICS # ============================================================================ def calculate_gross_margin(revenue: float, cogs: float) -> Optional[float]: """ Gross Margin Formula: Gross margin = (Revenue − COGS) / Revenue Explanation: Unit economics - whether price > cost. Rising gross margins imply pricing power or scale """ if type(revenue) in [int, float] and type(cogs) in [int, float]: if revenue and revenue > 0: return ((revenue - cogs) / revenue) * 100 return None def calculate_ebitda_margin(ebitda: float, revenue: float) -> Optional[float]: """ EBITDA Margin Formula: EBITDA margin = EBITDA / Revenue Explanation: Operational cash profitability before capital structure and non-cash charges """ if type(ebitda) in [int, float] and type(revenue) in [int, float]: if revenue and revenue > 0: return (ebitda / revenue) * 100 return None def calculate_ebit_margin(ebit: float, revenue: float) -> Optional[float]: """ EBIT (Operating) Margin Formula: EBIT margin = EBIT / Revenue Explanation: Includes depreciation/amortization; useful for capital-intensive businesses """ if type(ebit) in [int, float] and type(revenue) in [int, float]: if revenue and revenue > 0: return (ebit / revenue) * 100 return None def calculate_net_margin(net_income: float, revenue: float) -> Optional[float]: """ Net Margin Formula: Net margin = Net Income / Revenue Explanation: True bottom-line profitability after interest and tax """ if type(net_income) in [int, float] and type(revenue) in [int, float]: if revenue and revenue > 0: return (net_income / revenue) * 100 return None # ============================================================================ # 4. CASH FLOW METRICS # ============================================================================ def calculate_free_cash_flow(cash_from_operations: float, capex: float) -> Optional[float]: """ Free Cash Flow Formula: FCF = Cash from Operations − Capital Expenditures Explanation: Actual cash business generates after reinvestment. This is the ULTIMATE value driver. """ if type(cash_from_operations) in [int, float] and type(capex) in [int, float]: return cash_from_operations - abs(capex) # CapEx is usually negative return None def calculate_fcf_per_share(free_cash_flow: float, diluted_shares: float) -> Optional[float]: """ Free Cash Flow per Share Formula: FCF per share = Free Cash Flow / Diluted Shares Explanation: Per-share cash generation, adjusted for dilution """ if type(free_cash_flow) in [int, float] and type(diluted_shares) in [int, float]: if diluted_shares and diluted_shares > 0: return free_cash_flow / diluted_shares return None def calculate_cash_conversion(cfo: float, net_income: float) -> Optional[float]: """ Cash Conversion Ratio Formula: Cash Conversion = CFO / Net Income Explanation: Measures quality of earnings. Should be > 1.0 RED FLAG if consistently < 1.0 (profits not converting to cash) """ if type(cfo) in [int, float] and type(net_income) in [int, float]: if net_income and net_income != 0: return cfo / net_income return None # ============================================================================ # 5. LIQUIDITY & SOLVENCY METRICS # ============================================================================ def calculate_current_ratio(current_assets: float, current_liabilities: float) -> Optional[float]: """ Current Ratio Formula: Current ratio = Current Assets / Current Liabilities Explanation: Basic short-term liquidity. > 1 is typical """ if type(current_assets) in [int, float] and type(current_liabilities) in [int, float]: if current_liabilities and current_liabilities > 0: return current_assets / current_liabilities return None def calculate_quick_ratio(cash: float, marketable_securities: float, receivables: float, current_liabilities: float) -> Optional[float]: """ Quick Ratio (Acid Test) Formula: Quick ratio = (Cash + Marketable Securities + Receivables) / Current Liabilities Explanation: Stricter liquidity measure excluding inventory """ if type(cash) in [int, float] and type(marketable_securities) in [int, float] and \ type(receivables) in [int, float] and type(current_liabilities) in [int, float]: if current_liabilities and current_liabilities > 0: return (cash + marketable_securities + receivables) / current_liabilities return None def calculate_net_debt_to_ebitda(total_debt: float, cash: float, ebitda: float) -> Optional[float]: """ Net Debt to EBITDA Formula: Net debt / EBITDA = (Total Debt − Cash) / EBITDA Explanation: Years of EBITDA required to pay net debt Thresholds: - < 1.0: Low risk - 1-3: Moderate - > 3: High risk (alert if > 4-5) """ if type(total_debt) in [int, float] and type(cash) in [int, float] and type(ebitda) in [int, float]: if ebitda and ebitda > 0: net_debt = total_debt - cash return net_debt / ebitda return None def calculate_interest_coverage(ebit: float, interest_expense: float) -> Optional[float]: """ Interest Coverage Ratio Formula: Interest coverage = EBIT / Interest Expense Explanation: Ability to pay interest from operating earnings Threshold: > 3x is safe, < 2x is risky """ if type(ebit) in [int, float] and type(interest_expense) in [int, float]: if interest_expense and interest_expense > 0: return ebit / interest_expense return None def calculate_debt_to_equity(total_debt: float, total_equity: float) -> Optional[float]: """ Debt-to-Equity Ratio Formula: Debt-to-Equity = Total Debt / Total Equity Explanation: Capital structure leverage """ if type(total_debt) in [int, float] and type(total_equity) in [int, float]: if total_equity and total_equity > 0: return total_debt / total_equity return None # ============================================================================ # 6. RETURN & EFFICIENCY METRICS # ============================================================================ def calculate_roe(net_income: float, avg_shareholders_equity: float) -> Optional[float]: """ Return on Equity Formula: ROE = Net Income / Average Shareholders' Equity Explanation: Returns delivered to equity holders Threshold: > 15% is good, > 20% is excellent Priority: HIGH - quality indicator """ if type(net_income) in [int, float] and type(avg_shareholders_equity) in [int, float]: if avg_shareholders_equity and avg_shareholders_equity > 0: return (net_income / avg_shareholders_equity) * 100 return None def calculate_roa(net_income: float, total_assets: float) -> Optional[float]: """ Return on Assets Formula: ROA = Net Income / Total Assets Explanation: Asset utilization efficiency """ if type(net_income) in [int, float] and type(total_assets) in [int, float]: if total_assets and total_assets > 0: return (net_income / total_assets) * 100 return None def calculate_roic(ebit: float, tax_rate: float, invested_capital: float) -> Optional[float]: """ Return on Invested Capital Formula: NOPAT = EBIT × (1 − Tax Rate) ROIC = NOPAT / Invested Capital Where: Invested Capital = Total Equity + Total Debt - Cash Explanation: Operating return on all capital (equity + debt) Thresholds: - > 10%: Good - > 15%: Excellent (high-quality business) - Trending up: High-return projects Priority: VERY HIGH - best measure of capital efficiency """ if type(ebit) in [int, float] and type(tax_rate) in [int, float] and type(invested_capital) in [int, float]: if invested_capital and invested_capital > 0: nopat = ebit * (1 - tax_rate) return (nopat / invested_capital) * 100 return None def calculate_invested_capital(total_equity: float, total_debt: float, cash: float) -> Optional[float]: """ Invested Capital Formula: Invested Capital = Total Equity + Total Debt - Cash Explanation: Total capital deployed in operations """ if type(total_equity) in [int, float] and type(total_debt) in [int, float] and type(cash) in [int, float]: return total_equity + total_debt - cash return None # ============================================================================ # 7. GROWTH METRICS # ============================================================================ def calculate_revenue_growth(current_revenue: float, prior_revenue: float) -> Optional[float]: """ Revenue Growth Rate (YoY) Formula: Revenue Growth = (Current Revenue - Prior Revenue) / Prior Revenue Explanation: Top-line growth momentum """ if type(current_revenue) in [int, float] and type(prior_revenue) in [int, float]: if prior_revenue and prior_revenue > 0: return ((current_revenue - prior_revenue) / prior_revenue) * 100 return None def calculate_eps_growth(current_eps: float, prior_eps: float) -> Optional[float]: """ EPS Growth Rate (YoY) Formula: EPS Growth = (Current EPS - Prior EPS) / Prior EPS Explanation: Bottom-line growth momentum """ if type(current_eps) in [int, float] and type(prior_eps) in [int, float]: if prior_eps and prior_eps > 0: return ((current_eps - prior_eps) / prior_eps) * 100 return None def calculate_cagr(ending_value: float, beginning_value: float, years: int) -> Optional[float]: """ Compound Annual Growth Rate Formula: CAGR = (Ending Value / Beginning Value)^(1/years) - 1 Explanation: Smoothed annual growth rate over period """ if type(ending_value) in [int, float] and type(beginning_value) in [int, float] and type(years) in [int, float]: if beginning_value and beginning_value > 0 and years > 0: return (((ending_value / beginning_value) ** (1 / years)) - 1) * 100 return None def calculate_sustainable_growth(roe: float, payout_ratio: float) -> Optional[float]: """ Sustainable EPS Growth Formula: Sustainable Growth = ROE × (1 - Payout Ratio) Or: Sustainable Growth = ROE × Reinvestment Rate Explanation: Theoretical growth rate based on returns and reinvestment """ if type(roe) in [int, float] and type(payout_ratio) in [int, float]: reinvestment_rate = 1 - payout_ratio return roe * reinvestment_rate return None # ============================================================================ # 8. CAPITAL ALLOCATION METRICS # ============================================================================ def calculate_payout_ratio(dividends: float, net_income: float) -> Optional[float]: """ Payout Ratio Formula: Payout Ratio = Dividends / Net Income Explanation: Proportion of earnings paid as dividends """ if type(dividends) in [int, float] and type(net_income) in [int, float]: if net_income and net_income > 0: return (dividends / net_income) * 100 return None def calculate_buyback_yield(buyback_cash: float, market_cap: float) -> Optional[float]: """ Buyback Yield Formula: Buyback Yield = Cash spent on repurchases / Market Cap Explanation: Return to shareholders via buybacks """ if type(buyback_cash) in [int, float] and type(market_cap) in [int, float]: if market_cap and market_cap > 0: return (buyback_cash / market_cap) * 100 return None def calculate_dilution_rate(current_shares: float, prior_shares: float) -> Optional[float]: """ Dilution Rate Formula: Dilution = (Current Shares - Prior Shares) / Prior Shares Explanation: Share count increase (dilution) or decrease (buybacks) Negative value = buybacks, Positive value = dilution """ if type(current_shares) in [int, float] and type(prior_shares) in [int, float]: if prior_shares and prior_shares > 0: return ((current_shares - prior_shares) / prior_shares) * 100 return None def calculate_total_payout_ratio(dividends: float, buybacks: float, net_income: float) -> Optional[float]: """ Total Payout Ratio (including buybacks) Formula: Total Payout = (Dividends + Buybacks) / Net Income Explanation: Total cash returned to shareholders """ if type(dividends) in [int, float] and type(buybacks) in [int, float] and type(net_income) in [int, float]: if net_income and net_income > 0: return ((dividends + buybacks) / net_income) * 100 return None # ============================================================================ # 9. EFFICIENCY METRICS # ============================================================================ def calculate_asset_turnover(revenue: float, total_assets: float) -> Optional[float]: """ Asset Turnover Formula: Asset Turnover = Revenue / Total Assets Explanation: How efficiently assets generate revenue """ if type(revenue) in [int, float] and type(total_assets) in [int, float]: if total_assets and total_assets > 0: return revenue / total_assets return None def calculate_inventory_turnover(cogs: float, avg_inventory: float) -> Optional[float]: """ Inventory Turnover Formula: Inventory Turnover = COGS / Average Inventory Explanation: How quickly inventory is sold """ if type(cogs) in [int, float] and type(avg_inventory) in [int, float]: if avg_inventory and avg_inventory > 0: return cogs / avg_inventory return None def calculate_receivables_turnover(revenue: float, avg_receivables: float) -> Optional[float]: """ Receivables Turnover Formula: Receivables Turnover = Revenue / Average Receivables Explanation: How quickly receivables are collected """ if type(revenue) in [int, float] and type(avg_receivables) in [int, float]: if avg_receivables and avg_receivables > 0: return revenue / avg_receivables return None # ============================================================================ # 10. WORKING CAPITAL METRICS # ============================================================================ def calculate_working_capital(current_assets: float, current_liabilities: float) -> Optional[float]: """ Working Capital Formula: Working Capital = Current Assets - Current Liabilities Explanation: Short-term operational liquidity """ if type(current_assets) in [int, float] and type(current_liabilities) in [int, float]: return current_assets - current_liabilities return None def calculate_working_capital_ratio(working_capital: float, revenue: float) -> Optional[float]: """ Working Capital to Revenue Formula: WC Ratio = Working Capital / Revenue Explanation: Capital tied up in operations """ if type(working_capital) in [int, float] and type(revenue) in [int, float]: if revenue and revenue > 0: return (working_capital / revenue) * 100 return None # ============================================================================ # HELPER FUNCTIONS # ============================================================================ def safe_divide(numerator: float, denominator: float, default: Optional[float] = None) -> Optional[float]: """Safely divide two numbers, returning default if denominator is 0""" if type(numerator) in [int, float] and type(denominator) in [int, float]: if denominator and denominator != 0: return numerator / denominator return default def calculate_average(value1: float, value2: float) -> Optional[float]: """Calculate average of two values""" if type(value1) in [int, float] and type(value2) in [int, float]: return (value1 + value2) / 2 return None def annualize_quarterly(quarterly_value: float) -> Optional[float]: """Convert quarterly value to annual (TTM)""" if type(quarterly_value) in [int, float]: return quarterly_value * 4 return None