Papaflessas's picture
Deploy Signal Generator app
3fe0726
"""
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