Spaces:
Running
Running
| """ | |
| 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 | |