Fundamental Analysis (wraquant.fundamental)

The fundamental module provides 28 functions for financial ratio analysis, intrinsic valuation, financial statement analysis, and stock screening – the building blocks of fundamental-driven quant strategies.

Four areas of coverage:

  1. Financial ratios – Profitability, liquidity, leverage, efficiency, valuation, and growth ratios. Includes 3-way and 5-way DuPont decomposition and a convenience function that computes all ratios at once.

  2. Valuation models – DCF, relative valuation, Graham Number, Peter Lynch fair value, Dividend Discount Model, Residual Income Model, Piotroski F-Score, and margin of safety analysis.

  3. Financial statement analysis – Income, balance sheet, and cash flow trend analysis. Composite financial health scoring (0–100 with letter grades), earnings quality assessment (accruals analysis), and common-size statements.

  4. Stock screening – Pre-built screens for value, growth, quality, Piotroski F-Score, and Greenblatt’s Magic Formula strategies, plus a flexible custom screener.

All functions accept a ticker symbol and optionally an fmp_client parameter to reuse a single FMP provider instance. Requires the market-data extra and an FMP_API_KEY environment variable.

Quick Example

from wraquant.fundamental import (
    profitability_ratios,
    dcf_valuation,
    financial_health_score,
    value_screen,
)

# Profitability ratios for Apple
prof = profitability_ratios("AAPL")
print(f"ROE:        {prof['roe']:.2%}")
print(f"ROIC:       {prof['roic']:.2%}")
print(f"Net margin: {prof['net_margin']:.2%}")

# DCF intrinsic value
dcf = dcf_valuation("AAPL")
print(f"Intrinsic value: ${dcf['intrinsic_value_per_share']:.2f}")
print(f"Current price:   ${dcf['current_price']:.2f}")
print(f"Margin of safety: {dcf['margin_of_safety']:.1%}")

# Financial health composite score (0-100)
health = financial_health_score("AAPL")
print(f"Score: {health['score']:.0f}/100 ({health['grade']})")

# Screen for value stocks
stocks = value_screen(max_pe=15, min_dividend_yield=0.03)
print(f"Found {len(stocks)} value stocks")

DuPont Decomposition

from wraquant.fundamental import dupont_decomposition

# 5-way DuPont: decompose ROE into five drivers
dupont = dupont_decomposition("MSFT", method="5-way")
print(f"Tax burden:       {dupont['tax_burden']:.4f}")
print(f"Interest burden:  {dupont['interest_burden']:.4f}")
print(f"Operating margin: {dupont['operating_margin']:.4f}")
print(f"Asset turnover:   {dupont['asset_turnover']:.4f}")
print(f"Equity multiplier:{dupont['equity_multiplier']:.4f}")
print(f"ROE (product):    {dupont['roe']:.4f}")

Valuation Models

from wraquant.fundamental import (
    graham_number,
    dividend_discount_model,
    relative_valuation,
    piotroski_f_score,
)

# Graham Number: conservative intrinsic value
gn = graham_number("JNJ")
print(f"Graham Number: ${gn['graham_number']:.2f}")

# Dividend Discount Model (Gordon growth)
ddm = dividend_discount_model("KO", cost_of_equity=0.08)
print(f"DDM value: ${ddm['fair_value']:.2f}")

# Relative valuation vs. peer group
rv = relative_valuation("AAPL", peers=["MSFT", "GOOGL", "META"])
print(f"P/E relative: {rv['pe_relative']:.2f}x")

# Piotroski F-Score (0-9, higher = better)
f_score = piotroski_f_score("AAPL")
print(f"F-Score: {f_score['score']}/9")

Stock Screening

from wraquant.fundamental import (
    value_screen,
    growth_screen,
    quality_factor_screen,
    piotroski_screen,
    magic_formula_screen,
    custom_screen,
)

# Pre-built screens
value_stocks = value_screen(max_pe=12, min_dividend_yield=0.04)
growth_stocks = growth_screen(min_revenue_growth=0.20)
quality_stocks = quality_factor_screen(min_roe=0.20, max_debt_equity=0.5)

# Piotroski: filter by F-Score >= 7
strong = piotroski_screen(min_score=7)

# Magic Formula: rank by ROIC + earnings yield
magic = magic_formula_screen(top_n=30)

# Custom: arbitrary criteria
custom = custom_screen(criteria={
    "pe_ratio": {"max": 15},
    "roe": {"min": 0.15},
    "market_cap": {"min": 1e9},
})

See also

API Reference

Fundamental analysis for quantitative finance.

Provides tools for computing financial ratios, valuation models, financial statement analysis, and stock screening – the building blocks of fundamental-driven quant strategies.

This module covers four areas:

  1. Financial ratios (ratios submodule) – Profitability, liquidity, leverage, efficiency, valuation, and growth ratios. Includes DuPont decomposition (3-way and 5-way) and a convenience function that aggregates all ratios.

  2. Valuation models (valuation submodule) – DCF valuation, relative valuation, Graham Number, Peter Lynch fair value, Dividend Discount Model, Residual Income Model, and margin of safety. Also retains the Piotroski F-Score and DataFrame quality screen.

  3. Financial statement analysis (financials submodule) – Income statement, balance sheet, and cash flow trend analysis. Composite financial health scoring (0–100), earnings quality assessment, and common-size statements.

  4. Stock screening (screening submodule) – Pre-built screens for value, growth, and quality investing. Classic strategies: Piotroski F-Score screen and Greenblatt’s Magic Formula. Flexible custom screening with arbitrary criteria.

All FMP-based functions accept an optional fmp_client parameter to reuse a single provider instance across calls.

Example

>>> from wraquant.fundamental import profitability_ratios, dcf_valuation
>>> prof = profitability_ratios("AAPL")
>>> print(f"ROE: {prof['roe']:.2%}")
>>> dcf = dcf_valuation("AAPL")
>>> print(f"Fair value: ${dcf['intrinsic_value_per_share']:.2f}")

References

  • Graham & Dodd (1934), “Security Analysis”

  • Piotroski (2000), “Value Investing: The Use of Historical Financial Statement Information to Separate Winners from Losers”

  • Greenblatt (2006), “The Little Book That Beats the Market”

  • Damodaran (2012), “Investment Valuation”, 3rd edition

profitability_ratios(symbol, *, period='annual', fmp_client=None)[source]

Compute core profitability ratios for a company.

Profitability ratios measure how effectively a company converts revenue into profit at various stages of the income statement. Use these to compare operating efficiency across peers and to track margin trends over time.

Mathematical formulations:

ROE = Net Income / Shareholders’ Equity ROA = Net Income / Total Assets ROIC = NOPAT / Invested Capital

= EBIT * (1 - tax rate) / (Total Debt + Equity - Cash)

Gross Margin = Gross Profit / Revenue Operating Margin = Operating Income / Revenue Net Margin = Net Income / Revenue

Parameters:
  • symbol (str) – Ticker symbol (e.g., "AAPL").

  • period (str, default: 'annual') – "annual" or "quarter". Annual data is more stable; quarterly reveals recent trends.

  • fmp_client (Any | None, default: None) – Optional pre-configured FMPClient instance. If None, a default client is created.

Returns:

  • roe (float) – Return on equity. > 0.15 is generally strong.

  • roa (float) – Return on assets. > 0.05 is solid.

  • roic (float) – Return on invested capital. > WACC means the company creates value.

  • gross_margin (float) – Gross profit / revenue.

  • operating_margin (float) – Operating income / revenue.

  • net_margin (float) – Net income / revenue.

  • period (str) – The period used.

Return type:

dict[str, float]

Example

>>> from wraquant.fundamental.ratios import profitability_ratios
>>> p = profitability_ratios("MSFT")
>>> print(f"ROE: {p['roe']:.2%}, ROIC: {p['roic']:.2%}")

See also

dupont_decomposition: Breaks ROE into its drivers. efficiency_ratios: Asset utilisation metrics.

liquidity_ratios(symbol, *, period='annual', fmp_client=None)[source]

Compute short-term liquidity ratios.

Liquidity ratios assess a company’s ability to meet its short-term obligations. They are critical for credit analysis, bankruptcy prediction (Altman Z-Score), and the Piotroski F-Score.

Mathematical formulations:

Current Ratio = Current Assets / Current Liabilities Quick Ratio = (Current Assets - Inventory) / Current Liabilities Cash Ratio = Cash & Equivalents / Current Liabilities Working Capital = Current Assets - Current Liabilities

Parameters:
  • symbol (str) – Ticker symbol.

  • period (str, default: 'annual') – "annual" or "quarter".

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Returns:

  • current_ratio (float) – > 1.5 is healthy; < 1.0 is a red flag.

  • quick_ratio (float) – Acid-test; excludes illiquid inventory.

  • cash_ratio (float) – Most conservative; cash only.

  • working_capital (float) – Absolute dollar liquidity buffer.

  • period (str) – The period used.

Return type:

dict[str, float]

Example

>>> from wraquant.fundamental.ratios import liquidity_ratios
>>> liq = liquidity_ratios("AAPL")
>>> print(f"Current ratio: {liq['current_ratio']:.2f}")

References

Altman, E. I. (1968). “Financial Ratios, Discriminant Analysis and the Prediction of Corporate Bankruptcy.” Journal of Finance, 23(4), 589–609.

See also

leverage_ratios: Long-term solvency.

leverage_ratios(symbol, *, period='annual', fmp_client=None)[source]

Compute leverage and solvency ratios.

Leverage ratios measure the extent to which a company uses debt to finance its assets. Higher leverage amplifies both returns and risk. These ratios are essential for: - Credit risk modeling (probability of default). - Merton structural models (distance to default). - Factor investing (leverage as a risk factor).

Mathematical formulations:

Debt-to-Equity = Total Debt / Shareholders’ Equity Debt Ratio = Total Debt / Total Assets Interest Coverage = EBIT / Interest Expense Equity Multiplier = Total Assets / Shareholders’ Equity Debt-to-EBITDA = Total Debt / EBITDA

Parameters:
  • symbol (str) – Ticker symbol.

  • period (str, default: 'annual') – "annual" or "quarter".

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Returns:

  • debt_to_equity (float) – D/E ratio; > 2.0 is high leverage.

  • debt_ratio (float) – Portion of assets financed by debt.

  • interest_coverage (float) – EBIT / interest; < 1.5 is distressed. Higher is safer.

  • equity_multiplier (float) – Assets per dollar of equity; captures leverage in DuPont.

  • debt_to_ebitda (float) – How many years of EBITDA to repay debt; < 3 is conservative.

  • period (str) – The period used.

Return type:

dict[str, float]

Example

>>> from wraquant.fundamental.ratios import leverage_ratios
>>> lev = leverage_ratios("AAPL")
>>> print(f"D/E: {lev['debt_to_equity']:.2f}")

See also

liquidity_ratios: Short-term solvency. dupont_decomposition: How leverage drives ROE.

efficiency_ratios(symbol, *, period='annual', fmp_client=None)[source]

Compute asset utilisation and efficiency ratios.

Efficiency ratios (also called activity ratios) measure how effectively a company uses its assets to generate revenue. They are the turnover component in DuPont analysis and are critical for comparing capital-light vs. capital-intensive businesses.

Mathematical formulations:

Asset Turnover = Revenue / Total Assets Inventory Turnover = COGS / Average Inventory Receivable Turnover = Revenue / Accounts Receivable Payable Turnover = COGS / Accounts Payable Days Sales Outstanding = 365 / Receivable Turnover Days Inventory Outstanding = 365 / Inventory Turnover Days Payable Outstanding = 365 / Payable Turnover Cash Conversion Cycle = DSO + DIO - DPO

Parameters:
  • symbol (str) – Ticker symbol.

  • period (str, default: 'annual') – "annual" or "quarter".

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Returns:

  • asset_turnover (float) – Revenue per dollar of assets.

  • inventory_turnover (float) – How many times inventory is sold per period. Higher is better for retail.

  • receivable_turnover (float) – How quickly receivables convert to cash.

  • payable_turnover (float) – How quickly the company pays suppliers.

  • days_sales_outstanding (float) – Average collection period in days.

  • days_inventory_outstanding (float) – Average days to sell inventory.

  • days_payable_outstanding (float) – Average days to pay suppliers.

  • cash_conversion_cycle (float) – DSO + DIO - DPO; shorter is better. Negative means the company is funded by suppliers (e.g., Amazon).

  • period (str) – The period used.

Return type:

dict[str, float]

Example

>>> from wraquant.fundamental.ratios import efficiency_ratios
>>> eff = efficiency_ratios("WMT")
>>> print(f"Inventory turnover: {eff['inventory_turnover']:.1f}x")
>>> print(f"Cash conversion cycle: {eff['cash_conversion_cycle']:.0f} days")

See also

dupont_decomposition: Efficiency is a component of ROE.

valuation_ratios(symbol, *, fmp_client=None)[source]

Compute market-based valuation multiples.

Valuation ratios relate the market price to fundamentals. They are the workhorses of relative valuation and the value factor in Fama-French models. Use them to compare a stock to its sector median, its own history, or the broad market.

Mathematical formulations:

P/E = Price / Earnings Per Share P/B = Price / Book Value Per Share P/S = Price / Revenue Per Share EV/EBITDA = Enterprise Value / EBITDA PEG = P/E / EPS Growth Rate (%) Div Yield = Dividend Per Share / Price

Parameters:
  • symbol (str) – Ticker symbol.

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Returns:

  • pe_ratio (float) – Price-to-earnings. Market median ~15–20.

  • pb_ratio (float) – Price-to-book.

  • ps_ratio (float) – Price-to-sales. Useful for unprofitable companies.

  • ev_to_ebitda (float) – Enterprise value / EBITDA. < 10 is often considered cheap.

  • peg_ratio (float) – Growth-adjusted P/E. < 1 may be undervalued relative to growth.

  • dividend_yield (float) – Annual dividend / price.

  • earnings_yield (float) – Inverse of P/E; comparable to bond yields.

  • price_to_fcf (float) – Price / free cash flow per share.

Return type:

dict[str, float]

Example

>>> from wraquant.fundamental.ratios import valuation_ratios
>>> val = valuation_ratios("AAPL")
>>> print(f"P/E: {val['pe_ratio']:.1f}, EV/EBITDA: {val['ev_to_ebitda']:.1f}")

References

Fama, E. F. & French, K. R. (1992). “The Cross-Section of Expected Stock Returns.” Journal of Finance, 47(2), 427–465.

See also

relative_valuation: Compare multiples to peers.

growth_ratios(symbol, *, period='annual', fmp_client=None)[source]

Compute revenue, earnings, and dividend growth rates.

Growth ratios quantify the trajectory of a company’s top line, bottom line, and shareholder distributions. They are core inputs to the PEG ratio, DCF terminal growth assumptions, and growth factor construction.

Mathematical formulations:

Revenue Growth = (Revenue_t - Revenue_{t-1}) / Revenue_{t-1} EPS Growth = (EPS_t - EPS_{t-1}) / EPS_{t-1} Dividend Growth = (DPS_t - DPS_{t-1}) / DPS_{t-1} EBITDA Growth = (EBITDA_t - EBITDA_{t-1}) / EBITDA_{t-1} FCF Growth = (FCF_t - FCF_{t-1}) / FCF_{t-1}

Parameters:
  • symbol (str) – Ticker symbol.

  • period (str, default: 'annual') – "annual" or "quarter". Annual is less noisy; quarterly captures recent momentum.

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Returns:

  • revenue_growth (float) – Most recent period YoY revenue growth.

  • eps_growth (float) – Most recent period YoY EPS growth.

  • dividend_growth (float) – Most recent period YoY dividend growth.

  • ebitda_growth (float) – Most recent period YoY EBITDA growth.

  • fcf_growth (float) – Most recent period YoY FCF growth.

  • revenue_growth_3y (float) – 3-year CAGR of revenue.

  • revenue_growth_5y (float) – 5-year CAGR of revenue.

  • revenue_growth_history (list[float]) – Growth for each available period.

  • period (str) – The period used.

Return type:

dict[str, float | list[float]]

Example

>>> from wraquant.fundamental.ratios import growth_ratios
>>> g = growth_ratios("NVDA")
>>> print(f"Revenue growth: {g['revenue_growth']:.1%}")
>>> print(f"3Y CAGR: {g['revenue_growth_3y']:.1%}")

See also

valuation_ratios: PEG uses growth.

dupont_decomposition(symbol, *, period='annual', fmp_client=None)[source]

Perform 3-way and 5-way DuPont decomposition of ROE.

DuPont analysis decomposes return on equity into its fundamental drivers, revealing why a company’s ROE is high or low. This is essential for distinguishing between companies that earn high ROE through operational excellence vs. financial leverage.

Mathematical formulations:

3-way DuPont:
ROE = Net Margin x Asset Turnover x Equity Multiplier

= (NI / Rev) x (Rev / Assets) x (Assets / Equity)

5-way DuPont (extended):
ROE = Tax Burden x Interest Burden x Operating Margin

x Asset Turnover x Equity Multiplier

= (NI / EBT) x (EBT / EBIT) x (EBIT / Rev)

x (Rev / Assets) x (Assets / Equity)

Parameters:
  • symbol (str) – Ticker symbol.

  • period (str, default: 'annual') – "annual" or "quarter".

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Returns:

3-way components: - net_margin (float) – NI / Revenue. - asset_turnover (float) – Revenue / Total Assets. - equity_multiplier (float) – Total Assets / Equity. - roe_3way (float) – Product of the three components.

5-way components: - tax_burden (float) – NI / EBT. Closer to 1.0 means lower

effective tax rate.

  • interest_burden (float) – EBT / EBIT. Closer to 1.0 means less interest cost relative to operating profit.

  • operating_margin (float) – EBIT / Revenue.

  • roe_5way (float) – Product of the five components.

  • period (str) – The period used.

Return type:

dict[str, float]

Example

>>> from wraquant.fundamental.ratios import dupont_decomposition
>>> dp = dupont_decomposition("AAPL")
>>> print(f"ROE (3-way): {dp['roe_3way']:.2%}")
>>> print(f"  = {dp['net_margin']:.2%} margin")
>>> print(f"  x {dp['asset_turnover']:.2f} turnover")
>>> print(f"  x {dp['equity_multiplier']:.2f} leverage")

References

Soliman, M. T. (2008). “The Use of DuPont Analysis by Market Participants.” The Accounting Review, 83(3), 823–853.

See also

profitability_ratios: Individual profitability metrics. leverage_ratios: Detailed leverage analysis.

comprehensive_ratios(symbol, *, period='annual', fmp_client=None)[source]

Compute all financial ratios in a single call.

Convenience function that aggregates profitability, liquidity, leverage, efficiency, valuation, growth, and DuPont ratios into a single nested dictionary. Useful for building screening dashboards and factor databases.

Parameters:
  • symbol (str) – Ticker symbol.

  • period (str, default: 'annual') – "annual" or "quarter" (used for non-valuation ratios; valuation ratios always use TTM).

  • fmp_client (Any | None, default: None) – Optional FMPClient instance. Passing one avoids creating multiple clients.

Returns:

Return type:

dict[str, Any]

Example

>>> from wraquant.fundamental.ratios import comprehensive_ratios
>>> all_ratios = comprehensive_ratios("AAPL")
>>> print(f"ROE: {all_ratios['profitability']['roe']:.2%}")
>>> print(f"D/E: {all_ratios['leverage']['debt_to_equity']:.2f}")
>>> print(f"P/E: {all_ratios['valuation']['pe_ratio']:.1f}")

See also

financial_health_score: Composite score from these ratios.

ratio_comparison(symbol, *, peers=None, fmp_client=None)[source]

Compare a stock’s key ratios to its peer group with percentile ranking.

Relative ratio analysis is the backbone of peer-group valuation and factor-based stock selection. Rather than asking “is this ratio good in absolute terms?”, this function asks “how does it rank against comparable companies?” A 20 % ROE might be outstanding in utilities but mediocre in tech.

When to use:
  • Building peer-relative factor scores for multi-factor models.

  • Identifying companies that are outliers within their industry.

  • Due-diligence: confirming whether a stock’s ratios are truly exceptional or merely in-line with its sector.

Mathematical formulation:

Percentile_i = (# peers with ratio <= target_ratio_i) / N_peers

A percentile of 0.80 means the stock ranks in the 80th percentile among its peers on that metric.

Parameters:
  • symbol (str) – Ticker symbol (e.g., "AAPL").

  • peers (list[str] | None, default: None) – List of peer ticker symbols. If None, peers are auto-discovered via FMP’s stock_peers() endpoint, which returns companies in the same industry.

  • fmp_client (Any | None, default: None) – Optional pre-configured FMPClient instance.

Returns:

  • symbol (str) – The target ticker.

  • peers (list[str]) – Peers used for comparison.

  • target_ratios (dict) – The target’s key ratios.

  • peer_averages (dict) – Mean of each ratio across peers.

  • peer_medians (dict) – Median of each ratio across peers.

  • percentile_rank (dict) – Percentile rank (0–1) of the target vs. peers for each ratio. Higher is better for profitability/efficiency; lower is better for leverage.

  • peers_detail (list[dict]) – Individual peer ratios.

Return type:

dict[str, Any]

Example

>>> from wraquant.fundamental.ratios import ratio_comparison
>>> comp = ratio_comparison("AAPL")
>>> print(f"ROE percentile: {comp['percentile_rank']['roe']:.0%}")
>>> print(f"D/E percentile: {comp['percentile_rank']['debt_to_equity']:.0%}")

See also

comprehensive_ratios: All ratios for a single company. sector_comparison: Compare against sector-wide averages.

Analyse multi-year ratio trends to identify improving or deteriorating fundamentals.

Static ratios show where a company is now; trends show where it is going. A company with a 12 % ROE that has risen from 8 % is far more attractive than one at 15 % declining from 20 %. This function computes trend direction and magnitude for every major ratio category.

When to use:
  • Momentum-quality strategies: buy stocks with improving fundamentals (rising margins, declining leverage).

  • Turnaround detection: identify companies transitioning from weak to strong.

  • Risk monitoring: catch early signs of fundamental deterioration in existing holdings.

Mathematical formulation:

Trend direction = sign(ratio_latest - ratio_oldest) Trend magnitude = (ratio_latest - ratio_oldest) / |ratio_oldest| Trend is classified as “improving”, “deteriorating”, or “stable” based on a ±5% threshold.

Parameters:
  • symbol (str) – Ticker symbol (e.g., "AAPL").

  • periods (int, default: 5) – Number of annual periods to analyse. Default 5 gives a full business-cycle view. Use 3 for recent trajectory.

  • fmp_client (Any | None, default: None) – Optional pre-configured FMPClient instance.

Returns:

  • symbol (str) – The ticker analysed.

  • periods_analysed (int) – Actual number of periods with data.

  • roe (dict) – {"values": [...], "direction": str, "magnitude": float}. Direction is "improving", "deteriorating", or "stable".

  • roa (dict) – Same structure as ROE.

  • gross_margin (dict) – Margin trajectory.

  • operating_margin (dict) – Operating efficiency trend.

  • net_margin (dict) – Bottom-line margin trend.

  • debt_to_equity (dict) – Leverage trend. “improving” means leverage is decreasing.

  • current_ratio (dict) – Liquidity trend.

  • asset_turnover (dict) – Efficiency trend.

  • summary (str) – Overall assessment: "fundamentals improving", "fundamentals deteriorating", or "fundamentals mixed".

Return type:

dict[str, Any]

Example

>>> from wraquant.fundamental.ratios import ratio_trends
>>> trends = ratio_trends("MSFT", periods=5)
>>> print(f"ROE trend: {trends['roe']['direction']}")
>>> print(f"Summary: {trends['summary']}")

See also

ratio_comparison: Cross-sectional peer comparison. comprehensive_ratios: Point-in-time ratio snapshot.

sector_comparison(symbol, *, fmp_client=None)[source]

Compare a stock’s ratios to its sector and industry averages.

While ratio_comparison() benchmarks against specific peers, this function compares against the broader sector using FMP sector PE data and the company’s own profile metadata. This is useful for answering: “Is this stock cheap for its sector?” and “Does this company’s profitability justify a sector-premium valuation?”

When to use:
  • Sector rotation: compare company metrics to sector norms.

  • Relative valuation: determine if a premium/discount to sector is warranted by superior/inferior fundamentals.

  • Factor construction: normalise ratios by sector before ranking.

Parameters:
  • symbol (str) – Ticker symbol (e.g., "AAPL").

  • fmp_client (Any | None, default: None) – Optional pre-configured FMPClient instance.

Returns:

  • symbol (str) – The target ticker.

  • sector (str) – The company’s GICS sector.

  • industry (str) – The company’s industry classification.

  • company_ratios (dict) – The company’s key ratios.

  • sector_pe (float) – Average P/E for the sector.

  • company_pe (float) – The company’s P/E.

  • pe_premium_to_sector (float) – (company_PE - sector_PE) / sector_PE. Positive = premium; negative = discount.

  • sector_performance (float) – Current-day sector performance (% change).

  • assessment (str) – "premium to sector", "discount to sector", or "in-line with sector".

Return type:

dict[str, Any]

Example

>>> from wraquant.fundamental.ratios import sector_comparison
>>> sc = sector_comparison("AAPL")
>>> print(f"Sector: {sc['sector']}")
>>> print(f"PE premium: {sc['pe_premium_to_sector']:+.1%}")
>>> print(f"Assessment: {sc['assessment']}")

See also

ratio_comparison: Peer-level comparison with percentile ranking. relative_valuation: Full multi-metric peer comparison.

dcf_valuation(symbol, *, growth_rate=None, discount_rate=None, terminal_growth=0.025, projection_years=5, fmp_client=None)[source]

Discounted cash flow valuation using FMP financial data.

Estimates intrinsic value by projecting free cash flows forward and discounting them to the present. If growth_rate or discount_rate are not provided, they are estimated from historical data and the company’s cost structure.

When to use:
  • Absolute valuation when you have a view on future growth.

  • Sensitivity analysis: vary growth/discount to bound fair value.

  • Combine with margin_of_safety() for investment decisions.

Mathematical formulation:
PV = sum_{t=1}^{N} FCF_0 * (1+g)^t / (1+r)^t
  • [FCF_N * (1+g_term) / (r - g_term)] / (1+r)^N

Intrinsic value per share = PV / shares outstanding

Parameters:
  • symbol (str) – Ticker symbol.

  • growth_rate (float | None, default: None) – Projected annual FCF growth rate. If None, estimated from the 3-year historical FCF CAGR. Typical range: 0.03–0.20.

  • discount_rate (float | None, default: None) – Weighted average cost of capital (WACC). If None, estimated as 10% (common equity assumption). Typical range: 0.08–0.12.

  • terminal_growth (float, default: 0.025) – Perpetual growth rate for terminal value. Must be < discount_rate. Use GDP growth rate (~2–3%) as an upper bound.

  • projection_years (int, default: 5) – Number of years to project FCF. Typical: 5–10.

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Returns:

  • intrinsic_value (float) – Total intrinsic enterprise value.

  • intrinsic_value_per_share (float) – Per-share fair value.

  • current_price (float) – Current market price.

  • margin_of_safety (float) – (intrinsic - price) / intrinsic. Positive means undervalued.

  • upside_potential (float) – (intrinsic - price) / price.

  • pv_cash_flows (float) – PV of projected FCFs.

  • pv_terminal (float) – PV of terminal value.

  • terminal_value (float) – Undiscounted terminal value.

  • terminal_pct (float) – Terminal value as % of total PV. > 75% means the valuation is highly sensitive to terminal assumptions.

  • projected_fcf (list[float]) – Year-by-year projected FCFs.

  • assumptions (dict) – Growth rate, discount rate, terminal growth used.

  • fmp_dcf (float) – FMP’s own DCF estimate for comparison.

Return type:

dict[str, Any]

Raises:

ValueError – If discount_rate <= terminal_growth.

Example

>>> from wraquant.fundamental.valuation import dcf_valuation
>>> dcf = dcf_valuation("MSFT", growth_rate=0.12, discount_rate=0.10)
>>> print(f"Fair value: ${dcf['intrinsic_value_per_share']:.2f}")
>>> print(f"Margin of safety: {dcf['margin_of_safety']:.1%}")

References

Damodaran, A. (2012). Investment Valuation, 3rd ed., Chapter 12.

See also

relative_valuation: Peer-based valuation. margin_of_safety: Stand-alone margin computation.

relative_valuation(symbol, *, peers=None, fmp_client=None)[source]

Compare valuation multiples against a peer group.

Relative valuation assumes that similar companies should trade at similar multiples. Deviations suggest over- or under-pricing relative to the peer set.

When to use:
  • When absolute valuation (DCF) is too uncertain.

  • Sector rotation strategies: buy cheap sectors, sell expensive.

  • Pair trading: long the cheap peer, short the expensive one.

Parameters:
  • symbol (str) – Target ticker symbol.

  • peers (list[str] | None, default: None) – List of peer ticker symbols. If None, the function uses FMP’s sector peers (same industry/sector). Provide explicit peers for more meaningful comparisons.

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Returns:

  • symbol (str) – The target ticker.

  • multiples (dict) – Target’s P/E, P/B, P/S, EV/EBITDA.

  • peer_medians (dict) – Median multiples of the peer group.

  • peer_means (dict) – Mean multiples of the peer group.

  • premium_discount (dict) – For each multiple, the % premium (+) or discount (-) vs. peer median. Negative = cheaper than peers.

  • peers_data (list[dict]) – Individual peer multiples.

  • verdict (str) – Summary: “undervalued”, “fairly valued”, or “overvalued” based on median premium/discount.

Return type:

dict[str, Any]

Example

>>> from wraquant.fundamental.valuation import relative_valuation
>>> rv = relative_valuation("AAPL", peers=["MSFT", "GOOG", "META"])
>>> print(f"P/E premium: {rv['premium_discount']['pe_ratio']:+.1%}")
>>> print(f"Verdict: {rv['verdict']}")

See also

dcf_valuation: Absolute valuation approach. valuation_ratios: Single-stock multiples.

graham_number(symbol, *, fmp_client=None)[source]

Compute Ben Graham’s intrinsic value number.

The Graham Number is a conservative estimate of the maximum price a defensive investor should pay. It assumes a stock should not trade above P/E of 15 and P/B of 1.5 simultaneously.

When to use:
  • Deep value screening for defensive investors.

  • Quick sanity check on valuation.

  • Pair with Piotroski F-Score: high F-Score + price < Graham Number is a classic value strategy.

Mathematical formulation:

Graham Number = sqrt(22.5 * EPS * BVPS)

where 22.5 = 15 (max P/E) * 1.5 (max P/B)

Parameters:
  • symbol (str) – Ticker symbol.

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Returns:

  • graham_number (float) – Intrinsic value estimate. Only meaningful when EPS > 0 and BVPS > 0.

  • current_price (float) – Current market price.

  • margin_of_safety (float) – (graham - price) / graham.

  • eps (float) – Earnings per share used.

  • bvps (float) – Book value per share used.

Return type:

dict[str, float]

Example

>>> from wraquant.fundamental.valuation import graham_number
>>> gn = graham_number("JNJ")
>>> print(f"Graham Number: ${gn['graham_number']:.2f}")
>>> print(f"Current price: ${gn['current_price']:.2f}")

References

Graham, B. (1973). The Intelligent Investor, Revised ed., Chapter 14.

See also

peter_lynch_value: Growth-oriented fair value. dcf_valuation: More sophisticated intrinsic value.

peter_lynch_value(symbol, *, fmp_client=None)[source]

Compute fair value using Peter Lynch’s PEG-based methodology.

Peter Lynch argued that a fairly valued growth stock should have a P/E ratio roughly equal to its earnings growth rate. PEG < 1 suggests undervaluation; PEG > 2 suggests overvaluation.

When to use:
  • Growth stock screening.

  • Quick check on whether you’re overpaying for growth.

  • Combine with growth_ratios() for context on growth sustainability.

Mathematical formulation:

PEG = P/E / (EPS Growth Rate * 100) Fair Value = EPS * EPS Growth Rate * 100

Parameters:
  • symbol (str) – Ticker symbol.

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Returns:

  • fair_value (float) – Lynch fair value per share.

  • current_price (float) – Current market price.

  • peg_ratio (float) – PEG ratio.

  • pe_ratio (float) – Current P/E.

  • eps_growth_rate (float) – EPS growth rate used (decimal).

  • margin_of_safety (float) – (fair - price) / fair.

  • lynch_category (str) – “undervalued” (PEG < 1), “fairly valued” (1–2), or “overvalued” (> 2).

Return type:

dict[str, float | str]

Example

>>> from wraquant.fundamental.valuation import peter_lynch_value
>>> plv = peter_lynch_value("NVDA")
>>> print(f"PEG: {plv['peg_ratio']:.2f}")
>>> print(f"Lynch category: {plv['lynch_category']}")

References

Lynch, P. (1989). One Up on Wall Street. Simon & Schuster.

See also

graham_number: Conservative value approach. valuation_ratios: Raw multiples.

dividend_discount_model(symbol, *, required_return=0.1, fmp_client=None)[source]

Gordon Growth Model (single-stage DDM) valuation.

Values a stock as the present value of all future dividends growing at a constant rate in perpetuity. Only suitable for mature, stable-dividend companies (utilities, consumer staples, REITs).

When to use:
  • Value dividend aristocrats and other stable payers.

  • Income-focused portfolio construction.

  • Not suitable for non-dividend or high-growth companies.

Mathematical formulation:

V_0 = D_1 / (r - g)

where: D_1 = next year’s expected dividend = D_0 * (1 + g) r = required return (cost of equity) g = dividend growth rate (must be < r)

Parameters:
  • symbol (str) – Ticker symbol.

  • required_return (float, default: 0.1) – Required rate of return / cost of equity. Typical range: 0.08–0.12. Use CAPM or build-up method to estimate.

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Returns:

  • intrinsic_value (float) – DDM fair value per share.

  • current_price (float) – Current market price.

  • margin_of_safety (float) – (intrinsic - price) / intrinsic.

  • dividend_per_share (float) – Current annual DPS.

  • dividend_growth_rate (float) – Estimated growth rate.

  • dividend_yield (float) – Current dividend yield.

  • implied_return (float) – Yield + growth (implied total return at current price).

  • model_applicable (str) – “yes” if the company pays dividends and growth < required return; “no” otherwise.

Return type:

dict[str, float | str]

Example

>>> from wraquant.fundamental.valuation import dividend_discount_model
>>> ddm = dividend_discount_model("KO", required_return=0.09)
>>> print(f"DDM value: ${ddm['intrinsic_value']:.2f}")
>>> print(f"Implied return: {ddm['implied_return']:.2%}")

References

Gordon, M. J. (1959). “Dividends, Earnings, and Stock Prices.” Review of Economics and Statistics, 41(2), 99–105.

See also

dcf_valuation: FCF-based valuation (works for non-payers). residual_income_model: Book-value-based alternative.

residual_income_model(symbol, *, cost_of_equity=0.1, projection_years=5, fade_rate=0.2, fmp_client=None)[source]

Residual income (abnormal earnings) valuation model.

Values a stock as its book value plus the present value of future residual income (earnings in excess of the cost of equity). Unlike DCF, this model anchors on book value and is less sensitive to terminal value assumptions.

When to use:
  • Companies with stable book values (financials, industrials).

  • When terminal value dominates DCF (RIM reduces this problem).

  • Academic factor research: book value is the anchor.

Mathematical formulation:

V_0 = BV_0 + sum_{t=1}^{T} RI_t / (1 + r_e)^t + TV

RI_t = NI_t - r_e * BV_{t-1} (residual income)

TV = RI_T * (1 - fade) / (r_e - g_ri * (1 - fade))

Parameters:
  • symbol (str) – Ticker symbol.

  • cost_of_equity (float, default: 0.1) – Required return on equity. Use CAPM: r_e = r_f + beta * (r_m - r_f). Typical: 0.08–0.14.

  • projection_years (int, default: 5) – Years of explicit RI projection.

  • fade_rate (float, default: 0.2) – Annual rate at which residual income fades toward zero. Higher fade = more conservative. 0.0 = no fade (perpetual excess returns). 0.20 = industry reversion.

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Returns:

  • intrinsic_value (float) – RIM fair value per share.

  • current_price (float) – Current market price.

  • margin_of_safety (float) – (intrinsic - price) / intrinsic.

  • book_value_per_share (float) – Current BVPS.

  • current_roe (float) – Current ROE.

  • residual_income (float) – Most recent period RI.

  • pv_residual_income (float) – PV of projected RI stream.

  • pv_terminal (float) – PV of terminal RI.

  • excess_return_spread (float) – ROE - cost of equity. Positive means the company creates value.

Return type:

dict[str, Any]

Example

>>> from wraquant.fundamental.valuation import residual_income_model
>>> rim = residual_income_model("JPM", cost_of_equity=0.11)
>>> print(f"RIM value: ${rim['intrinsic_value']:.2f}")
>>> print(f"Excess spread: {rim['excess_return_spread']:.2%}")

References

Ohlson, J. A. (1995). “Earnings, Book Values, and Dividends in Equity Valuation.” Contemporary Accounting Research, 11(2), 661–687.

See also

dcf_valuation: Cash-flow-based alternative. graham_number: Simpler book-value-based approach.

margin_of_safety(symbol=None, intrinsic_value=0.0, current_price=0.0, *, fmp_client=None)[source]

Compute the margin of safety between intrinsic value and market price.

The margin of safety is the central concept of value investing. Graham recommended buying only when the market price is significantly below intrinsic value to protect against estimation errors.

Mathematical formulation:

Margin of Safety = (Intrinsic Value - Market Price) / Intrinsic Value

When to use:
  • After computing intrinsic value via DCF, Graham Number, etc.

  • Graham recommended a minimum 33% margin of safety.

  • Negative margin means the stock trades above estimated fair value.

Parameters:
  • symbol (str | None, default: None) – Optional ticker symbol. If provided with no current_price, the current market price is fetched.

  • intrinsic_value (float, default: 0.0) – Your estimated fair value per share.

  • current_price (float, default: 0.0) – Current market price. If 0.0 and symbol is provided, fetched from FMP.

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Return type:

float

Returns:

Margin of safety as a float (e.g., 0.30 = 30% discount to intrinsic value). Negative means premium to intrinsic.

Example

>>> from wraquant.fundamental.valuation import margin_of_safety
>>> mos = margin_of_safety(intrinsic_value=150.0, current_price=100.0)
>>> print(f"Margin of safety: {mos:.1%}")
Margin of safety: 33.3%

See also

dcf_valuation: Compute intrinsic value. graham_number: Conservative intrinsic value.

piotroski_f_score(financials)[source]

Compute the Piotroski F-Score (0–9) for financial health.

The Piotroski F-Score is a composite score of nine binary tests that evaluate profitability, leverage/liquidity, and operating efficiency. Stocks scoring 8–9 are considered financially strong; scores of 0–2 indicate financial distress.

When to use:
  • Screen value stocks (low P/B) for financial health.

  • Avoid value traps: low P/B stocks with low F-Scores tend to underperform.

  • Long/short strategy: long high F-Score value stocks, short low F-Score value stocks.

The nine binary tests:

Profitability (4 points):
  1. ROA > 0 (net_income / total_assets > 0)

  2. Operating cash flow > 0

  3. ROA increased vs. prior year

  4. Cash flow from operations > net income (accruals quality)

Leverage & liquidity (3 points):
  1. Long-term debt decreased vs. prior year

  2. Current ratio increased vs. prior year

  3. No new shares issued (shares outstanding unchanged or decreased)

Operating efficiency (2 points):
  1. Gross margin increased vs. prior year

  2. Asset turnover increased vs. prior year

Parameters:

financials (dict[str, float]) –

Dictionary with the following keys:

  • net_income: Current year net income.

  • prev_net_income: Prior year net income.

  • operating_cash_flow: Current year operating cash flow.

  • total_assets: Current year total assets.

  • prev_total_assets: Prior year total assets.

  • long_term_debt: Current year long-term debt.

  • prev_long_term_debt: Prior year long-term debt.

  • current_ratio: Current year current ratio.

  • prev_current_ratio: Prior year current ratio.

  • shares_outstanding: Current year shares outstanding.

  • prev_shares_outstanding: Prior year shares outstanding.

  • gross_margin: Current year gross margin.

  • prev_gross_margin: Prior year gross margin.

  • asset_turnover: Current year asset turnover.

  • prev_asset_turnover: Prior year asset turnover.

Return type:

int

Returns:

Integer score from 0 to 9.

Example

>>> financials = {
...     "net_income": 1e6, "prev_net_income": 8e5,
...     "operating_cash_flow": 1.2e6, "total_assets": 5e6,
...     "prev_total_assets": 4.8e6, "long_term_debt": 1e6,
...     "prev_long_term_debt": 1.1e6, "current_ratio": 1.5,
...     "prev_current_ratio": 1.3, "shares_outstanding": 1e6,
...     "prev_shares_outstanding": 1e6, "gross_margin": 0.4,
...     "prev_gross_margin": 0.38, "asset_turnover": 0.8,
...     "prev_asset_turnover": 0.75,
... }
>>> piotroski_f_score(financials)
9

References

Piotroski, J. D. (2000). “Value Investing: The Use of Historical Financial Statement Information to Separate Winners from Losers.” Journal of Accounting Research, 38, 1–41.

See also

dcf_valuation: Intrinsic value estimation. financial_health_score: FMP-powered composite score.

quality_screen(stocks_df, metrics=None)[source]

Rank stocks by a composite quality score.

Computes a composite quality score by ranking each stock on multiple fundamental metrics and averaging the percentile ranks. Higher composite scores indicate higher quality.

When to use:
  • Construct a quality factor for multi-factor models.

  • Screen a universe for high-quality long candidates.

  • Complement value screening (avoid value traps by requiring quality).

Parameters:
  • stocks_df (Any) – DataFrame where each row is a stock and columns contain fundamental metrics. Missing values are handled by assigning median rank.

  • metrics (list[str] | None, default: None) – List of column names to include in the composite. If None, defaults to ["roe", "operating_margin", "current_ratio"] (using only columns that exist in the DataFrame).

Return type:

Any

Returns:

DataFrame with the original data plus a quality_score column (0 to 1, higher is better) and a quality_rank column (1 = best), sorted by quality_score descending.

Example

>>> import pandas as pd
>>> stocks = pd.DataFrame({
...     "ticker": ["AAPL", "MSFT", "GOOG"],
...     "roe": [0.25, 0.30, 0.20],
...     "operating_margin": [0.30, 0.35, 0.25],
...     "current_ratio": [1.5, 2.0, 3.0],
... }).set_index("ticker")
>>> result = quality_screen(stocks)
>>> result["quality_rank"].iloc[0]
1

See also

piotroski_f_score: Financial health assessment (single stock). custom_screen: Flexible screening with arbitrary criteria.

income_analysis(symbol, period='annual', *, fmp_client=None)[source]

Analyse income statement trends over multiple periods.

Goes beyond single-period ratios to reveal the trajectory of revenue, margins, and bottom-line profitability. Use this when you need to understand whether a company’s earning power is structurally improving, temporarily inflated, or in secular decline. A company with a 20 % margin that is declining is very different from one with a 15 % margin that is expanding.

This function is the starting point for fundamental stock analysis: it answers “is the business growing?” and “are margins expanding or compressing?”

Parameters:
  • symbol (str) – Ticker symbol (e.g., "AAPL").

  • period (str, default: 'annual') – "annual" or "quarter". Annual data smooths out seasonality; quarterly reveals recent momentum.

  • fmp_client (Any | None, default: None) – Optional pre-configured FMPClient instance. If None, a default client is created using the FMP_API_KEY environment variable.

Returns:

Time-series data (most recent first): - revenue (list[float]) – Revenue by period. - revenue_growth (list[float]) – YoY revenue growth rates. - gross_margin (list[float]) – Gross profit / revenue per period. - operating_margin (list[float]) – Operating income / revenue. - net_margin (list[float]) – Net income / revenue. - ebitda_margin (list[float]) – EBITDA / revenue. - eps (list[float]) – Diluted EPS by period. - dates (list[str]) – Reporting period dates.

Trend analysis: - margin_trend (str) – "expanding", "contracting",

or "stable" based on operating margin trajectory.

Growth rates: - revenue_cagr_3y (float) – 3-year revenue CAGR. > 10 %

is strong organic growth; negative signals secular decline.

  • revenue_cagr_5y (float) – 5-year revenue CAGR.

Metadata: - periods_analysed (int) – Number of periods returned.

Return type:

dict[str, Any]

Example

>>> from wraquant.fundamental.financials import income_analysis
>>> inc = income_analysis("MSFT")
>>> print(f"Revenue CAGR (3Y): {inc['revenue_cagr_3y']:.1%}")
>>> print(f"Margin trend: {inc['margin_trend']}")

See also

balance_sheet_analysis: Asset/liability composition trends. cash_flow_analysis: Cash flow quality and FCF trends. common_size_analysis: Line items as % of revenue.

balance_sheet_analysis(symbol, period='annual', *, fmp_client=None)[source]

Analyse balance sheet composition, leverage, and capital structure.

Reveals the structure of assets (tangible vs. intangible, current vs. long-term), the financing mix (debt vs. equity), and how these have evolved. Use this for:

  • Credit analysis: Is the company over-leveraged? Is the debt- to-equity ratio trending upward?

  • Equity screening: Is book value growing? What fraction of assets is goodwill from acquisitions?

  • Factor investing: Value (P/B), investment (asset growth).

  • Distress prediction: Working capital and liquidity trends.

Parameters:
  • symbol (str) – Ticker symbol (e.g., "AAPL").

  • period (str, default: 'annual') – "annual" or "quarter".

  • fmp_client (Any | None, default: None) – Optional pre-configured FMPClient instance. If None, a default client is created.

Returns:

Time-series data (most recent first): - total_assets (list[float]) – Total assets by period. - total_equity (list[float]) – Stockholders’ equity by period. - total_debt (list[float]) – Total debt by period. - cash (list[float]) – Cash & equivalents by period. - net_debt (list[float]) – Debt minus cash by period.

Negative means the company has more cash than debt.

  • debt_to_equity (list[float]) – D/E ratio by period. > 2.0 is high leverage for most industries.

  • debt_to_assets (list[float]) – Debt ratio by period.

  • current_ratio (list[float]) – Current ratio by period. < 1.0 is a liquidity warning.

  • equity_pct (list[float]) – Equity as % of total assets.

  • intangible_pct (list[float]) – (Intangibles + goodwill) / total assets. > 50 % means most “assets” are goodwill from acquisitions – a risk in downturns.

  • book_value_per_share (list[float]) – BVPS by period.

  • tangible_bvps (list[float]) – BVPS excluding intangibles.

  • dates (list[str]) – Period end dates.

Trend analysis: - leverage_trend (str) – "increasing", "decreasing",

or "stable" based on D/E ratio trajectory.

Return type:

dict[str, Any]

Example

>>> from wraquant.fundamental.financials import balance_sheet_analysis
>>> bs = balance_sheet_analysis("AAPL")
>>> print(f"Net debt: ${bs['net_debt'][0]:,.0f}")
>>> print(f"Leverage trend: {bs['leverage_trend']}")

See also

income_analysis: Revenue and margin trends. cash_flow_analysis: Cash flow quality and FCF trends. financial_health_score: Composite assessment.

cash_flow_analysis(symbol, period='annual', *, fmp_client=None)[source]

Analyse cash flow statement trends and free cash flow quality.

Cash flow analysis reveals whether reported earnings are backed by real cash generation. A company can report growing profits while hemorrhaging cash – this analysis catches that.

The key metric is free cash flow (FCF) = operating cash flow minus capital expenditures. FCF is what is actually available for dividends, buybacks, debt reduction, and reinvestment.

Use this function to: - Verify that reported earnings translate into actual cash. - Assess CapEx requirements and how much free cash flow remains. - Track whether the company is self-funding or reliant on external

capital.

  • Compare cash returned to shareholders vs. cash generated.

Parameters:
  • symbol (str) – Ticker symbol (e.g., "AAPL").

  • period (str, default: 'annual') – "annual" or "quarter".

  • fmp_client (Any | None, default: None) – Optional pre-configured FMPClient instance. If None, a default client is created.

Returns:

Time-series data (most recent first): - operating_cash_flow (list[float]) – OCF by period.

Should consistently exceed net income for healthy companies.

  • capital_expenditures (list[float]) – CapEx by period (negative = spending).

  • free_cash_flow (list[float]) – FCF by period.

  • fcf_margin (list[float]) – FCF / revenue by period. > 10 % is strong; indicates each revenue dollar generates substantial free cash.

  • fcf_growth (list[float]) – YoY FCF growth rates.

  • cash_conversion (list[float]) – OCF / net income. > 1.0 means cash earnings exceed accounting earnings – a sign of high earnings quality.

  • capex_to_revenue (list[float]) – |CapEx| / revenue. > 15 % indicates capital-intensive business.

  • capex_to_ocf (list[float]) – |CapEx| / OCF. > 50 % means heavy reinvestment requirements.

  • dividends_paid (list[float]) – Absolute dividends paid.

  • buybacks (list[float]) – Absolute share repurchases.

  • total_shareholder_return (list[float]) – Dividends + buybacks.

  • fcf_payout_ratio (list[float]) – (Dividends + buybacks) / FCF. > 1.0 means the company is returning more than it generates.

  • dates (list[str]) – Period end dates.

Point estimates: - fcf_yield (float) – FCF / market cap (most recent).

> 5 % is typically attractive for value investors.

Return type:

dict[str, Any]

Example

>>> from wraquant.fundamental.financials import cash_flow_analysis
>>> cf = cash_flow_analysis("MSFT")
>>> print(f"FCF margin: {cf['fcf_margin'][0]:.1%}")
>>> print(f"Cash conversion: {cf['cash_conversion'][0]:.2f}x")

References

Sloan, R. G. (1996). “Do Stock Prices Fully Reflect Information in Accruals and Cash Flows about Future Earnings?” The Accounting Review, 71(3), 289–315.

See also

earnings_quality: Detailed accruals analysis. income_analysis: Margin trends for context.

financial_health_score(symbol, *, fmp_client=None)[source]

Compute a composite financial health score (0–100) with letter grade.

Aggregates profitability, liquidity, leverage, efficiency, and cash flow quality into a single score. This is a modernised, continuous version of the binary Piotroski F-Score – it captures how much better or worse a metric is, not just whether it passes a threshold.

Use this for: - Screening universes: Filter out financially distressed companies

before building factor portfolios.

  • Risk management: Flag holdings whose fundamentals are deteriorating.

  • Quick triage: Rapidly assess health before deep-diving into individual statements.

The score is computed as a weighted average of five sub-scores:
  1. Profitability (30 pts): ROE, ROA, operating margin, net margin.

  2. Liquidity (15 pts): Current ratio, quick ratio.

  3. Leverage (20 pts): D/E ratio, interest coverage.

  4. Efficiency (15 pts): Asset turnover, positive NI.

  5. Cash flow quality (20 pts): FCF margin, cash conversion.

Grading scale:

A (80–100 “excellent”), B (60–79 “good”), C (40–59 “fair”), D (20–39 “weak”), F (0–19 “critical”).

Parameters:
  • symbol (str) – Ticker symbol (e.g., "AAPL").

  • fmp_client (Any | None, default: None) – Optional pre-configured FMPClient instance. If None, a default client is created.

Returns:

  • total_score (float) – Composite score 0–100.

  • grade (str) – Letter grade: "A" through "F".

  • category (str) – "excellent", "good", "fair", "weak", or "critical".

  • profitability_score (float) – Sub-score out of 30.

  • liquidity_score (float) – Sub-score out of 15.

  • leverage_score (float) – Sub-score out of 20.

  • efficiency_score (float) – Sub-score out of 15.

  • cash_flow_score (float) – Sub-score out of 20.

  • strengths (list[str]) – Top-performing areas.

  • weaknesses (list[str]) – Areas of concern.

  • piotroski_f_score (int) – Traditional F-Score (0–9) for reference.

  • symbol (str) – The ticker analysed.

Return type:

dict[str, Any]

Example

>>> from wraquant.fundamental.financials import financial_health_score
>>> health = financial_health_score("MSFT")
>>> print(f"Score: {health['total_score']:.0f}/100 ({health['grade']})")
>>> print(f"Strengths: {', '.join(health['strengths'])}")

See also

earnings_quality: Deep dive into earnings reliability. comprehensive_ratios: All ratios in one call.

earnings_quality(symbol, *, fmp_client=None)[source]

Assess the quality and sustainability of reported earnings.

High-quality earnings are cash-backed, persistent, and free of accounting manipulation. Low-quality earnings are driven by accruals, non-recurring items, or aggressive accounting.

This function computes multiple earnings quality metrics from the academic literature on earnings management and accruals. Use it as a quality filter in stock selection: prefer companies with low accruals and high cash conversion.

Key metrics:

  • Accruals ratio: Total accruals / average total assets. High accruals (> 10 % of assets) predict future earnings reversals (Sloan, 1996). This is the single most powerful quality signal.

  • Cash conversion: OCF / net income. Should be > 1.0 for healthy companies. Consistently < 0.7 is a red flag.

  • Earnings persistence: Autocorrelation of earnings across periods. High persistence (> 0.7) = sustainable earnings.

Mathematical formulations:

Accruals = Net Income - Operating Cash Flow Accruals Ratio = Accruals / Average Total Assets Cash Conversion Ratio = Operating CF / Net Income FCF to Net Income = Free Cash Flow / Net Income

Parameters:
  • symbol (str) – Ticker symbol (e.g., "AAPL").

  • fmp_client (Any | None, default: None) – Optional pre-configured FMPClient instance. If None, a default client is created.

Returns:

  • accruals_ratio (float) – Accruals / avg total assets. < 5 % is high quality; > 10 % is a red flag.

  • cash_conversion_ratio (float) – OCF / net income. > 1.0 means earnings are backed by cash.

  • earnings_persistence (float) – Autocorrelation of NI. > 0.7 = stable; < 0.3 = volatile.

  • fcf_to_net_income (float) – FCF / net income. > 0.8 is strong; < 0.5 means heavy CapEx eats into earnings.

  • quality_grade (str) – "A" (excellent) through "F" (manipulated/unreliable).

  • accruals_trend (list[float]) – Accruals ratio history (most recent first).

  • red_flags (list[str]) – Specific concerns identified.

  • periods_analysed (int) – Number of periods used.

  • symbol (str) – The ticker analysed.

Return type:

dict[str, Any]

Example

>>> from wraquant.fundamental.financials import earnings_quality
>>> eq = earnings_quality("AAPL")
>>> print(f"Quality grade: {eq['quality_grade']}")
>>> print(f"Accruals ratio: {eq['accruals_ratio']:.2%}")
>>> if eq['red_flags']:
...     print(f"Warnings: {', '.join(eq['red_flags'])}")

Notes

Reference: Sloan, R. G. (1996). “Do Stock Prices Fully Reflect Information in Accruals and Cash Flows about Future Earnings?” The Accounting Review, 71(3), 289–315.

See also

cash_flow_analysis: Detailed cash flow trends. financial_health_score: Composite score.

common_size_analysis(symbol, period='annual', *, fmp_client=None)[source]

Generate a common-size DataFrame combining income and balance sheet.

Common-size analysis expresses each line item as a percentage of a base figure: revenue for the income statement, total assets for the balance sheet. This normalisation makes it easy to:

  • Compare companies of different sizes on an apples-to-apples basis.

  • Track composition changes over time (e.g., is R&D spending growing as a share of revenue?).

  • Benchmark against sector medians to spot outliers.

When to use:

Use common-size analysis as input to peer-relative valuation and industry benchmarking. It is also a prerequisite for detecting structural shifts in cost structure or asset mix across reporting periods.

Parameters:
  • symbol (str) – Ticker symbol (e.g., "AAPL").

  • period (str, default: 'annual') – "annual" or "quarter".

  • fmp_client (Any | None, default: None) – Optional pre-configured FMPClient instance. If None, a default client is created.

Return type:

DataFrame

Returns:

DataFrame with one row per reporting period and columns for each line item expressed as a ratio (0–1 scale):

Income statement (% of revenue): - date (str) – Reporting period date. - cost_of_revenue_pct (float) – COGS / revenue. - gross_profit_pct (float) – Gross profit / revenue. - rd_pct (float) – R&D expense / revenue. - sga_pct (float) – SG&A expense / revenue. - operating_income_pct (float) – Operating income / revenue. - interest_expense_pct (float) – |Interest| / revenue. - income_tax_pct (float) – Income tax / revenue. - net_income_pct (float) – Net income / revenue. - ebitda_pct (float) – EBITDA / revenue.

Balance sheet (% of total assets): - current_assets_pct (float) – Current assets / total assets. - cash_pct (float) – Cash / total assets. - receivables_pct (float) – Net receivables / total assets. - inventory_pct (float) – Inventory / total assets. - fixed_assets_pct (float) – PP&E / total assets. - intangibles_pct (float) – Intangible assets / total assets. - goodwill_pct (float) – Goodwill / total assets. - current_liabilities_pct (float) – Current liabilities /

total assets.

  • long_term_debt_pct (float) – Long-term debt / total assets.

  • total_debt_pct (float) – Total debt / total assets.

  • equity_pct (float) – Equity / total assets.

  • retained_earnings_pct (float) – Retained earnings / total assets.

Example

>>> from wraquant.fundamental.financials import common_size_analysis
>>> cs = common_size_analysis("AAPL")
>>> print(cs[["date", "gross_profit_pct", "rd_pct",
...           "operating_income_pct"]].head())

See also

income_analysis: Absolute values and growth rates. balance_sheet_analysis: Composition and leverage trends.

revenue_decomposition(symbol, *, fmp_client=None)[source]

Break down revenue by product segment and geographic region.

Understanding where revenue comes from is essential for assessing concentration risk, growth drivers, and geographic diversification. A company with 80 % of revenue from one product is far riskier than one with balanced segment mix. Similarly, heavy geographic concentration creates FX and geopolitical risk.

When to use:
  • Identify revenue concentration risk (single product/region).

  • Assess growth drivers: which segments are accelerating?

  • Geographic diversification analysis for risk management.

  • Input to sum_of_parts_valuation() for SOTP analysis.

Parameters:
  • symbol (str) – Ticker symbol (e.g., "AAPL").

  • fmp_client (Any | None, default: None) – Optional pre-configured FMPClient instance.

Returns:

  • symbol (str) – The ticker analysed.

  • product_segments (list[dict]) – Per-product breakdown with name, revenue, pct_of_total. Sorted by revenue descending.

  • geographic_segments (list[dict]) – Per-region breakdown with name, revenue, pct_of_total.

  • total_revenue (float) – Total revenue for reference.

  • concentration_risk (str) – "high" if top segment > 60 % of total, "moderate" if > 40 %, "low" otherwise.

  • top_product_pct (float) – Largest product segment as fraction of total revenue.

  • top_geo_pct (float) – Largest geographic region as fraction of total revenue.

Return type:

dict[str, Any]

Example

>>> from wraquant.fundamental.financials import revenue_decomposition
>>> rd = revenue_decomposition("AAPL")
>>> for seg in rd["product_segments"]:
...     print(f"{seg['name']}: {seg['pct_of_total']:.1%}")
>>> print(f"Concentration risk: {rd['concentration_risk']}")

See also

income_analysis: Revenue trends over time. common_size_analysis: Line items as % of revenue.

working_capital_analysis(symbol, *, periods=5, fmp_client=None)[source]

Analyse working capital efficiency and cash conversion cycle trends.

Working capital management directly impacts free cash flow. A company that collects receivables faster, turns inventory quicker, and delays payables generates more cash from the same level of sales. The Cash Conversion Cycle (CCC) captures all three dynamics in one number.

Deteriorating working capital (rising CCC) is an early warning of operational problems – even before it shows up in earnings.

When to use:
  • Cash flow quality assessment: rising DSO may signal aggressive revenue recognition.

  • Operational efficiency benchmarking vs. peers.

  • Early warning system: deteriorating CCC often precedes earnings misses.

  • Complement to earnings_quality() for detecting manipulation.

Mathematical formulations:

DSO = Accounts Receivable / (Revenue / 365) DIO = Inventory / (COGS / 365) DPO = Accounts Payable / (COGS / 365) CCC = DSO + DIO - DPO

Parameters:
  • symbol (str) – Ticker symbol (e.g., "WMT").

  • periods (int, default: 5) – Number of annual periods to analyse for trend detection.

  • fmp_client (Any | None, default: None) – Optional pre-configured FMPClient instance.

Returns:

  • symbol (str) – The ticker analysed.

  • periods_analysed (int) – Actual periods with data.

  • dso (list[float]) – Days Sales Outstanding by period (most recent first). Rising DSO = slower collections.

  • dio (list[float]) – Days Inventory Outstanding by period. Rising DIO = inventory building up (demand problem?).

  • dpo (list[float]) – Days Payable Outstanding by period. Rising DPO = stretching supplier payments.

  • ccc (list[float]) – Cash Conversion Cycle by period. Negative CCC (like Amazon) = funded by suppliers.

  • working_capital (list[float]) – Net working capital (current assets - current liabilities) by period.

  • wc_to_revenue (list[float]) – Working capital as % of revenue. Rising ratio = more capital tied up.

  • ccc_trend (str) – "improving" (CCC declining), "deteriorating" (rising), or "stable".

  • dates (list[str]) – Period end dates.

Return type:

dict[str, Any]

Example

>>> from wraquant.fundamental.financials import working_capital_analysis
>>> wc = working_capital_analysis("WMT")
>>> print(f"CCC: {wc['ccc'][0]:.0f} days (trend: {wc['ccc_trend']})")
>>> print(f"DSO: {wc['dso'][0]:.0f} days")

References

Richards, V. D. & Laughlin, E. J. (1980). “A Cash Conversion Cycle Approach to Liquidity Analysis.” Financial Management, 9(1), 32–38.

See also

efficiency_ratios: Point-in-time turnover ratios. cash_flow_analysis: Broader cash flow trends.

capex_analysis(symbol, *, periods=5, fmp_client=None)[source]

Analyse capital expenditure intensity, maintenance vs. growth split.

Not all CapEx is created equal. Maintenance CapEx merely sustains the existing asset base (roughly equal to depreciation). Growth CapEx expands productive capacity and drives future revenue. Understanding this split is crucial for:

  • True free cash flow: FCF = OCF - Maintenance CapEx (not total CapEx).

  • Growth assessment: high growth CapEx signals management confidence.

  • Capital intensity: CapEx/Revenue reveals how capital-hungry the business model is.

When to use:
  • Distinguish between asset-light (SaaS) and capital-heavy (industrials, utilities) business models.

  • Estimate “owner earnings” (Buffett): NI + D&A - Maintenance CapEx.

  • Identify companies investing aggressively for future growth.

  • Input to valuation: only maintenance CapEx should be deducted in a “normalised FCF” model.

Mathematical formulations:

Maintenance CapEx ≈ Depreciation & Amortisation Growth CapEx = Total CapEx - Maintenance CapEx CapEx Intensity = |CapEx| / Revenue CapEx / OCF = |CapEx| / Operating Cash Flow Owner Earnings = Net Income + D&A - Maintenance CapEx

Parameters:
  • symbol (str) – Ticker symbol (e.g., "AAPL").

  • periods (int, default: 5) – Number of annual periods to analyse.

  • fmp_client (Any | None, default: None) – Optional pre-configured FMPClient instance.

Returns:

  • symbol (str) – The ticker analysed.

  • periods_analysed (int) – Actual periods with data.

  • total_capex (list[float]) – Total CapEx by period (negative = spending).

  • depreciation (list[float]) – D&A by period (proxy for maintenance CapEx).

  • maintenance_capex (list[float]) – Estimated maintenance CapEx (≈ D&A).

  • growth_capex (list[float]) – Total CapEx minus maintenance. Positive = investing for growth.

  • capex_to_revenue (list[float]) – |CapEx| / revenue. > 15 % = capital-intensive; < 5 % = asset-light.

  • capex_to_ocf (list[float]) – |CapEx| / OCF. > 80 % leaves little FCF.

  • growth_capex_pct (list[float]) – Growth CapEx as % of total CapEx.

  • owner_earnings (list[float]) – NI + D&A - maintenance CapEx (Buffett’s preferred measure).

  • capex_trend (str) – "increasing", "decreasing", or "stable" based on CapEx/Revenue trajectory.

  • dates (list[str]) – Period end dates.

Return type:

dict[str, Any]

Example

>>> from wraquant.fundamental.financials import capex_analysis
>>> ca = capex_analysis("AMZN")
>>> print(f"CapEx intensity: {ca['capex_to_revenue'][0]:.1%}")
>>> print(f"Growth CapEx %: {ca['growth_capex_pct'][0]:.1%}")
>>> print(f"Owner earnings: ${ca['owner_earnings'][0]:,.0f}")

References

Buffett, W. (1986). Berkshire Hathaway Shareholder Letter (“owner earnings” concept).

See also

cash_flow_analysis: Broader cash flow metrics. shareholder_returns: How CapEx competes with buybacks/dividends.

shareholder_returns(symbol, *, periods=5, fmp_client=None)[source]

Analyse total shareholder yield: dividends + buybacks + debt reduction.

Total shareholder yield captures all cash returned to shareholders, not just dividends. In the modern market, share buybacks often exceed dividends by a wide margin (e.g., Apple returns 3-4x more via buybacks than dividends). Focusing only on dividend yield misses half the story.

When to use:
  • Income-focused investing: total yield (dividends + buybacks) is a better income proxy than dividend yield alone.

  • Sustainability analysis: is the company returning more cash than it generates? Payout ratio > 100 % is unsustainable.

  • Shareholder-friendly management: track trends in capital allocation policy.

  • Factor construction: total yield is a more predictive value signal than dividend yield.

Mathematical formulations:

Total Yield = (Dividends + Buybacks) / Market Cap Payout Ratio = (Dividends + Buybacks) / Net Income FCF Payout Ratio = (Dividends + Buybacks) / FCF Buyback Yield = Net Buybacks / Market Cap Dividend Yield = Dividends / Market Cap

Parameters:
  • symbol (str) – Ticker symbol (e.g., "AAPL").

  • periods (int, default: 5) – Number of annual periods to analyse.

  • fmp_client (Any | None, default: None) – Optional pre-configured FMPClient instance.

Returns:

  • symbol (str) – The ticker analysed.

  • periods_analysed (int) – Actual periods with data.

  • dividends_paid (list[float]) – Absolute dividends by period.

  • buybacks (list[float]) – Absolute buybacks by period.

  • total_returned (list[float]) – Dividends + buybacks.

  • dividend_yield (float) – Current dividend yield (TTM).

  • buyback_yield (float) – Most recent buyback yield.

  • total_yield (float) – Dividend + buyback yield combined.

  • payout_ratio (list[float]) – Total returned / net income. > 1.0 means returning more than earned (using balance sheet).

  • fcf_payout_ratio (list[float]) – Total returned / FCF. > 1.0 means returning more than free cash flow.

  • sustainability (str) – "sustainable" if FCF payout < 80 %, "caution" if 80–120 %, "unsustainable" if > 120 %.

  • trend (str) – "increasing" if total returned is growing, "decreasing" or "stable".

  • dates (list[str]) – Period end dates.

Return type:

dict[str, Any]

Example

>>> from wraquant.fundamental.financials import shareholder_returns
>>> sr = shareholder_returns("AAPL")
>>> print(f"Total yield: {sr['total_yield']:.2%}")
>>> print(f"Sustainability: {sr['sustainability']}")
>>> for i, d in enumerate(sr['dates'][:3]):
...     print(f"  {d}: ${sr['total_returned'][i]:,.0f}")

References

Mauboussin, M. J. & Callahan, D. (2014). “Capital Allocation: Evidence, Analytical Methods, and Assessment Guidance.” Credit Suisse Global Financial Strategies.

See also

cash_flow_analysis: Broader cash flow metrics. capex_analysis: How CapEx competes with shareholder returns.

value_screen(max_pe=20.0, min_dividend_yield=0.02, *, max_debt_equity=1.5, min_market_cap=1000000000, country='US', limit=50, fmp_client=None)[source]

Screen for value stocks: low P/E, decent dividend, manageable debt.

Implements a classic Ben Graham-style value screen that identifies stocks trading at low multiples while paying dividends and maintaining conservative balance sheets. This is the foundation of value investing and the HML (high-minus-low) factor in Fama-French.

When to use:

Use this screen to build a value-tilted portfolio, identify contrarian opportunities, or as the starting point for deep-dive fundamental analysis. Value screens work best in conjunction with a quality filter (see quality_screen()) to avoid “value traps” – cheap stocks that are cheap for good reason.

Parameters:
  • max_pe (float, default: 20.0) – Maximum price-to-earnings ratio. The S&P 500 median P/E is historically around 15–20. Setting this to 15 focuses on deep value; 20 casts a wider net.

  • min_dividend_yield (float, default: 0.02) – Minimum annual dividend yield (as decimal). 0.02 = 2 %. Set to 0 to include non-dividend payers.

  • max_debt_equity (float, default: 1.5) – Maximum debt-to-equity ratio. 1.5 allows moderate leverage; 0.5 is conservative.

  • min_market_cap (int, default: 1000000000) – Minimum market capitalisation in USD. The default ($1B) excludes micro/small-caps.

  • country (str, default: 'US') – Country filter (ISO code). "US" for domestic.

  • limit (int, default: 50) – Maximum number of results to return.

  • fmp_client (Any | None, default: None) – Optional pre-configured FMPClient instance. If None, a default client is created.

Return type:

DataFrame

Returns:

DataFrame of matching stocks with key metrics including symbol, companyName, marketCap, price, beta, lastAnnualDividend, and sector.

Example

>>> from wraquant.fundamental.screening import value_screen
>>> df = value_screen(max_pe=15, min_dividend_yield=0.03)
>>> print(f"Found {len(df)} deep value stocks")
>>> print(df[["symbol", "price", "marketCap"]].head(10))

See also

quality_screen: Filter value candidates by profitability. magic_formula_screen: Combines value and quality in one rank.

growth_screen(min_revenue_growth=0.15, *, min_market_cap=500000000, country='US', limit=50, fmp_client=None)[source]

Screen for growth stocks: high revenue growth, positive momentum.

Identifies companies with strong top-line growth, which is the primary driver of long-term equity returns. Revenue growth is preferred over earnings growth because it is harder to manipulate and more persistent.

When to use:

Use this screen to identify companies in secular growth industries or those gaining market share. Growth screens are most effective in bull markets and for momentum-based strategies. Combine with earnings_quality() to filter out companies that are growing revenue but burning cash.

Parameters:
  • min_revenue_growth (float, default: 0.15) – Minimum YoY revenue growth rate (as decimal). 0.15 = 15 %. Set higher (0.30+) for hyper-growth.

  • min_market_cap (int, default: 500000000) – Minimum market cap in USD.

  • country (str, default: 'US') – Country filter.

  • limit (int, default: 50) – Maximum results.

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Return type:

DataFrame

Returns:

DataFrame of high-growth stocks with key metrics. Includes a revenue_growth column when available from FMP data.

Example

>>> from wraquant.fundamental.screening import growth_screen
>>> df = growth_screen(min_revenue_growth=0.25)
>>> print(f"Found {len(df)} high-growth stocks")

See also

value_screen: Complement for a barbell strategy. quality_screen: Ensure growth is profitable.

quality_factor_screen(min_roe=0.15, max_de=1.0, *, min_market_cap=1000000000, country='US', limit=50, fmp_client=None)

Screen for quality stocks: high ROE, low leverage, wide moats.

Quality investing targets companies with durable competitive advantages – high returns on equity, conservative balance sheets, and stable profitability. The quality factor (RMW in Fama-French 5) has historically delivered positive risk-adjusted returns with lower drawdowns than value or momentum.

When to use:

Use this screen to identify “compounders” – stocks that grow book value through high reinvestment rates. Quality screens excel in bear markets and risk-off environments because high- quality companies are more resilient to economic downturns.

Parameters:
  • min_roe (float, default: 0.15) – Minimum return on equity (as decimal). 0.15 = 15 %. The median S&P 500 ROE is around 15–18 %.

  • max_de (float, default: 1.0) – Maximum debt-to-equity ratio. 1.0 is moderate; 0.5 is conservative. Some capital-light businesses (tech, SaaS) naturally have low D/E.

  • min_market_cap (int, default: 1000000000) – Minimum market cap in USD.

  • country (str, default: 'US') – Country filter.

  • limit (int, default: 50) – Maximum results.

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Return type:

DataFrame

Returns:

DataFrame of quality stocks enriched with roe and debt_to_equity columns for verification.

Example

>>> from wraquant.fundamental.screening import quality_screen
>>> df = quality_screen(min_roe=0.20, max_de=0.5)
>>> print(f"Found {len(df)} high-quality stocks")
>>> print(df[["symbol", "roe", "debt_to_equity"]].head(10))

References

Novy-Marx, R. (2013). “The Other Side of Value: The Gross Profitability Premium.” Journal of Financial Economics, 108(1), 1–28.

See also

value_screen: Combine quality + value for best risk/reward. earnings_quality: Validate that earnings are cash-backed.

piotroski_screen(min_score=7, *, min_market_cap=500000000, limit=100, fmp_client=None)[source]

Screen for stocks with high Piotroski F-Score.

The Piotroski F-Score is a 0–9 composite score measuring financial strength across three categories:

Profitability (4 pts):
  1. Positive ROA

  2. Positive operating cash flow

  3. ROA improvement (year over year)

  4. Cash flow > net income (accruals quality)

Leverage & liquidity (3 pts):
  1. Decrease in leverage (long-term debt / assets)

  2. Improvement in current ratio

  3. No new equity issuance

Operating efficiency (2 pts):
  1. Improvement in gross margin

  2. Improvement in asset turnover

Scores of 8–9 identify the strongest companies; scores of 0–2 predict financial distress. Piotroski (2000) showed that a long- short strategy based on the F-Score earned 23 % annual returns among high book-to-market stocks.

Parameters:
  • min_score (int, default: 7) – Minimum Piotroski F-Score (0–9). Default 7 captures the top tier. Set to 8 for ultra-high quality.

  • min_market_cap (int, default: 500000000) – Minimum market cap in USD.

  • limit (int, default: 100) – Number of candidates to evaluate (more = slower but more results).

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Return type:

DataFrame

Returns:

DataFrame with columns symbol, piotroski_score, altman_z, and market_cap for stocks meeting the threshold. Sorted by F-Score descending.

Example

>>> from wraquant.fundamental.screening import piotroski_screen
>>> df = piotroski_screen(min_score=8)
>>> print(f"Found {len(df)} high F-Score stocks")
>>> print(df[["symbol", "piotroski_score", "altman_z"]].head())

References

Piotroski, J. D. (2000). “Value Investing: The Use of Historical Financial Statement Information to Separate Winners from Losers.” Journal of Accounting Research, 38, 1–41.

See also

financial_health_score: Continuous 0–100 health score. value_screen: Combine with F-Score for deep value strategy.

magic_formula_screen(top_n=30, *, min_market_cap=1000000000, fmp_client=None)[source]

Screen using Greenblatt’s Magic Formula: ROIC + Earnings Yield.

The magic formula ranks stocks by two criteria:
  1. Return on invested capital (ROIC) – Measures how efficiently management deploys capital. Higher is better.

  2. Earnings yield (EBIT / EV) – Measures how cheap the stock is relative to its earning power. Higher is better.

Each stock gets a rank on ROIC and a rank on earnings yield; the combined rank identifies companies that are both high-quality AND cheap. Greenblatt (2006) showed this simple strategy beat the market over 17 years.

When to use:

Use this as a standalone strategy or as a starting point for further due diligence. The magic formula is most effective for mid-to-large cap US equities with stable earnings. Avoid applying it to financials and utilities, which have different capital structures.

Parameters:
  • top_n (int, default: 30) – Number of top-ranked stocks to return. Greenblatt recommends holding 20–30 positions.

  • min_market_cap (int, default: 1000000000) – Minimum market cap in USD. The original study used $50M; $1B is more practical for institutional investors.

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Returns:

  • symbol (str) – Ticker symbol.

  • roic (float) – Return on invested capital (TTM).

  • earnings_yield (float) – 1 / P/E ratio (TTM).

  • pe_ratio (float) – P/E ratio for reference.

  • market_cap (float) – Market capitalisation.

  • roic_rank (float) – Rank by ROIC (1 = best).

  • ey_rank (float) – Rank by earnings yield (1 = best).

  • magic_rank (float) – Combined rank (lower = better).

Return type:

DataFrame

Example

>>> from wraquant.fundamental.screening import magic_formula_screen
>>> df = magic_formula_screen(top_n=20)
>>> print(df[["symbol", "roic", "earnings_yield", "magic_rank"]].head())

References

Greenblatt, J. (2006). The Little Book That Beats the Market. Wiley.

See also

quality_screen: Alternative quality-focused screen. value_screen: Pure value screen without quality component.

custom_screen(criteria, *, fmp_client=None)[source]

Screen stocks using a flexible criteria dictionary.

A general-purpose screener that accepts any combination of filter criteria as a dictionary. Use this when the predefined screens (value, growth, quality) do not match your requirements.

When to use:

Use this for custom factor construction, sector-specific screens, or when you need to combine filters that span multiple predefined screens (e.g., “growth + low beta + tech sector”).

Parameters:
  • criteria (dict[str, Any]) –

    Dictionary of screening criteria. Supported keys:

    Market cap: - min_market_cap (int) – Minimum market cap in USD. - max_market_cap (int) – Maximum market cap in USD.

    Classification: - sector (str) – GICS sector (e.g., "Technology"). - industry (str) – Industry filter. - country (str) – Country code (default "US"). - exchange (str) – Exchange (e.g., "NASDAQ").

    Valuation: - min_pe (float) – Minimum P/E ratio. - max_pe (float) – Maximum P/E ratio. - min_dividend_yield (float) – Minimum dividend yield.

    Quality: - min_roe (float) – Minimum ROE. - max_debt_equity (float) – Maximum D/E ratio.

    Risk: - min_beta (float) – Minimum beta. - max_beta (float) – Maximum beta.

    Price & volume: - min_price (float) – Minimum share price. - max_price (float) – Maximum share price. - min_volume (int) – Minimum average daily volume.

    Control: - limit (int) – Maximum results (default 100).

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Return type:

DataFrame

Returns:

DataFrame of matching stocks with standard FMP screener columns.

Example

>>> from wraquant.fundamental.screening import custom_screen
>>> df = custom_screen({
...     "sector": "Technology",
...     "min_market_cap": 10_000_000_000,
...     "max_beta": 1.2,
...     "min_dividend_yield": 0.01,
...     "limit": 20,
... })
>>> print(df[["symbol", "companyName", "marketCap"]].head())

See also

value_screen: Predefined value criteria. growth_screen: Predefined growth criteria. quality_screen: Predefined quality criteria.

dividend_aristocrat_screen(min_years=10, *, min_market_cap=1000000000, country='US', limit=50, fmp_client=None)[source]

Screen for stocks with consecutive years of dividend growth.

Dividend Aristocrats (S&P 500 members with 25+ years of consecutive dividend increases) have historically outperformed with lower volatility. This screen identifies companies with N or more consecutive years of annual dividend per share growth – a strong signal of financial discipline, predictable cash flows, and shareholder-friendly management.

When to use:
  • Income-focused portfolio construction.

  • Quality screening: consistent dividend growth requires consistent earnings growth.

  • Defensive strategy: dividend growers tend to outperform in bear markets.

  • Retirement portfolios: rising income stream over time.

Parameters:
  • min_years (int, default: 10) – Minimum consecutive years of dividend growth. 25 = traditional Aristocrat; 10 = broader “achiever” screen; 5 = emerging dividend growers.

  • min_market_cap (int, default: 1000000000) – Minimum market cap in USD.

  • country (str, default: 'US') – Country filter.

  • limit (int, default: 50) – Maximum results to return.

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Returns:

  • symbol (str) – Ticker symbol.

  • consecutive_years (int) – Years of consecutive dividend growth.

  • current_yield (float) – Current dividend yield.

  • dividend_growth_rate (float) – Most recent YoY growth.

  • market_cap (float) – Market capitalisation.

Return type:

DataFrame

Example

>>> from wraquant.fundamental.screening import dividend_aristocrat_screen
>>> df = dividend_aristocrat_screen(min_years=25)
>>> print(f"Found {len(df)} Dividend Aristocrats")
>>> print(df[["symbol", "consecutive_years", "current_yield"]].head())

References

S&P Dow Jones Indices. “S&P 500 Dividend Aristocrats.” ProShares (2019). “Why Dividend Growth Matters.”

See also

value_screen: Combine with dividend screen for income + value. quality_screen: Dividend consistency as a quality proxy.

turnaround_screen(max_pe=15.0, *, min_margin_improvement=0.02, min_market_cap=500000000, country='US', limit=50, fmp_client=None)[source]

Screen for turnaround candidates: improving margins but still cheap.

Turnaround stocks are value stocks with positive momentum in their fundamentals. They trade at low multiples (the market hasn’t noticed the improvement yet) but show rising margins, indicating operational recovery. This combines value investing with momentum – the two most robust factors in academic research.

When to use:
  • Contrarian strategies: buy when margins inflect upward.

  • Mean-reversion plays: stocks with temporarily depressed earnings reverting to historical norms.

  • Combine with insider buying screen for higher conviction.

Parameters:
  • max_pe (float, default: 15.0) – Maximum P/E ratio (cheap stocks only). Default 15.

  • min_margin_improvement (float, default: 0.02) – Minimum operating margin improvement (most recent - prior year). 0.02 = 2pp improvement.

  • min_market_cap (int, default: 500000000) – Minimum market cap in USD.

  • country (str, default: 'US') – Country filter.

  • limit (int, default: 50) – Maximum results.

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Returns:

  • symbol (str) – Ticker symbol.

  • pe_ratio (float) – Current P/E ratio.

  • operating_margin_current (float) – Most recent period.

  • operating_margin_prior (float) – Prior period.

  • margin_improvement (float) – Change in operating margin.

  • revenue_growth (float) – YoY revenue growth.

  • market_cap (float) – Market capitalisation.

Return type:

DataFrame

Example

>>> from wraquant.fundamental.screening import turnaround_screen
>>> df = turnaround_screen(max_pe=12)
>>> print(f"Found {len(df)} turnaround candidates")
>>> print(df[["symbol", "pe_ratio", "margin_improvement"]].head())

See also

value_screen: Pure value screen. quality_screen: Ensure turnarounds have staying power. insider_buying_screen: Insider confidence in the turnaround.

insider_buying_screen(min_buys=3, days=90, *, min_market_cap=500000000, country='US', limit=50, fmp_client=None)[source]

Screen for stocks with significant recent insider buying activity.

Insiders (officers, directors, 10 %+ owners) are the most informed participants in a stock. Academic research consistently shows that insider purchases predict positive future returns, especially cluster buying (multiple insiders buying in a short window). Insider sales are less informative (often driven by diversification/taxes).

This screen identifies stocks with multiple insider buy transactions in the recent period – a strong signal of management confidence.

When to use:
  • Confirmation signal: use alongside value/quality screens.

  • Contrarian buying: insiders buying during market selloffs.

  • Special situations: new CEO buying, founder increasing stake.

  • Pair with turnaround_screen() for high-conviction turnarounds.

Parameters:
  • min_buys (int, default: 3) – Minimum number of insider purchase transactions in the lookback window. 3+ is significant cluster buying.

  • days (int, default: 90) – Lookback period in calendar days. Default 90 (one quarter).

  • min_market_cap (int, default: 500000000) – Minimum market cap in USD.

  • country (str, default: 'US') – Country filter.

  • limit (int, default: 50) – Maximum results.

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Returns:

  • symbol (str) – Ticker symbol.

  • buy_count (int) – Number of insider purchases.

  • total_value (float) – Total dollar value of purchases.

  • unique_insiders (int) – Number of distinct insiders buying.

  • market_cap (float) – Market capitalisation.

Return type:

DataFrame

Example

>>> from wraquant.fundamental.screening import insider_buying_screen
>>> df = insider_buying_screen(min_buys=5, days=60)
>>> print(f"Found {len(df)} stocks with cluster insider buying")
>>> print(df[["symbol", "buy_count", "total_value"]].head())

References

Lakonishok, J. & Lee, I. (2001). “Are Insider Trades Informative?” Review of Financial Studies, 14(1), 79–111.

See also

turnaround_screen: Combine with insider buying for conviction. quality_screen: Verify fundamentals back the insider thesis.

momentum_value_screen(top_n=30, *, min_market_cap=1000000000, country='US', fmp_client=None)[source]

Screen combining value (low PE) with price momentum (positive 6M return).

Value and momentum are the two most persistent and well-documented factors in equity returns. They are negatively correlated, making a combined strategy more robust than either alone. This screen identifies stocks that are both cheap (low P/E) and in an uptrend (positive 6-month price return), capturing the sweet spot where value is being recognized by the market.

When to use:
  • Multi-factor portfolio construction: value + momentum is the classic two-factor strategy (Asness et al., 2013).

  • Avoid value traps: momentum filter ensures the market is beginning to recognize the value.

  • Tactical allocation: identify undervalued stocks with improving price action.

Mathematical formulation:

Value Score = percentile_rank(1/PE) (higher = cheaper) Momentum Score = percentile_rank(6M return) Combined Score = Value Score + Momentum Score Select top N by Combined Score.

Parameters:
  • top_n (int, default: 30) – Number of top-ranked stocks to return.

  • min_market_cap (int, default: 1000000000) – Minimum market cap in USD.

  • country (str, default: 'US') – Country filter.

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Returns:

  • symbol (str) – Ticker symbol.

  • pe_ratio (float) – P/E ratio.

  • earnings_yield (float) – 1/PE (higher = cheaper).

  • price_return_6m (float) – 6-month price return.

  • value_rank (float) – Rank by earnings yield (1 = best).

  • momentum_rank (float) – Rank by 6M return (1 = best).

  • combined_rank (float) – Sum of value and momentum ranks (lower = better).

  • market_cap (float) – Market capitalisation.

Return type:

DataFrame

Example

>>> from wraquant.fundamental.screening import momentum_value_screen
>>> df = momentum_value_screen(top_n=20)
>>> print(df[["symbol", "pe_ratio", "price_return_6m",
...           "combined_rank"]].head())

References

Asness, C. S., Moskowitz, T. J., & Pedersen, L. H. (2013). “Value and Momentum Everywhere.” Journal of Finance, 68(3), 929–985.

See also

value_screen: Pure value screen. magic_formula_screen: Value + quality (similar concept).

Ratios

Financial ratio analysis using FMP data.

Provides comprehensive financial ratio computation across six categories: profitability, liquidity, leverage, efficiency, valuation, and growth. Includes DuPont decomposition (3-way and 5-way) and a convenience function that aggregates all ratios into a single dictionary.

All functions accept a ticker symbol and optionally an FMP client instance. When no client is provided, one is created automatically (requires the market-data extra and an FMP API key in the environment).

The ratios computed here form the foundation of: - Value investing: P/E, P/B, EV/EBITDA for identifying underpriced assets. - Quality screening: ROE, ROIC, margins for separating winners from losers. - Factor models: Fama-French HML (P/B), RMW (profitability), CMA (investment). - Credit analysis: leverage and liquidity ratios for default prediction.

Example

>>> from wraquant.fundamental.ratios import comprehensive_ratios
>>> ratios = comprehensive_ratios("AAPL")
>>> print(f"ROE: {ratios['profitability']['roe']:.2%}")
>>> print(f"D/E: {ratios['leverage']['debt_to_equity']:.2f}")

References

  • Fama, E. F. & French, K. R. (1993). “Common risk factors in the returns on stocks and bonds.” Journal of Financial Economics, 33, 3–56.

  • Piotroski, J. D. (2000). “Value Investing: The Use of Historical Financial Statement Information to Separate Winners from Losers.” Journal of Accounting Research, 38, 1–41.

  • Palepu, K. G. & Healy, P. M. (2013). Business Analysis and Valuation, 5th edition.

profitability_ratios(symbol, *, period='annual', fmp_client=None)[source]

Compute core profitability ratios for a company.

Profitability ratios measure how effectively a company converts revenue into profit at various stages of the income statement. Use these to compare operating efficiency across peers and to track margin trends over time.

Mathematical formulations:

ROE = Net Income / Shareholders’ Equity ROA = Net Income / Total Assets ROIC = NOPAT / Invested Capital

= EBIT * (1 - tax rate) / (Total Debt + Equity - Cash)

Gross Margin = Gross Profit / Revenue Operating Margin = Operating Income / Revenue Net Margin = Net Income / Revenue

Parameters:
  • symbol (str) – Ticker symbol (e.g., "AAPL").

  • period (str, default: 'annual') – "annual" or "quarter". Annual data is more stable; quarterly reveals recent trends.

  • fmp_client (Any | None, default: None) – Optional pre-configured FMPClient instance. If None, a default client is created.

Returns:

  • roe (float) – Return on equity. > 0.15 is generally strong.

  • roa (float) – Return on assets. > 0.05 is solid.

  • roic (float) – Return on invested capital. > WACC means the company creates value.

  • gross_margin (float) – Gross profit / revenue.

  • operating_margin (float) – Operating income / revenue.

  • net_margin (float) – Net income / revenue.

  • period (str) – The period used.

Return type:

dict[str, float]

Example

>>> from wraquant.fundamental.ratios import profitability_ratios
>>> p = profitability_ratios("MSFT")
>>> print(f"ROE: {p['roe']:.2%}, ROIC: {p['roic']:.2%}")

See also

dupont_decomposition: Breaks ROE into its drivers. efficiency_ratios: Asset utilisation metrics.

liquidity_ratios(symbol, *, period='annual', fmp_client=None)[source]

Compute short-term liquidity ratios.

Liquidity ratios assess a company’s ability to meet its short-term obligations. They are critical for credit analysis, bankruptcy prediction (Altman Z-Score), and the Piotroski F-Score.

Mathematical formulations:

Current Ratio = Current Assets / Current Liabilities Quick Ratio = (Current Assets - Inventory) / Current Liabilities Cash Ratio = Cash & Equivalents / Current Liabilities Working Capital = Current Assets - Current Liabilities

Parameters:
  • symbol (str) – Ticker symbol.

  • period (str, default: 'annual') – "annual" or "quarter".

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Returns:

  • current_ratio (float) – > 1.5 is healthy; < 1.0 is a red flag.

  • quick_ratio (float) – Acid-test; excludes illiquid inventory.

  • cash_ratio (float) – Most conservative; cash only.

  • working_capital (float) – Absolute dollar liquidity buffer.

  • period (str) – The period used.

Return type:

dict[str, float]

Example

>>> from wraquant.fundamental.ratios import liquidity_ratios
>>> liq = liquidity_ratios("AAPL")
>>> print(f"Current ratio: {liq['current_ratio']:.2f}")

References

Altman, E. I. (1968). “Financial Ratios, Discriminant Analysis and the Prediction of Corporate Bankruptcy.” Journal of Finance, 23(4), 589–609.

See also

leverage_ratios: Long-term solvency.

leverage_ratios(symbol, *, period='annual', fmp_client=None)[source]

Compute leverage and solvency ratios.

Leverage ratios measure the extent to which a company uses debt to finance its assets. Higher leverage amplifies both returns and risk. These ratios are essential for: - Credit risk modeling (probability of default). - Merton structural models (distance to default). - Factor investing (leverage as a risk factor).

Mathematical formulations:

Debt-to-Equity = Total Debt / Shareholders’ Equity Debt Ratio = Total Debt / Total Assets Interest Coverage = EBIT / Interest Expense Equity Multiplier = Total Assets / Shareholders’ Equity Debt-to-EBITDA = Total Debt / EBITDA

Parameters:
  • symbol (str) – Ticker symbol.

  • period (str, default: 'annual') – "annual" or "quarter".

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Returns:

  • debt_to_equity (float) – D/E ratio; > 2.0 is high leverage.

  • debt_ratio (float) – Portion of assets financed by debt.

  • interest_coverage (float) – EBIT / interest; < 1.5 is distressed. Higher is safer.

  • equity_multiplier (float) – Assets per dollar of equity; captures leverage in DuPont.

  • debt_to_ebitda (float) – How many years of EBITDA to repay debt; < 3 is conservative.

  • period (str) – The period used.

Return type:

dict[str, float]

Example

>>> from wraquant.fundamental.ratios import leverage_ratios
>>> lev = leverage_ratios("AAPL")
>>> print(f"D/E: {lev['debt_to_equity']:.2f}")

See also

liquidity_ratios: Short-term solvency. dupont_decomposition: How leverage drives ROE.

efficiency_ratios(symbol, *, period='annual', fmp_client=None)[source]

Compute asset utilisation and efficiency ratios.

Efficiency ratios (also called activity ratios) measure how effectively a company uses its assets to generate revenue. They are the turnover component in DuPont analysis and are critical for comparing capital-light vs. capital-intensive businesses.

Mathematical formulations:

Asset Turnover = Revenue / Total Assets Inventory Turnover = COGS / Average Inventory Receivable Turnover = Revenue / Accounts Receivable Payable Turnover = COGS / Accounts Payable Days Sales Outstanding = 365 / Receivable Turnover Days Inventory Outstanding = 365 / Inventory Turnover Days Payable Outstanding = 365 / Payable Turnover Cash Conversion Cycle = DSO + DIO - DPO

Parameters:
  • symbol (str) – Ticker symbol.

  • period (str, default: 'annual') – "annual" or "quarter".

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Returns:

  • asset_turnover (float) – Revenue per dollar of assets.

  • inventory_turnover (float) – How many times inventory is sold per period. Higher is better for retail.

  • receivable_turnover (float) – How quickly receivables convert to cash.

  • payable_turnover (float) – How quickly the company pays suppliers.

  • days_sales_outstanding (float) – Average collection period in days.

  • days_inventory_outstanding (float) – Average days to sell inventory.

  • days_payable_outstanding (float) – Average days to pay suppliers.

  • cash_conversion_cycle (float) – DSO + DIO - DPO; shorter is better. Negative means the company is funded by suppliers (e.g., Amazon).

  • period (str) – The period used.

Return type:

dict[str, float]

Example

>>> from wraquant.fundamental.ratios import efficiency_ratios
>>> eff = efficiency_ratios("WMT")
>>> print(f"Inventory turnover: {eff['inventory_turnover']:.1f}x")
>>> print(f"Cash conversion cycle: {eff['cash_conversion_cycle']:.0f} days")

See also

dupont_decomposition: Efficiency is a component of ROE.

valuation_ratios(symbol, *, fmp_client=None)[source]

Compute market-based valuation multiples.

Valuation ratios relate the market price to fundamentals. They are the workhorses of relative valuation and the value factor in Fama-French models. Use them to compare a stock to its sector median, its own history, or the broad market.

Mathematical formulations:

P/E = Price / Earnings Per Share P/B = Price / Book Value Per Share P/S = Price / Revenue Per Share EV/EBITDA = Enterprise Value / EBITDA PEG = P/E / EPS Growth Rate (%) Div Yield = Dividend Per Share / Price

Parameters:
  • symbol (str) – Ticker symbol.

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Returns:

  • pe_ratio (float) – Price-to-earnings. Market median ~15–20.

  • pb_ratio (float) – Price-to-book.

  • ps_ratio (float) – Price-to-sales. Useful for unprofitable companies.

  • ev_to_ebitda (float) – Enterprise value / EBITDA. < 10 is often considered cheap.

  • peg_ratio (float) – Growth-adjusted P/E. < 1 may be undervalued relative to growth.

  • dividend_yield (float) – Annual dividend / price.

  • earnings_yield (float) – Inverse of P/E; comparable to bond yields.

  • price_to_fcf (float) – Price / free cash flow per share.

Return type:

dict[str, float]

Example

>>> from wraquant.fundamental.ratios import valuation_ratios
>>> val = valuation_ratios("AAPL")
>>> print(f"P/E: {val['pe_ratio']:.1f}, EV/EBITDA: {val['ev_to_ebitda']:.1f}")

References

Fama, E. F. & French, K. R. (1992). “The Cross-Section of Expected Stock Returns.” Journal of Finance, 47(2), 427–465.

See also

relative_valuation: Compare multiples to peers.

growth_ratios(symbol, *, period='annual', fmp_client=None)[source]

Compute revenue, earnings, and dividend growth rates.

Growth ratios quantify the trajectory of a company’s top line, bottom line, and shareholder distributions. They are core inputs to the PEG ratio, DCF terminal growth assumptions, and growth factor construction.

Mathematical formulations:

Revenue Growth = (Revenue_t - Revenue_{t-1}) / Revenue_{t-1} EPS Growth = (EPS_t - EPS_{t-1}) / EPS_{t-1} Dividend Growth = (DPS_t - DPS_{t-1}) / DPS_{t-1} EBITDA Growth = (EBITDA_t - EBITDA_{t-1}) / EBITDA_{t-1} FCF Growth = (FCF_t - FCF_{t-1}) / FCF_{t-1}

Parameters:
  • symbol (str) – Ticker symbol.

  • period (str, default: 'annual') – "annual" or "quarter". Annual is less noisy; quarterly captures recent momentum.

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Returns:

  • revenue_growth (float) – Most recent period YoY revenue growth.

  • eps_growth (float) – Most recent period YoY EPS growth.

  • dividend_growth (float) – Most recent period YoY dividend growth.

  • ebitda_growth (float) – Most recent period YoY EBITDA growth.

  • fcf_growth (float) – Most recent period YoY FCF growth.

  • revenue_growth_3y (float) – 3-year CAGR of revenue.

  • revenue_growth_5y (float) – 5-year CAGR of revenue.

  • revenue_growth_history (list[float]) – Growth for each available period.

  • period (str) – The period used.

Return type:

dict[str, float | list[float]]

Example

>>> from wraquant.fundamental.ratios import growth_ratios
>>> g = growth_ratios("NVDA")
>>> print(f"Revenue growth: {g['revenue_growth']:.1%}")
>>> print(f"3Y CAGR: {g['revenue_growth_3y']:.1%}")

See also

valuation_ratios: PEG uses growth.

dupont_decomposition(symbol, *, period='annual', fmp_client=None)[source]

Perform 3-way and 5-way DuPont decomposition of ROE.

DuPont analysis decomposes return on equity into its fundamental drivers, revealing why a company’s ROE is high or low. This is essential for distinguishing between companies that earn high ROE through operational excellence vs. financial leverage.

Mathematical formulations:

3-way DuPont:
ROE = Net Margin x Asset Turnover x Equity Multiplier

= (NI / Rev) x (Rev / Assets) x (Assets / Equity)

5-way DuPont (extended):
ROE = Tax Burden x Interest Burden x Operating Margin

x Asset Turnover x Equity Multiplier

= (NI / EBT) x (EBT / EBIT) x (EBIT / Rev)

x (Rev / Assets) x (Assets / Equity)

Parameters:
  • symbol (str) – Ticker symbol.

  • period (str, default: 'annual') – "annual" or "quarter".

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Returns:

3-way components: - net_margin (float) – NI / Revenue. - asset_turnover (float) – Revenue / Total Assets. - equity_multiplier (float) – Total Assets / Equity. - roe_3way (float) – Product of the three components.

5-way components: - tax_burden (float) – NI / EBT. Closer to 1.0 means lower

effective tax rate.

  • interest_burden (float) – EBT / EBIT. Closer to 1.0 means less interest cost relative to operating profit.

  • operating_margin (float) – EBIT / Revenue.

  • roe_5way (float) – Product of the five components.

  • period (str) – The period used.

Return type:

dict[str, float]

Example

>>> from wraquant.fundamental.ratios import dupont_decomposition
>>> dp = dupont_decomposition("AAPL")
>>> print(f"ROE (3-way): {dp['roe_3way']:.2%}")
>>> print(f"  = {dp['net_margin']:.2%} margin")
>>> print(f"  x {dp['asset_turnover']:.2f} turnover")
>>> print(f"  x {dp['equity_multiplier']:.2f} leverage")

References

Soliman, M. T. (2008). “The Use of DuPont Analysis by Market Participants.” The Accounting Review, 83(3), 823–853.

See also

profitability_ratios: Individual profitability metrics. leverage_ratios: Detailed leverage analysis.

comprehensive_ratios(symbol, *, period='annual', fmp_client=None)[source]

Compute all financial ratios in a single call.

Convenience function that aggregates profitability, liquidity, leverage, efficiency, valuation, growth, and DuPont ratios into a single nested dictionary. Useful for building screening dashboards and factor databases.

Parameters:
  • symbol (str) – Ticker symbol.

  • period (str, default: 'annual') – "annual" or "quarter" (used for non-valuation ratios; valuation ratios always use TTM).

  • fmp_client (Any | None, default: None) – Optional FMPClient instance. Passing one avoids creating multiple clients.

Returns:

Return type:

dict[str, Any]

Example

>>> from wraquant.fundamental.ratios import comprehensive_ratios
>>> all_ratios = comprehensive_ratios("AAPL")
>>> print(f"ROE: {all_ratios['profitability']['roe']:.2%}")
>>> print(f"D/E: {all_ratios['leverage']['debt_to_equity']:.2f}")
>>> print(f"P/E: {all_ratios['valuation']['pe_ratio']:.1f}")

See also

financial_health_score: Composite score from these ratios.

ratio_comparison(symbol, *, peers=None, fmp_client=None)[source]

Compare a stock’s key ratios to its peer group with percentile ranking.

Relative ratio analysis is the backbone of peer-group valuation and factor-based stock selection. Rather than asking “is this ratio good in absolute terms?”, this function asks “how does it rank against comparable companies?” A 20 % ROE might be outstanding in utilities but mediocre in tech.

When to use:
  • Building peer-relative factor scores for multi-factor models.

  • Identifying companies that are outliers within their industry.

  • Due-diligence: confirming whether a stock’s ratios are truly exceptional or merely in-line with its sector.

Mathematical formulation:

Percentile_i = (# peers with ratio <= target_ratio_i) / N_peers

A percentile of 0.80 means the stock ranks in the 80th percentile among its peers on that metric.

Parameters:
  • symbol (str) – Ticker symbol (e.g., "AAPL").

  • peers (list[str] | None, default: None) – List of peer ticker symbols. If None, peers are auto-discovered via FMP’s stock_peers() endpoint, which returns companies in the same industry.

  • fmp_client (Any | None, default: None) – Optional pre-configured FMPClient instance.

Returns:

  • symbol (str) – The target ticker.

  • peers (list[str]) – Peers used for comparison.

  • target_ratios (dict) – The target’s key ratios.

  • peer_averages (dict) – Mean of each ratio across peers.

  • peer_medians (dict) – Median of each ratio across peers.

  • percentile_rank (dict) – Percentile rank (0–1) of the target vs. peers for each ratio. Higher is better for profitability/efficiency; lower is better for leverage.

  • peers_detail (list[dict]) – Individual peer ratios.

Return type:

dict[str, Any]

Example

>>> from wraquant.fundamental.ratios import ratio_comparison
>>> comp = ratio_comparison("AAPL")
>>> print(f"ROE percentile: {comp['percentile_rank']['roe']:.0%}")
>>> print(f"D/E percentile: {comp['percentile_rank']['debt_to_equity']:.0%}")

See also

comprehensive_ratios: All ratios for a single company. sector_comparison: Compare against sector-wide averages.

Analyse multi-year ratio trends to identify improving or deteriorating fundamentals.

Static ratios show where a company is now; trends show where it is going. A company with a 12 % ROE that has risen from 8 % is far more attractive than one at 15 % declining from 20 %. This function computes trend direction and magnitude for every major ratio category.

When to use:
  • Momentum-quality strategies: buy stocks with improving fundamentals (rising margins, declining leverage).

  • Turnaround detection: identify companies transitioning from weak to strong.

  • Risk monitoring: catch early signs of fundamental deterioration in existing holdings.

Mathematical formulation:

Trend direction = sign(ratio_latest - ratio_oldest) Trend magnitude = (ratio_latest - ratio_oldest) / |ratio_oldest| Trend is classified as “improving”, “deteriorating”, or “stable” based on a ±5% threshold.

Parameters:
  • symbol (str) – Ticker symbol (e.g., "AAPL").

  • periods (int, default: 5) – Number of annual periods to analyse. Default 5 gives a full business-cycle view. Use 3 for recent trajectory.

  • fmp_client (Any | None, default: None) – Optional pre-configured FMPClient instance.

Returns:

  • symbol (str) – The ticker analysed.

  • periods_analysed (int) – Actual number of periods with data.

  • roe (dict) – {"values": [...], "direction": str, "magnitude": float}. Direction is "improving", "deteriorating", or "stable".

  • roa (dict) – Same structure as ROE.

  • gross_margin (dict) – Margin trajectory.

  • operating_margin (dict) – Operating efficiency trend.

  • net_margin (dict) – Bottom-line margin trend.

  • debt_to_equity (dict) – Leverage trend. “improving” means leverage is decreasing.

  • current_ratio (dict) – Liquidity trend.

  • asset_turnover (dict) – Efficiency trend.

  • summary (str) – Overall assessment: "fundamentals improving", "fundamentals deteriorating", or "fundamentals mixed".

Return type:

dict[str, Any]

Example

>>> from wraquant.fundamental.ratios import ratio_trends
>>> trends = ratio_trends("MSFT", periods=5)
>>> print(f"ROE trend: {trends['roe']['direction']}")
>>> print(f"Summary: {trends['summary']}")

See also

ratio_comparison: Cross-sectional peer comparison. comprehensive_ratios: Point-in-time ratio snapshot.

sector_comparison(symbol, *, fmp_client=None)[source]

Compare a stock’s ratios to its sector and industry averages.

While ratio_comparison() benchmarks against specific peers, this function compares against the broader sector using FMP sector PE data and the company’s own profile metadata. This is useful for answering: “Is this stock cheap for its sector?” and “Does this company’s profitability justify a sector-premium valuation?”

When to use:
  • Sector rotation: compare company metrics to sector norms.

  • Relative valuation: determine if a premium/discount to sector is warranted by superior/inferior fundamentals.

  • Factor construction: normalise ratios by sector before ranking.

Parameters:
  • symbol (str) – Ticker symbol (e.g., "AAPL").

  • fmp_client (Any | None, default: None) – Optional pre-configured FMPClient instance.

Returns:

  • symbol (str) – The target ticker.

  • sector (str) – The company’s GICS sector.

  • industry (str) – The company’s industry classification.

  • company_ratios (dict) – The company’s key ratios.

  • sector_pe (float) – Average P/E for the sector.

  • company_pe (float) – The company’s P/E.

  • pe_premium_to_sector (float) – (company_PE - sector_PE) / sector_PE. Positive = premium; negative = discount.

  • sector_performance (float) – Current-day sector performance (% change).

  • assessment (str) – "premium to sector", "discount to sector", or "in-line with sector".

Return type:

dict[str, Any]

Example

>>> from wraquant.fundamental.ratios import sector_comparison
>>> sc = sector_comparison("AAPL")
>>> print(f"Sector: {sc['sector']}")
>>> print(f"PE premium: {sc['pe_premium_to_sector']:+.1%}")
>>> print(f"Assessment: {sc['assessment']}")

See also

ratio_comparison: Peer-level comparison with percentile ranking. relative_valuation: Full multi-metric peer comparison.

Valuation

Valuation models for intrinsic value estimation.

Provides multiple approaches to estimating the fair value of a stock:

  • Discounted Cash Flow (DCF) – Projects free cash flows and discounts them back to the present. The gold standard for absolute valuation.

  • Relative valuation – Compares multiples (P/E, EV/EBITDA, P/B) to a peer group or sector to identify relative mis-pricing.

  • Graham Number – Ben Graham’s conservative formula for intrinsic value based on earnings and book value.

  • Peter Lynch fair value – PEG-based valuation: a stock is fairly valued when P/E equals the EPS growth rate.

  • Dividend Discount Model (DDM) – Gordon growth model for stable dividend-paying stocks.

  • Residual Income Model (RIM) – Book value plus the present value of future excess earnings above the cost of equity.

All symbol-based functions call the FMP data provider. Pass an fmp_client to reuse a client across calls and avoid re-creation.

Example

>>> from wraquant.fundamental.valuation import dcf_valuation, margin_of_safety
>>> dcf = dcf_valuation("AAPL")
>>> print(f"Intrinsic value: ${dcf['intrinsic_value_per_share']:.2f}")
>>> print(f"Margin of safety: {dcf['margin_of_safety']:.1%}")

References

  • Damodaran, A. (2012). Investment Valuation, 3rd ed. Wiley.

  • Graham, B. & Dodd, D. (1934). Security Analysis. McGraw-Hill.

  • Gordon, M. J. (1959). “Dividends, Earnings, and Stock Prices.” Review of Economics and Statistics, 41(2), 99–105.

  • Ohlson, J. A. (1995). “Earnings, Book Values, and Dividends in Equity Valuation.” Contemporary Accounting Research, 11(2), 661–687.

dcf_valuation(symbol, *, growth_rate=None, discount_rate=None, terminal_growth=0.025, projection_years=5, fmp_client=None)[source]

Discounted cash flow valuation using FMP financial data.

Estimates intrinsic value by projecting free cash flows forward and discounting them to the present. If growth_rate or discount_rate are not provided, they are estimated from historical data and the company’s cost structure.

When to use:
  • Absolute valuation when you have a view on future growth.

  • Sensitivity analysis: vary growth/discount to bound fair value.

  • Combine with margin_of_safety() for investment decisions.

Mathematical formulation:
PV = sum_{t=1}^{N} FCF_0 * (1+g)^t / (1+r)^t
  • [FCF_N * (1+g_term) / (r - g_term)] / (1+r)^N

Intrinsic value per share = PV / shares outstanding

Parameters:
  • symbol (str) – Ticker symbol.

  • growth_rate (float | None, default: None) – Projected annual FCF growth rate. If None, estimated from the 3-year historical FCF CAGR. Typical range: 0.03–0.20.

  • discount_rate (float | None, default: None) – Weighted average cost of capital (WACC). If None, estimated as 10% (common equity assumption). Typical range: 0.08–0.12.

  • terminal_growth (float, default: 0.025) – Perpetual growth rate for terminal value. Must be < discount_rate. Use GDP growth rate (~2–3%) as an upper bound.

  • projection_years (int, default: 5) – Number of years to project FCF. Typical: 5–10.

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Returns:

  • intrinsic_value (float) – Total intrinsic enterprise value.

  • intrinsic_value_per_share (float) – Per-share fair value.

  • current_price (float) – Current market price.

  • margin_of_safety (float) – (intrinsic - price) / intrinsic. Positive means undervalued.

  • upside_potential (float) – (intrinsic - price) / price.

  • pv_cash_flows (float) – PV of projected FCFs.

  • pv_terminal (float) – PV of terminal value.

  • terminal_value (float) – Undiscounted terminal value.

  • terminal_pct (float) – Terminal value as % of total PV. > 75% means the valuation is highly sensitive to terminal assumptions.

  • projected_fcf (list[float]) – Year-by-year projected FCFs.

  • assumptions (dict) – Growth rate, discount rate, terminal growth used.

  • fmp_dcf (float) – FMP’s own DCF estimate for comparison.

Return type:

dict[str, Any]

Raises:

ValueError – If discount_rate <= terminal_growth.

Example

>>> from wraquant.fundamental.valuation import dcf_valuation
>>> dcf = dcf_valuation("MSFT", growth_rate=0.12, discount_rate=0.10)
>>> print(f"Fair value: ${dcf['intrinsic_value_per_share']:.2f}")
>>> print(f"Margin of safety: {dcf['margin_of_safety']:.1%}")

References

Damodaran, A. (2012). Investment Valuation, 3rd ed., Chapter 12.

See also

relative_valuation: Peer-based valuation. margin_of_safety: Stand-alone margin computation.

relative_valuation(symbol, *, peers=None, fmp_client=None)[source]

Compare valuation multiples against a peer group.

Relative valuation assumes that similar companies should trade at similar multiples. Deviations suggest over- or under-pricing relative to the peer set.

When to use:
  • When absolute valuation (DCF) is too uncertain.

  • Sector rotation strategies: buy cheap sectors, sell expensive.

  • Pair trading: long the cheap peer, short the expensive one.

Parameters:
  • symbol (str) – Target ticker symbol.

  • peers (list[str] | None, default: None) – List of peer ticker symbols. If None, the function uses FMP’s sector peers (same industry/sector). Provide explicit peers for more meaningful comparisons.

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Returns:

  • symbol (str) – The target ticker.

  • multiples (dict) – Target’s P/E, P/B, P/S, EV/EBITDA.

  • peer_medians (dict) – Median multiples of the peer group.

  • peer_means (dict) – Mean multiples of the peer group.

  • premium_discount (dict) – For each multiple, the % premium (+) or discount (-) vs. peer median. Negative = cheaper than peers.

  • peers_data (list[dict]) – Individual peer multiples.

  • verdict (str) – Summary: “undervalued”, “fairly valued”, or “overvalued” based on median premium/discount.

Return type:

dict[str, Any]

Example

>>> from wraquant.fundamental.valuation import relative_valuation
>>> rv = relative_valuation("AAPL", peers=["MSFT", "GOOG", "META"])
>>> print(f"P/E premium: {rv['premium_discount']['pe_ratio']:+.1%}")
>>> print(f"Verdict: {rv['verdict']}")

See also

dcf_valuation: Absolute valuation approach. valuation_ratios: Single-stock multiples.

graham_number(symbol, *, fmp_client=None)[source]

Compute Ben Graham’s intrinsic value number.

The Graham Number is a conservative estimate of the maximum price a defensive investor should pay. It assumes a stock should not trade above P/E of 15 and P/B of 1.5 simultaneously.

When to use:
  • Deep value screening for defensive investors.

  • Quick sanity check on valuation.

  • Pair with Piotroski F-Score: high F-Score + price < Graham Number is a classic value strategy.

Mathematical formulation:

Graham Number = sqrt(22.5 * EPS * BVPS)

where 22.5 = 15 (max P/E) * 1.5 (max P/B)

Parameters:
  • symbol (str) – Ticker symbol.

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Returns:

  • graham_number (float) – Intrinsic value estimate. Only meaningful when EPS > 0 and BVPS > 0.

  • current_price (float) – Current market price.

  • margin_of_safety (float) – (graham - price) / graham.

  • eps (float) – Earnings per share used.

  • bvps (float) – Book value per share used.

Return type:

dict[str, float]

Example

>>> from wraquant.fundamental.valuation import graham_number
>>> gn = graham_number("JNJ")
>>> print(f"Graham Number: ${gn['graham_number']:.2f}")
>>> print(f"Current price: ${gn['current_price']:.2f}")

References

Graham, B. (1973). The Intelligent Investor, Revised ed., Chapter 14.

See also

peter_lynch_value: Growth-oriented fair value. dcf_valuation: More sophisticated intrinsic value.

peter_lynch_value(symbol, *, fmp_client=None)[source]

Compute fair value using Peter Lynch’s PEG-based methodology.

Peter Lynch argued that a fairly valued growth stock should have a P/E ratio roughly equal to its earnings growth rate. PEG < 1 suggests undervaluation; PEG > 2 suggests overvaluation.

When to use:
  • Growth stock screening.

  • Quick check on whether you’re overpaying for growth.

  • Combine with growth_ratios() for context on growth sustainability.

Mathematical formulation:

PEG = P/E / (EPS Growth Rate * 100) Fair Value = EPS * EPS Growth Rate * 100

Parameters:
  • symbol (str) – Ticker symbol.

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Returns:

  • fair_value (float) – Lynch fair value per share.

  • current_price (float) – Current market price.

  • peg_ratio (float) – PEG ratio.

  • pe_ratio (float) – Current P/E.

  • eps_growth_rate (float) – EPS growth rate used (decimal).

  • margin_of_safety (float) – (fair - price) / fair.

  • lynch_category (str) – “undervalued” (PEG < 1), “fairly valued” (1–2), or “overvalued” (> 2).

Return type:

dict[str, float | str]

Example

>>> from wraquant.fundamental.valuation import peter_lynch_value
>>> plv = peter_lynch_value("NVDA")
>>> print(f"PEG: {plv['peg_ratio']:.2f}")
>>> print(f"Lynch category: {plv['lynch_category']}")

References

Lynch, P. (1989). One Up on Wall Street. Simon & Schuster.

See also

graham_number: Conservative value approach. valuation_ratios: Raw multiples.

dividend_discount_model(symbol, *, required_return=0.1, fmp_client=None)[source]

Gordon Growth Model (single-stage DDM) valuation.

Values a stock as the present value of all future dividends growing at a constant rate in perpetuity. Only suitable for mature, stable-dividend companies (utilities, consumer staples, REITs).

When to use:
  • Value dividend aristocrats and other stable payers.

  • Income-focused portfolio construction.

  • Not suitable for non-dividend or high-growth companies.

Mathematical formulation:

V_0 = D_1 / (r - g)

where: D_1 = next year’s expected dividend = D_0 * (1 + g) r = required return (cost of equity) g = dividend growth rate (must be < r)

Parameters:
  • symbol (str) – Ticker symbol.

  • required_return (float, default: 0.1) – Required rate of return / cost of equity. Typical range: 0.08–0.12. Use CAPM or build-up method to estimate.

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Returns:

  • intrinsic_value (float) – DDM fair value per share.

  • current_price (float) – Current market price.

  • margin_of_safety (float) – (intrinsic - price) / intrinsic.

  • dividend_per_share (float) – Current annual DPS.

  • dividend_growth_rate (float) – Estimated growth rate.

  • dividend_yield (float) – Current dividend yield.

  • implied_return (float) – Yield + growth (implied total return at current price).

  • model_applicable (str) – “yes” if the company pays dividends and growth < required return; “no” otherwise.

Return type:

dict[str, float | str]

Example

>>> from wraquant.fundamental.valuation import dividend_discount_model
>>> ddm = dividend_discount_model("KO", required_return=0.09)
>>> print(f"DDM value: ${ddm['intrinsic_value']:.2f}")
>>> print(f"Implied return: {ddm['implied_return']:.2%}")

References

Gordon, M. J. (1959). “Dividends, Earnings, and Stock Prices.” Review of Economics and Statistics, 41(2), 99–105.

See also

dcf_valuation: FCF-based valuation (works for non-payers). residual_income_model: Book-value-based alternative.

residual_income_model(symbol, *, cost_of_equity=0.1, projection_years=5, fade_rate=0.2, fmp_client=None)[source]

Residual income (abnormal earnings) valuation model.

Values a stock as its book value plus the present value of future residual income (earnings in excess of the cost of equity). Unlike DCF, this model anchors on book value and is less sensitive to terminal value assumptions.

When to use:
  • Companies with stable book values (financials, industrials).

  • When terminal value dominates DCF (RIM reduces this problem).

  • Academic factor research: book value is the anchor.

Mathematical formulation:

V_0 = BV_0 + sum_{t=1}^{T} RI_t / (1 + r_e)^t + TV

RI_t = NI_t - r_e * BV_{t-1} (residual income)

TV = RI_T * (1 - fade) / (r_e - g_ri * (1 - fade))

Parameters:
  • symbol (str) – Ticker symbol.

  • cost_of_equity (float, default: 0.1) – Required return on equity. Use CAPM: r_e = r_f + beta * (r_m - r_f). Typical: 0.08–0.14.

  • projection_years (int, default: 5) – Years of explicit RI projection.

  • fade_rate (float, default: 0.2) – Annual rate at which residual income fades toward zero. Higher fade = more conservative. 0.0 = no fade (perpetual excess returns). 0.20 = industry reversion.

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Returns:

  • intrinsic_value (float) – RIM fair value per share.

  • current_price (float) – Current market price.

  • margin_of_safety (float) – (intrinsic - price) / intrinsic.

  • book_value_per_share (float) – Current BVPS.

  • current_roe (float) – Current ROE.

  • residual_income (float) – Most recent period RI.

  • pv_residual_income (float) – PV of projected RI stream.

  • pv_terminal (float) – PV of terminal RI.

  • excess_return_spread (float) – ROE - cost of equity. Positive means the company creates value.

Return type:

dict[str, Any]

Example

>>> from wraquant.fundamental.valuation import residual_income_model
>>> rim = residual_income_model("JPM", cost_of_equity=0.11)
>>> print(f"RIM value: ${rim['intrinsic_value']:.2f}")
>>> print(f"Excess spread: {rim['excess_return_spread']:.2%}")

References

Ohlson, J. A. (1995). “Earnings, Book Values, and Dividends in Equity Valuation.” Contemporary Accounting Research, 11(2), 661–687.

See also

dcf_valuation: Cash-flow-based alternative. graham_number: Simpler book-value-based approach.

margin_of_safety(symbol=None, intrinsic_value=0.0, current_price=0.0, *, fmp_client=None)[source]

Compute the margin of safety between intrinsic value and market price.

The margin of safety is the central concept of value investing. Graham recommended buying only when the market price is significantly below intrinsic value to protect against estimation errors.

Mathematical formulation:

Margin of Safety = (Intrinsic Value - Market Price) / Intrinsic Value

When to use:
  • After computing intrinsic value via DCF, Graham Number, etc.

  • Graham recommended a minimum 33% margin of safety.

  • Negative margin means the stock trades above estimated fair value.

Parameters:
  • symbol (str | None, default: None) – Optional ticker symbol. If provided with no current_price, the current market price is fetched.

  • intrinsic_value (float, default: 0.0) – Your estimated fair value per share.

  • current_price (float, default: 0.0) – Current market price. If 0.0 and symbol is provided, fetched from FMP.

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Return type:

float

Returns:

Margin of safety as a float (e.g., 0.30 = 30% discount to intrinsic value). Negative means premium to intrinsic.

Example

>>> from wraquant.fundamental.valuation import margin_of_safety
>>> mos = margin_of_safety(intrinsic_value=150.0, current_price=100.0)
>>> print(f"Margin of safety: {mos:.1%}")
Margin of safety: 33.3%

See also

dcf_valuation: Compute intrinsic value. graham_number: Conservative intrinsic value.

piotroski_f_score(financials)[source]

Compute the Piotroski F-Score (0–9) for financial health.

The Piotroski F-Score is a composite score of nine binary tests that evaluate profitability, leverage/liquidity, and operating efficiency. Stocks scoring 8–9 are considered financially strong; scores of 0–2 indicate financial distress.

When to use:
  • Screen value stocks (low P/B) for financial health.

  • Avoid value traps: low P/B stocks with low F-Scores tend to underperform.

  • Long/short strategy: long high F-Score value stocks, short low F-Score value stocks.

The nine binary tests:

Profitability (4 points):
  1. ROA > 0 (net_income / total_assets > 0)

  2. Operating cash flow > 0

  3. ROA increased vs. prior year

  4. Cash flow from operations > net income (accruals quality)

Leverage & liquidity (3 points):
  1. Long-term debt decreased vs. prior year

  2. Current ratio increased vs. prior year

  3. No new shares issued (shares outstanding unchanged or decreased)

Operating efficiency (2 points):
  1. Gross margin increased vs. prior year

  2. Asset turnover increased vs. prior year

Parameters:

financials (dict[str, float]) –

Dictionary with the following keys:

  • net_income: Current year net income.

  • prev_net_income: Prior year net income.

  • operating_cash_flow: Current year operating cash flow.

  • total_assets: Current year total assets.

  • prev_total_assets: Prior year total assets.

  • long_term_debt: Current year long-term debt.

  • prev_long_term_debt: Prior year long-term debt.

  • current_ratio: Current year current ratio.

  • prev_current_ratio: Prior year current ratio.

  • shares_outstanding: Current year shares outstanding.

  • prev_shares_outstanding: Prior year shares outstanding.

  • gross_margin: Current year gross margin.

  • prev_gross_margin: Prior year gross margin.

  • asset_turnover: Current year asset turnover.

  • prev_asset_turnover: Prior year asset turnover.

Return type:

int

Returns:

Integer score from 0 to 9.

Example

>>> financials = {
...     "net_income": 1e6, "prev_net_income": 8e5,
...     "operating_cash_flow": 1.2e6, "total_assets": 5e6,
...     "prev_total_assets": 4.8e6, "long_term_debt": 1e6,
...     "prev_long_term_debt": 1.1e6, "current_ratio": 1.5,
...     "prev_current_ratio": 1.3, "shares_outstanding": 1e6,
...     "prev_shares_outstanding": 1e6, "gross_margin": 0.4,
...     "prev_gross_margin": 0.38, "asset_turnover": 0.8,
...     "prev_asset_turnover": 0.75,
... }
>>> piotroski_f_score(financials)
9

References

Piotroski, J. D. (2000). “Value Investing: The Use of Historical Financial Statement Information to Separate Winners from Losers.” Journal of Accounting Research, 38, 1–41.

See also

dcf_valuation: Intrinsic value estimation. financial_health_score: FMP-powered composite score.

quality_screen(stocks_df, metrics=None)[source]

Rank stocks by a composite quality score.

Computes a composite quality score by ranking each stock on multiple fundamental metrics and averaging the percentile ranks. Higher composite scores indicate higher quality.

When to use:
  • Construct a quality factor for multi-factor models.

  • Screen a universe for high-quality long candidates.

  • Complement value screening (avoid value traps by requiring quality).

Parameters:
  • stocks_df (Any) – DataFrame where each row is a stock and columns contain fundamental metrics. Missing values are handled by assigning median rank.

  • metrics (list[str] | None, default: None) – List of column names to include in the composite. If None, defaults to ["roe", "operating_margin", "current_ratio"] (using only columns that exist in the DataFrame).

Return type:

Any

Returns:

DataFrame with the original data plus a quality_score column (0 to 1, higher is better) and a quality_rank column (1 = best), sorted by quality_score descending.

Example

>>> import pandas as pd
>>> stocks = pd.DataFrame({
...     "ticker": ["AAPL", "MSFT", "GOOG"],
...     "roe": [0.25, 0.30, 0.20],
...     "operating_margin": [0.30, 0.35, 0.25],
...     "current_ratio": [1.5, 2.0, 3.0],
... }).set_index("ticker")
>>> result = quality_screen(stocks)
>>> result["quality_rank"].iloc[0]
1

See also

piotroski_f_score: Financial health assessment (single stock). custom_screen: Flexible screening with arbitrary criteria.

Financial Statements

Financial statement analysis using FMP data.

Provides deep analytical functions that go beyond raw financial statements to deliver trend analysis, growth decomposition, health scoring, and earnings quality assessment. These are the tools a fundamental analyst reaches for after reading the 10-K: they answer “what happened?”, “is it getting better?”, and “can I trust the numbers?”

Functions in this module compute derived analytics from the three core financial statements (income statement, balance sheet, cash flow statement). All data is fetched from the FMP (Financial Modeling Prep) API.

Key capabilities:

  1. Income analysis – Revenue and margin trends, growth rates, and operating leverage across multiple periods.

  2. Balance sheet analysis – Asset composition, leverage evolution, book value trends, and working capital dynamics.

  3. Cash flow analysis – Free cash flow generation, cash conversion efficiency, and CapEx intensity.

  4. Financial health score – Composite 0–100 score aggregating profitability, liquidity, solvency, and efficiency into a single grade (A–F).

  5. Earnings quality – Accruals analysis and cash conversion to detect potential earnings manipulation.

  6. Common-size analysis – Vertical analysis expressing every line item as a percentage of revenue (income statement) or total assets (balance sheet).

Example

>>> from wraquant.fundamental.financials import income_analysis
>>> result = income_analysis("AAPL", period="annual")
>>> print(f"Revenue CAGR (3Y): {result['revenue_cagr_3y']:.1%}")
>>> print(f"Margin trend: {result['margin_trend']}")

References

  • Sloan, R. G. (1996). “Do Stock Prices Fully Reflect Information in Accruals and Cash Flows about Future Earnings?” The Accounting Review, 71(3), 289–315.

  • Dechow, P. M. & Dichev, I. D. (2002). “The Quality of Accruals and Earnings.” The Accounting Review, 77(s-1), 35–59.

  • Beneish, M. D. (1999). “The Detection of Earnings Manipulation.” Financial Analysts Journal, 55(5), 24–36.

  • Piotroski, J. D. (2000). “Value Investing: The Use of Historical Financial Statement Information to Separate Winners from Losers.” Journal of Accounting Research, 38, 1–41.

  • Palepu, K. G. & Healy, P. M. (2013). Business Analysis and Valuation, 5th edition. Cengage.

income_analysis(symbol, period='annual', *, fmp_client=None)[source]

Analyse income statement trends over multiple periods.

Goes beyond single-period ratios to reveal the trajectory of revenue, margins, and bottom-line profitability. Use this when you need to understand whether a company’s earning power is structurally improving, temporarily inflated, or in secular decline. A company with a 20 % margin that is declining is very different from one with a 15 % margin that is expanding.

This function is the starting point for fundamental stock analysis: it answers “is the business growing?” and “are margins expanding or compressing?”

Parameters:
  • symbol (str) – Ticker symbol (e.g., "AAPL").

  • period (str, default: 'annual') – "annual" or "quarter". Annual data smooths out seasonality; quarterly reveals recent momentum.

  • fmp_client (Any | None, default: None) – Optional pre-configured FMPClient instance. If None, a default client is created using the FMP_API_KEY environment variable.

Returns:

Time-series data (most recent first): - revenue (list[float]) – Revenue by period. - revenue_growth (list[float]) – YoY revenue growth rates. - gross_margin (list[float]) – Gross profit / revenue per period. - operating_margin (list[float]) – Operating income / revenue. - net_margin (list[float]) – Net income / revenue. - ebitda_margin (list[float]) – EBITDA / revenue. - eps (list[float]) – Diluted EPS by period. - dates (list[str]) – Reporting period dates.

Trend analysis: - margin_trend (str) – "expanding", "contracting",

or "stable" based on operating margin trajectory.

Growth rates: - revenue_cagr_3y (float) – 3-year revenue CAGR. > 10 %

is strong organic growth; negative signals secular decline.

  • revenue_cagr_5y (float) – 5-year revenue CAGR.

Metadata: - periods_analysed (int) – Number of periods returned.

Return type:

dict[str, Any]

Example

>>> from wraquant.fundamental.financials import income_analysis
>>> inc = income_analysis("MSFT")
>>> print(f"Revenue CAGR (3Y): {inc['revenue_cagr_3y']:.1%}")
>>> print(f"Margin trend: {inc['margin_trend']}")

See also

balance_sheet_analysis: Asset/liability composition trends. cash_flow_analysis: Cash flow quality and FCF trends. common_size_analysis: Line items as % of revenue.

balance_sheet_analysis(symbol, period='annual', *, fmp_client=None)[source]

Analyse balance sheet composition, leverage, and capital structure.

Reveals the structure of assets (tangible vs. intangible, current vs. long-term), the financing mix (debt vs. equity), and how these have evolved. Use this for:

  • Credit analysis: Is the company over-leveraged? Is the debt- to-equity ratio trending upward?

  • Equity screening: Is book value growing? What fraction of assets is goodwill from acquisitions?

  • Factor investing: Value (P/B), investment (asset growth).

  • Distress prediction: Working capital and liquidity trends.

Parameters:
  • symbol (str) – Ticker symbol (e.g., "AAPL").

  • period (str, default: 'annual') – "annual" or "quarter".

  • fmp_client (Any | None, default: None) – Optional pre-configured FMPClient instance. If None, a default client is created.

Returns:

Time-series data (most recent first): - total_assets (list[float]) – Total assets by period. - total_equity (list[float]) – Stockholders’ equity by period. - total_debt (list[float]) – Total debt by period. - cash (list[float]) – Cash & equivalents by period. - net_debt (list[float]) – Debt minus cash by period.

Negative means the company has more cash than debt.

  • debt_to_equity (list[float]) – D/E ratio by period. > 2.0 is high leverage for most industries.

  • debt_to_assets (list[float]) – Debt ratio by period.

  • current_ratio (list[float]) – Current ratio by period. < 1.0 is a liquidity warning.

  • equity_pct (list[float]) – Equity as % of total assets.

  • intangible_pct (list[float]) – (Intangibles + goodwill) / total assets. > 50 % means most “assets” are goodwill from acquisitions – a risk in downturns.

  • book_value_per_share (list[float]) – BVPS by period.

  • tangible_bvps (list[float]) – BVPS excluding intangibles.

  • dates (list[str]) – Period end dates.

Trend analysis: - leverage_trend (str) – "increasing", "decreasing",

or "stable" based on D/E ratio trajectory.

Return type:

dict[str, Any]

Example

>>> from wraquant.fundamental.financials import balance_sheet_analysis
>>> bs = balance_sheet_analysis("AAPL")
>>> print(f"Net debt: ${bs['net_debt'][0]:,.0f}")
>>> print(f"Leverage trend: {bs['leverage_trend']}")

See also

income_analysis: Revenue and margin trends. cash_flow_analysis: Cash flow quality and FCF trends. financial_health_score: Composite assessment.

cash_flow_analysis(symbol, period='annual', *, fmp_client=None)[source]

Analyse cash flow statement trends and free cash flow quality.

Cash flow analysis reveals whether reported earnings are backed by real cash generation. A company can report growing profits while hemorrhaging cash – this analysis catches that.

The key metric is free cash flow (FCF) = operating cash flow minus capital expenditures. FCF is what is actually available for dividends, buybacks, debt reduction, and reinvestment.

Use this function to: - Verify that reported earnings translate into actual cash. - Assess CapEx requirements and how much free cash flow remains. - Track whether the company is self-funding or reliant on external

capital.

  • Compare cash returned to shareholders vs. cash generated.

Parameters:
  • symbol (str) – Ticker symbol (e.g., "AAPL").

  • period (str, default: 'annual') – "annual" or "quarter".

  • fmp_client (Any | None, default: None) – Optional pre-configured FMPClient instance. If None, a default client is created.

Returns:

Time-series data (most recent first): - operating_cash_flow (list[float]) – OCF by period.

Should consistently exceed net income for healthy companies.

  • capital_expenditures (list[float]) – CapEx by period (negative = spending).

  • free_cash_flow (list[float]) – FCF by period.

  • fcf_margin (list[float]) – FCF / revenue by period. > 10 % is strong; indicates each revenue dollar generates substantial free cash.

  • fcf_growth (list[float]) – YoY FCF growth rates.

  • cash_conversion (list[float]) – OCF / net income. > 1.0 means cash earnings exceed accounting earnings – a sign of high earnings quality.

  • capex_to_revenue (list[float]) – |CapEx| / revenue. > 15 % indicates capital-intensive business.

  • capex_to_ocf (list[float]) – |CapEx| / OCF. > 50 % means heavy reinvestment requirements.

  • dividends_paid (list[float]) – Absolute dividends paid.

  • buybacks (list[float]) – Absolute share repurchases.

  • total_shareholder_return (list[float]) – Dividends + buybacks.

  • fcf_payout_ratio (list[float]) – (Dividends + buybacks) / FCF. > 1.0 means the company is returning more than it generates.

  • dates (list[str]) – Period end dates.

Point estimates: - fcf_yield (float) – FCF / market cap (most recent).

> 5 % is typically attractive for value investors.

Return type:

dict[str, Any]

Example

>>> from wraquant.fundamental.financials import cash_flow_analysis
>>> cf = cash_flow_analysis("MSFT")
>>> print(f"FCF margin: {cf['fcf_margin'][0]:.1%}")
>>> print(f"Cash conversion: {cf['cash_conversion'][0]:.2f}x")

References

Sloan, R. G. (1996). “Do Stock Prices Fully Reflect Information in Accruals and Cash Flows about Future Earnings?” The Accounting Review, 71(3), 289–315.

See also

earnings_quality: Detailed accruals analysis. income_analysis: Margin trends for context.

financial_health_score(symbol, *, fmp_client=None)[source]

Compute a composite financial health score (0–100) with letter grade.

Aggregates profitability, liquidity, leverage, efficiency, and cash flow quality into a single score. This is a modernised, continuous version of the binary Piotroski F-Score – it captures how much better or worse a metric is, not just whether it passes a threshold.

Use this for: - Screening universes: Filter out financially distressed companies

before building factor portfolios.

  • Risk management: Flag holdings whose fundamentals are deteriorating.

  • Quick triage: Rapidly assess health before deep-diving into individual statements.

The score is computed as a weighted average of five sub-scores:
  1. Profitability (30 pts): ROE, ROA, operating margin, net margin.

  2. Liquidity (15 pts): Current ratio, quick ratio.

  3. Leverage (20 pts): D/E ratio, interest coverage.

  4. Efficiency (15 pts): Asset turnover, positive NI.

  5. Cash flow quality (20 pts): FCF margin, cash conversion.

Grading scale:

A (80–100 “excellent”), B (60–79 “good”), C (40–59 “fair”), D (20–39 “weak”), F (0–19 “critical”).

Parameters:
  • symbol (str) – Ticker symbol (e.g., "AAPL").

  • fmp_client (Any | None, default: None) – Optional pre-configured FMPClient instance. If None, a default client is created.

Returns:

  • total_score (float) – Composite score 0–100.

  • grade (str) – Letter grade: "A" through "F".

  • category (str) – "excellent", "good", "fair", "weak", or "critical".

  • profitability_score (float) – Sub-score out of 30.

  • liquidity_score (float) – Sub-score out of 15.

  • leverage_score (float) – Sub-score out of 20.

  • efficiency_score (float) – Sub-score out of 15.

  • cash_flow_score (float) – Sub-score out of 20.

  • strengths (list[str]) – Top-performing areas.

  • weaknesses (list[str]) – Areas of concern.

  • piotroski_f_score (int) – Traditional F-Score (0–9) for reference.

  • symbol (str) – The ticker analysed.

Return type:

dict[str, Any]

Example

>>> from wraquant.fundamental.financials import financial_health_score
>>> health = financial_health_score("MSFT")
>>> print(f"Score: {health['total_score']:.0f}/100 ({health['grade']})")
>>> print(f"Strengths: {', '.join(health['strengths'])}")

See also

earnings_quality: Deep dive into earnings reliability. comprehensive_ratios: All ratios in one call.

earnings_quality(symbol, *, fmp_client=None)[source]

Assess the quality and sustainability of reported earnings.

High-quality earnings are cash-backed, persistent, and free of accounting manipulation. Low-quality earnings are driven by accruals, non-recurring items, or aggressive accounting.

This function computes multiple earnings quality metrics from the academic literature on earnings management and accruals. Use it as a quality filter in stock selection: prefer companies with low accruals and high cash conversion.

Key metrics:

  • Accruals ratio: Total accruals / average total assets. High accruals (> 10 % of assets) predict future earnings reversals (Sloan, 1996). This is the single most powerful quality signal.

  • Cash conversion: OCF / net income. Should be > 1.0 for healthy companies. Consistently < 0.7 is a red flag.

  • Earnings persistence: Autocorrelation of earnings across periods. High persistence (> 0.7) = sustainable earnings.

Mathematical formulations:

Accruals = Net Income - Operating Cash Flow Accruals Ratio = Accruals / Average Total Assets Cash Conversion Ratio = Operating CF / Net Income FCF to Net Income = Free Cash Flow / Net Income

Parameters:
  • symbol (str) – Ticker symbol (e.g., "AAPL").

  • fmp_client (Any | None, default: None) – Optional pre-configured FMPClient instance. If None, a default client is created.

Returns:

  • accruals_ratio (float) – Accruals / avg total assets. < 5 % is high quality; > 10 % is a red flag.

  • cash_conversion_ratio (float) – OCF / net income. > 1.0 means earnings are backed by cash.

  • earnings_persistence (float) – Autocorrelation of NI. > 0.7 = stable; < 0.3 = volatile.

  • fcf_to_net_income (float) – FCF / net income. > 0.8 is strong; < 0.5 means heavy CapEx eats into earnings.

  • quality_grade (str) – "A" (excellent) through "F" (manipulated/unreliable).

  • accruals_trend (list[float]) – Accruals ratio history (most recent first).

  • red_flags (list[str]) – Specific concerns identified.

  • periods_analysed (int) – Number of periods used.

  • symbol (str) – The ticker analysed.

Return type:

dict[str, Any]

Example

>>> from wraquant.fundamental.financials import earnings_quality
>>> eq = earnings_quality("AAPL")
>>> print(f"Quality grade: {eq['quality_grade']}")
>>> print(f"Accruals ratio: {eq['accruals_ratio']:.2%}")
>>> if eq['red_flags']:
...     print(f"Warnings: {', '.join(eq['red_flags'])}")

Notes

Reference: Sloan, R. G. (1996). “Do Stock Prices Fully Reflect Information in Accruals and Cash Flows about Future Earnings?” The Accounting Review, 71(3), 289–315.

See also

cash_flow_analysis: Detailed cash flow trends. financial_health_score: Composite score.

common_size_analysis(symbol, period='annual', *, fmp_client=None)[source]

Generate a common-size DataFrame combining income and balance sheet.

Common-size analysis expresses each line item as a percentage of a base figure: revenue for the income statement, total assets for the balance sheet. This normalisation makes it easy to:

  • Compare companies of different sizes on an apples-to-apples basis.

  • Track composition changes over time (e.g., is R&D spending growing as a share of revenue?).

  • Benchmark against sector medians to spot outliers.

When to use:

Use common-size analysis as input to peer-relative valuation and industry benchmarking. It is also a prerequisite for detecting structural shifts in cost structure or asset mix across reporting periods.

Parameters:
  • symbol (str) – Ticker symbol (e.g., "AAPL").

  • period (str, default: 'annual') – "annual" or "quarter".

  • fmp_client (Any | None, default: None) – Optional pre-configured FMPClient instance. If None, a default client is created.

Return type:

DataFrame

Returns:

DataFrame with one row per reporting period and columns for each line item expressed as a ratio (0–1 scale):

Income statement (% of revenue): - date (str) – Reporting period date. - cost_of_revenue_pct (float) – COGS / revenue. - gross_profit_pct (float) – Gross profit / revenue. - rd_pct (float) – R&D expense / revenue. - sga_pct (float) – SG&A expense / revenue. - operating_income_pct (float) – Operating income / revenue. - interest_expense_pct (float) – |Interest| / revenue. - income_tax_pct (float) – Income tax / revenue. - net_income_pct (float) – Net income / revenue. - ebitda_pct (float) – EBITDA / revenue.

Balance sheet (% of total assets): - current_assets_pct (float) – Current assets / total assets. - cash_pct (float) – Cash / total assets. - receivables_pct (float) – Net receivables / total assets. - inventory_pct (float) – Inventory / total assets. - fixed_assets_pct (float) – PP&E / total assets. - intangibles_pct (float) – Intangible assets / total assets. - goodwill_pct (float) – Goodwill / total assets. - current_liabilities_pct (float) – Current liabilities /

total assets.

  • long_term_debt_pct (float) – Long-term debt / total assets.

  • total_debt_pct (float) – Total debt / total assets.

  • equity_pct (float) – Equity / total assets.

  • retained_earnings_pct (float) – Retained earnings / total assets.

Example

>>> from wraquant.fundamental.financials import common_size_analysis
>>> cs = common_size_analysis("AAPL")
>>> print(cs[["date", "gross_profit_pct", "rd_pct",
...           "operating_income_pct"]].head())

See also

income_analysis: Absolute values and growth rates. balance_sheet_analysis: Composition and leverage trends.

revenue_decomposition(symbol, *, fmp_client=None)[source]

Break down revenue by product segment and geographic region.

Understanding where revenue comes from is essential for assessing concentration risk, growth drivers, and geographic diversification. A company with 80 % of revenue from one product is far riskier than one with balanced segment mix. Similarly, heavy geographic concentration creates FX and geopolitical risk.

When to use:
  • Identify revenue concentration risk (single product/region).

  • Assess growth drivers: which segments are accelerating?

  • Geographic diversification analysis for risk management.

  • Input to sum_of_parts_valuation() for SOTP analysis.

Parameters:
  • symbol (str) – Ticker symbol (e.g., "AAPL").

  • fmp_client (Any | None, default: None) – Optional pre-configured FMPClient instance.

Returns:

  • symbol (str) – The ticker analysed.

  • product_segments (list[dict]) – Per-product breakdown with name, revenue, pct_of_total. Sorted by revenue descending.

  • geographic_segments (list[dict]) – Per-region breakdown with name, revenue, pct_of_total.

  • total_revenue (float) – Total revenue for reference.

  • concentration_risk (str) – "high" if top segment > 60 % of total, "moderate" if > 40 %, "low" otherwise.

  • top_product_pct (float) – Largest product segment as fraction of total revenue.

  • top_geo_pct (float) – Largest geographic region as fraction of total revenue.

Return type:

dict[str, Any]

Example

>>> from wraquant.fundamental.financials import revenue_decomposition
>>> rd = revenue_decomposition("AAPL")
>>> for seg in rd["product_segments"]:
...     print(f"{seg['name']}: {seg['pct_of_total']:.1%}")
>>> print(f"Concentration risk: {rd['concentration_risk']}")

See also

income_analysis: Revenue trends over time. common_size_analysis: Line items as % of revenue.

working_capital_analysis(symbol, *, periods=5, fmp_client=None)[source]

Analyse working capital efficiency and cash conversion cycle trends.

Working capital management directly impacts free cash flow. A company that collects receivables faster, turns inventory quicker, and delays payables generates more cash from the same level of sales. The Cash Conversion Cycle (CCC) captures all three dynamics in one number.

Deteriorating working capital (rising CCC) is an early warning of operational problems – even before it shows up in earnings.

When to use:
  • Cash flow quality assessment: rising DSO may signal aggressive revenue recognition.

  • Operational efficiency benchmarking vs. peers.

  • Early warning system: deteriorating CCC often precedes earnings misses.

  • Complement to earnings_quality() for detecting manipulation.

Mathematical formulations:

DSO = Accounts Receivable / (Revenue / 365) DIO = Inventory / (COGS / 365) DPO = Accounts Payable / (COGS / 365) CCC = DSO + DIO - DPO

Parameters:
  • symbol (str) – Ticker symbol (e.g., "WMT").

  • periods (int, default: 5) – Number of annual periods to analyse for trend detection.

  • fmp_client (Any | None, default: None) – Optional pre-configured FMPClient instance.

Returns:

  • symbol (str) – The ticker analysed.

  • periods_analysed (int) – Actual periods with data.

  • dso (list[float]) – Days Sales Outstanding by period (most recent first). Rising DSO = slower collections.

  • dio (list[float]) – Days Inventory Outstanding by period. Rising DIO = inventory building up (demand problem?).

  • dpo (list[float]) – Days Payable Outstanding by period. Rising DPO = stretching supplier payments.

  • ccc (list[float]) – Cash Conversion Cycle by period. Negative CCC (like Amazon) = funded by suppliers.

  • working_capital (list[float]) – Net working capital (current assets - current liabilities) by period.

  • wc_to_revenue (list[float]) – Working capital as % of revenue. Rising ratio = more capital tied up.

  • ccc_trend (str) – "improving" (CCC declining), "deteriorating" (rising), or "stable".

  • dates (list[str]) – Period end dates.

Return type:

dict[str, Any]

Example

>>> from wraquant.fundamental.financials import working_capital_analysis
>>> wc = working_capital_analysis("WMT")
>>> print(f"CCC: {wc['ccc'][0]:.0f} days (trend: {wc['ccc_trend']})")
>>> print(f"DSO: {wc['dso'][0]:.0f} days")

References

Richards, V. D. & Laughlin, E. J. (1980). “A Cash Conversion Cycle Approach to Liquidity Analysis.” Financial Management, 9(1), 32–38.

See also

efficiency_ratios: Point-in-time turnover ratios. cash_flow_analysis: Broader cash flow trends.

capex_analysis(symbol, *, periods=5, fmp_client=None)[source]

Analyse capital expenditure intensity, maintenance vs. growth split.

Not all CapEx is created equal. Maintenance CapEx merely sustains the existing asset base (roughly equal to depreciation). Growth CapEx expands productive capacity and drives future revenue. Understanding this split is crucial for:

  • True free cash flow: FCF = OCF - Maintenance CapEx (not total CapEx).

  • Growth assessment: high growth CapEx signals management confidence.

  • Capital intensity: CapEx/Revenue reveals how capital-hungry the business model is.

When to use:
  • Distinguish between asset-light (SaaS) and capital-heavy (industrials, utilities) business models.

  • Estimate “owner earnings” (Buffett): NI + D&A - Maintenance CapEx.

  • Identify companies investing aggressively for future growth.

  • Input to valuation: only maintenance CapEx should be deducted in a “normalised FCF” model.

Mathematical formulations:

Maintenance CapEx ≈ Depreciation & Amortisation Growth CapEx = Total CapEx - Maintenance CapEx CapEx Intensity = |CapEx| / Revenue CapEx / OCF = |CapEx| / Operating Cash Flow Owner Earnings = Net Income + D&A - Maintenance CapEx

Parameters:
  • symbol (str) – Ticker symbol (e.g., "AAPL").

  • periods (int, default: 5) – Number of annual periods to analyse.

  • fmp_client (Any | None, default: None) – Optional pre-configured FMPClient instance.

Returns:

  • symbol (str) – The ticker analysed.

  • periods_analysed (int) – Actual periods with data.

  • total_capex (list[float]) – Total CapEx by period (negative = spending).

  • depreciation (list[float]) – D&A by period (proxy for maintenance CapEx).

  • maintenance_capex (list[float]) – Estimated maintenance CapEx (≈ D&A).

  • growth_capex (list[float]) – Total CapEx minus maintenance. Positive = investing for growth.

  • capex_to_revenue (list[float]) – |CapEx| / revenue. > 15 % = capital-intensive; < 5 % = asset-light.

  • capex_to_ocf (list[float]) – |CapEx| / OCF. > 80 % leaves little FCF.

  • growth_capex_pct (list[float]) – Growth CapEx as % of total CapEx.

  • owner_earnings (list[float]) – NI + D&A - maintenance CapEx (Buffett’s preferred measure).

  • capex_trend (str) – "increasing", "decreasing", or "stable" based on CapEx/Revenue trajectory.

  • dates (list[str]) – Period end dates.

Return type:

dict[str, Any]

Example

>>> from wraquant.fundamental.financials import capex_analysis
>>> ca = capex_analysis("AMZN")
>>> print(f"CapEx intensity: {ca['capex_to_revenue'][0]:.1%}")
>>> print(f"Growth CapEx %: {ca['growth_capex_pct'][0]:.1%}")
>>> print(f"Owner earnings: ${ca['owner_earnings'][0]:,.0f}")

References

Buffett, W. (1986). Berkshire Hathaway Shareholder Letter (“owner earnings” concept).

See also

cash_flow_analysis: Broader cash flow metrics. shareholder_returns: How CapEx competes with buybacks/dividends.

shareholder_returns(symbol, *, periods=5, fmp_client=None)[source]

Analyse total shareholder yield: dividends + buybacks + debt reduction.

Total shareholder yield captures all cash returned to shareholders, not just dividends. In the modern market, share buybacks often exceed dividends by a wide margin (e.g., Apple returns 3-4x more via buybacks than dividends). Focusing only on dividend yield misses half the story.

When to use:
  • Income-focused investing: total yield (dividends + buybacks) is a better income proxy than dividend yield alone.

  • Sustainability analysis: is the company returning more cash than it generates? Payout ratio > 100 % is unsustainable.

  • Shareholder-friendly management: track trends in capital allocation policy.

  • Factor construction: total yield is a more predictive value signal than dividend yield.

Mathematical formulations:

Total Yield = (Dividends + Buybacks) / Market Cap Payout Ratio = (Dividends + Buybacks) / Net Income FCF Payout Ratio = (Dividends + Buybacks) / FCF Buyback Yield = Net Buybacks / Market Cap Dividend Yield = Dividends / Market Cap

Parameters:
  • symbol (str) – Ticker symbol (e.g., "AAPL").

  • periods (int, default: 5) – Number of annual periods to analyse.

  • fmp_client (Any | None, default: None) – Optional pre-configured FMPClient instance.

Returns:

  • symbol (str) – The ticker analysed.

  • periods_analysed (int) – Actual periods with data.

  • dividends_paid (list[float]) – Absolute dividends by period.

  • buybacks (list[float]) – Absolute buybacks by period.

  • total_returned (list[float]) – Dividends + buybacks.

  • dividend_yield (float) – Current dividend yield (TTM).

  • buyback_yield (float) – Most recent buyback yield.

  • total_yield (float) – Dividend + buyback yield combined.

  • payout_ratio (list[float]) – Total returned / net income. > 1.0 means returning more than earned (using balance sheet).

  • fcf_payout_ratio (list[float]) – Total returned / FCF. > 1.0 means returning more than free cash flow.

  • sustainability (str) – "sustainable" if FCF payout < 80 %, "caution" if 80–120 %, "unsustainable" if > 120 %.

  • trend (str) – "increasing" if total returned is growing, "decreasing" or "stable".

  • dates (list[str]) – Period end dates.

Return type:

dict[str, Any]

Example

>>> from wraquant.fundamental.financials import shareholder_returns
>>> sr = shareholder_returns("AAPL")
>>> print(f"Total yield: {sr['total_yield']:.2%}")
>>> print(f"Sustainability: {sr['sustainability']}")
>>> for i, d in enumerate(sr['dates'][:3]):
...     print(f"  {d}: ${sr['total_returned'][i]:,.0f}")

References

Mauboussin, M. J. & Callahan, D. (2014). “Capital Allocation: Evidence, Analytical Methods, and Assessment Guidance.” Credit Suisse Global Financial Strategies.

See also

cash_flow_analysis: Broader cash flow metrics. capex_analysis: How CapEx competes with shareholder returns.

Screening

Stock screening using FMP data.

Provides six screening strategies for identifying stocks that match specific fundamental criteria. Each screen encapsulates a well-known investment philosophy:

  1. Value screen – Classic Ben Graham-style: low P/E, decent dividend yield, manageable debt.

  2. Growth screen – Momentum/growth: high revenue growth, positive earnings trajectory.

  3. Quality screen – Buffett-style: high ROE, low leverage, durable competitive advantages.

  4. Piotroski screen – Academic: financial health via the 9-point F-Score (Piotroski, 2000).

  5. Magic formula screen – Greenblatt: rank by ROIC + earnings yield, buy the top-ranked stocks.

  6. Custom screen – Flexible: pass any combination of criteria as a dictionary.

All screening functions use FMPClient for data retrieval. The FMP stock screener endpoint filters the market-wide universe server-side, so results are returned quickly even for broad criteria.

Example

>>> from wraquant.fundamental.screening import value_screen
>>> stocks = value_screen(max_pe=15, min_dividend_yield=0.03)
>>> print(f"Found {len(stocks)} value stocks")
>>> print(stocks[["symbol", "price", "marketCap"]].head())

References

  • Graham, B. (1949). The Intelligent Investor. Harper & Brothers.

  • Piotroski, J. D. (2000). “Value Investing: The Use of Historical Financial Statement Information to Separate Winners from Losers.” Journal of Accounting Research, 38, 1–41.

  • Greenblatt, J. (2006). The Little Book That Beats the Market. Wiley.

  • Novy-Marx, R. (2013). “The Other Side of Value: The Gross Profitability Premium.” Journal of Financial Economics, 108(1), 1–28.

value_screen(max_pe=20.0, min_dividend_yield=0.02, *, max_debt_equity=1.5, min_market_cap=1000000000, country='US', limit=50, fmp_client=None)[source]

Screen for value stocks: low P/E, decent dividend, manageable debt.

Implements a classic Ben Graham-style value screen that identifies stocks trading at low multiples while paying dividends and maintaining conservative balance sheets. This is the foundation of value investing and the HML (high-minus-low) factor in Fama-French.

When to use:

Use this screen to build a value-tilted portfolio, identify contrarian opportunities, or as the starting point for deep-dive fundamental analysis. Value screens work best in conjunction with a quality filter (see quality_screen()) to avoid “value traps” – cheap stocks that are cheap for good reason.

Parameters:
  • max_pe (float, default: 20.0) – Maximum price-to-earnings ratio. The S&P 500 median P/E is historically around 15–20. Setting this to 15 focuses on deep value; 20 casts a wider net.

  • min_dividend_yield (float, default: 0.02) – Minimum annual dividend yield (as decimal). 0.02 = 2 %. Set to 0 to include non-dividend payers.

  • max_debt_equity (float, default: 1.5) – Maximum debt-to-equity ratio. 1.5 allows moderate leverage; 0.5 is conservative.

  • min_market_cap (int, default: 1000000000) – Minimum market capitalisation in USD. The default ($1B) excludes micro/small-caps.

  • country (str, default: 'US') – Country filter (ISO code). "US" for domestic.

  • limit (int, default: 50) – Maximum number of results to return.

  • fmp_client (Any | None, default: None) – Optional pre-configured FMPClient instance. If None, a default client is created.

Return type:

DataFrame

Returns:

DataFrame of matching stocks with key metrics including symbol, companyName, marketCap, price, beta, lastAnnualDividend, and sector.

Example

>>> from wraquant.fundamental.screening import value_screen
>>> df = value_screen(max_pe=15, min_dividend_yield=0.03)
>>> print(f"Found {len(df)} deep value stocks")
>>> print(df[["symbol", "price", "marketCap"]].head(10))

See also

quality_screen: Filter value candidates by profitability. magic_formula_screen: Combines value and quality in one rank.

growth_screen(min_revenue_growth=0.15, *, min_market_cap=500000000, country='US', limit=50, fmp_client=None)[source]

Screen for growth stocks: high revenue growth, positive momentum.

Identifies companies with strong top-line growth, which is the primary driver of long-term equity returns. Revenue growth is preferred over earnings growth because it is harder to manipulate and more persistent.

When to use:

Use this screen to identify companies in secular growth industries or those gaining market share. Growth screens are most effective in bull markets and for momentum-based strategies. Combine with earnings_quality() to filter out companies that are growing revenue but burning cash.

Parameters:
  • min_revenue_growth (float, default: 0.15) – Minimum YoY revenue growth rate (as decimal). 0.15 = 15 %. Set higher (0.30+) for hyper-growth.

  • min_market_cap (int, default: 500000000) – Minimum market cap in USD.

  • country (str, default: 'US') – Country filter.

  • limit (int, default: 50) – Maximum results.

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Return type:

DataFrame

Returns:

DataFrame of high-growth stocks with key metrics. Includes a revenue_growth column when available from FMP data.

Example

>>> from wraquant.fundamental.screening import growth_screen
>>> df = growth_screen(min_revenue_growth=0.25)
>>> print(f"Found {len(df)} high-growth stocks")

See also

value_screen: Complement for a barbell strategy. quality_screen: Ensure growth is profitable.

quality_screen(min_roe=0.15, max_de=1.0, *, min_market_cap=1000000000, country='US', limit=50, fmp_client=None)[source]

Screen for quality stocks: high ROE, low leverage, wide moats.

Quality investing targets companies with durable competitive advantages – high returns on equity, conservative balance sheets, and stable profitability. The quality factor (RMW in Fama-French 5) has historically delivered positive risk-adjusted returns with lower drawdowns than value or momentum.

When to use:

Use this screen to identify “compounders” – stocks that grow book value through high reinvestment rates. Quality screens excel in bear markets and risk-off environments because high- quality companies are more resilient to economic downturns.

Parameters:
  • min_roe (float, default: 0.15) – Minimum return on equity (as decimal). 0.15 = 15 %. The median S&P 500 ROE is around 15–18 %.

  • max_de (float, default: 1.0) – Maximum debt-to-equity ratio. 1.0 is moderate; 0.5 is conservative. Some capital-light businesses (tech, SaaS) naturally have low D/E.

  • min_market_cap (int, default: 1000000000) – Minimum market cap in USD.

  • country (str, default: 'US') – Country filter.

  • limit (int, default: 50) – Maximum results.

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Return type:

DataFrame

Returns:

DataFrame of quality stocks enriched with roe and debt_to_equity columns for verification.

Example

>>> from wraquant.fundamental.screening import quality_screen
>>> df = quality_screen(min_roe=0.20, max_de=0.5)
>>> print(f"Found {len(df)} high-quality stocks")
>>> print(df[["symbol", "roe", "debt_to_equity"]].head(10))

References

Novy-Marx, R. (2013). “The Other Side of Value: The Gross Profitability Premium.” Journal of Financial Economics, 108(1), 1–28.

See also

value_screen: Combine quality + value for best risk/reward. earnings_quality: Validate that earnings are cash-backed.

piotroski_screen(min_score=7, *, min_market_cap=500000000, limit=100, fmp_client=None)[source]

Screen for stocks with high Piotroski F-Score.

The Piotroski F-Score is a 0–9 composite score measuring financial strength across three categories:

Profitability (4 pts):
  1. Positive ROA

  2. Positive operating cash flow

  3. ROA improvement (year over year)

  4. Cash flow > net income (accruals quality)

Leverage & liquidity (3 pts):
  1. Decrease in leverage (long-term debt / assets)

  2. Improvement in current ratio

  3. No new equity issuance

Operating efficiency (2 pts):
  1. Improvement in gross margin

  2. Improvement in asset turnover

Scores of 8–9 identify the strongest companies; scores of 0–2 predict financial distress. Piotroski (2000) showed that a long- short strategy based on the F-Score earned 23 % annual returns among high book-to-market stocks.

Parameters:
  • min_score (int, default: 7) – Minimum Piotroski F-Score (0–9). Default 7 captures the top tier. Set to 8 for ultra-high quality.

  • min_market_cap (int, default: 500000000) – Minimum market cap in USD.

  • limit (int, default: 100) – Number of candidates to evaluate (more = slower but more results).

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Return type:

DataFrame

Returns:

DataFrame with columns symbol, piotroski_score, altman_z, and market_cap for stocks meeting the threshold. Sorted by F-Score descending.

Example

>>> from wraquant.fundamental.screening import piotroski_screen
>>> df = piotroski_screen(min_score=8)
>>> print(f"Found {len(df)} high F-Score stocks")
>>> print(df[["symbol", "piotroski_score", "altman_z"]].head())

References

Piotroski, J. D. (2000). “Value Investing: The Use of Historical Financial Statement Information to Separate Winners from Losers.” Journal of Accounting Research, 38, 1–41.

See also

financial_health_score: Continuous 0–100 health score. value_screen: Combine with F-Score for deep value strategy.

magic_formula_screen(top_n=30, *, min_market_cap=1000000000, fmp_client=None)[source]

Screen using Greenblatt’s Magic Formula: ROIC + Earnings Yield.

The magic formula ranks stocks by two criteria:
  1. Return on invested capital (ROIC) – Measures how efficiently management deploys capital. Higher is better.

  2. Earnings yield (EBIT / EV) – Measures how cheap the stock is relative to its earning power. Higher is better.

Each stock gets a rank on ROIC and a rank on earnings yield; the combined rank identifies companies that are both high-quality AND cheap. Greenblatt (2006) showed this simple strategy beat the market over 17 years.

When to use:

Use this as a standalone strategy or as a starting point for further due diligence. The magic formula is most effective for mid-to-large cap US equities with stable earnings. Avoid applying it to financials and utilities, which have different capital structures.

Parameters:
  • top_n (int, default: 30) – Number of top-ranked stocks to return. Greenblatt recommends holding 20–30 positions.

  • min_market_cap (int, default: 1000000000) – Minimum market cap in USD. The original study used $50M; $1B is more practical for institutional investors.

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Returns:

  • symbol (str) – Ticker symbol.

  • roic (float) – Return on invested capital (TTM).

  • earnings_yield (float) – 1 / P/E ratio (TTM).

  • pe_ratio (float) – P/E ratio for reference.

  • market_cap (float) – Market capitalisation.

  • roic_rank (float) – Rank by ROIC (1 = best).

  • ey_rank (float) – Rank by earnings yield (1 = best).

  • magic_rank (float) – Combined rank (lower = better).

Return type:

DataFrame

Example

>>> from wraquant.fundamental.screening import magic_formula_screen
>>> df = magic_formula_screen(top_n=20)
>>> print(df[["symbol", "roic", "earnings_yield", "magic_rank"]].head())

References

Greenblatt, J. (2006). The Little Book That Beats the Market. Wiley.

See also

quality_screen: Alternative quality-focused screen. value_screen: Pure value screen without quality component.

custom_screen(criteria, *, fmp_client=None)[source]

Screen stocks using a flexible criteria dictionary.

A general-purpose screener that accepts any combination of filter criteria as a dictionary. Use this when the predefined screens (value, growth, quality) do not match your requirements.

When to use:

Use this for custom factor construction, sector-specific screens, or when you need to combine filters that span multiple predefined screens (e.g., “growth + low beta + tech sector”).

Parameters:
  • criteria (dict[str, Any]) –

    Dictionary of screening criteria. Supported keys:

    Market cap: - min_market_cap (int) – Minimum market cap in USD. - max_market_cap (int) – Maximum market cap in USD.

    Classification: - sector (str) – GICS sector (e.g., "Technology"). - industry (str) – Industry filter. - country (str) – Country code (default "US"). - exchange (str) – Exchange (e.g., "NASDAQ").

    Valuation: - min_pe (float) – Minimum P/E ratio. - max_pe (float) – Maximum P/E ratio. - min_dividend_yield (float) – Minimum dividend yield.

    Quality: - min_roe (float) – Minimum ROE. - max_debt_equity (float) – Maximum D/E ratio.

    Risk: - min_beta (float) – Minimum beta. - max_beta (float) – Maximum beta.

    Price & volume: - min_price (float) – Minimum share price. - max_price (float) – Maximum share price. - min_volume (int) – Minimum average daily volume.

    Control: - limit (int) – Maximum results (default 100).

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Return type:

DataFrame

Returns:

DataFrame of matching stocks with standard FMP screener columns.

Example

>>> from wraquant.fundamental.screening import custom_screen
>>> df = custom_screen({
...     "sector": "Technology",
...     "min_market_cap": 10_000_000_000,
...     "max_beta": 1.2,
...     "min_dividend_yield": 0.01,
...     "limit": 20,
... })
>>> print(df[["symbol", "companyName", "marketCap"]].head())

See also

value_screen: Predefined value criteria. growth_screen: Predefined growth criteria. quality_screen: Predefined quality criteria.

dividend_aristocrat_screen(min_years=10, *, min_market_cap=1000000000, country='US', limit=50, fmp_client=None)[source]

Screen for stocks with consecutive years of dividend growth.

Dividend Aristocrats (S&P 500 members with 25+ years of consecutive dividend increases) have historically outperformed with lower volatility. This screen identifies companies with N or more consecutive years of annual dividend per share growth – a strong signal of financial discipline, predictable cash flows, and shareholder-friendly management.

When to use:
  • Income-focused portfolio construction.

  • Quality screening: consistent dividend growth requires consistent earnings growth.

  • Defensive strategy: dividend growers tend to outperform in bear markets.

  • Retirement portfolios: rising income stream over time.

Parameters:
  • min_years (int, default: 10) – Minimum consecutive years of dividend growth. 25 = traditional Aristocrat; 10 = broader “achiever” screen; 5 = emerging dividend growers.

  • min_market_cap (int, default: 1000000000) – Minimum market cap in USD.

  • country (str, default: 'US') – Country filter.

  • limit (int, default: 50) – Maximum results to return.

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Returns:

  • symbol (str) – Ticker symbol.

  • consecutive_years (int) – Years of consecutive dividend growth.

  • current_yield (float) – Current dividend yield.

  • dividend_growth_rate (float) – Most recent YoY growth.

  • market_cap (float) – Market capitalisation.

Return type:

DataFrame

Example

>>> from wraquant.fundamental.screening import dividend_aristocrat_screen
>>> df = dividend_aristocrat_screen(min_years=25)
>>> print(f"Found {len(df)} Dividend Aristocrats")
>>> print(df[["symbol", "consecutive_years", "current_yield"]].head())

References

S&P Dow Jones Indices. “S&P 500 Dividend Aristocrats.” ProShares (2019). “Why Dividend Growth Matters.”

See also

value_screen: Combine with dividend screen for income + value. quality_screen: Dividend consistency as a quality proxy.

turnaround_screen(max_pe=15.0, *, min_margin_improvement=0.02, min_market_cap=500000000, country='US', limit=50, fmp_client=None)[source]

Screen for turnaround candidates: improving margins but still cheap.

Turnaround stocks are value stocks with positive momentum in their fundamentals. They trade at low multiples (the market hasn’t noticed the improvement yet) but show rising margins, indicating operational recovery. This combines value investing with momentum – the two most robust factors in academic research.

When to use:
  • Contrarian strategies: buy when margins inflect upward.

  • Mean-reversion plays: stocks with temporarily depressed earnings reverting to historical norms.

  • Combine with insider buying screen for higher conviction.

Parameters:
  • max_pe (float, default: 15.0) – Maximum P/E ratio (cheap stocks only). Default 15.

  • min_margin_improvement (float, default: 0.02) – Minimum operating margin improvement (most recent - prior year). 0.02 = 2pp improvement.

  • min_market_cap (int, default: 500000000) – Minimum market cap in USD.

  • country (str, default: 'US') – Country filter.

  • limit (int, default: 50) – Maximum results.

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Returns:

  • symbol (str) – Ticker symbol.

  • pe_ratio (float) – Current P/E ratio.

  • operating_margin_current (float) – Most recent period.

  • operating_margin_prior (float) – Prior period.

  • margin_improvement (float) – Change in operating margin.

  • revenue_growth (float) – YoY revenue growth.

  • market_cap (float) – Market capitalisation.

Return type:

DataFrame

Example

>>> from wraquant.fundamental.screening import turnaround_screen
>>> df = turnaround_screen(max_pe=12)
>>> print(f"Found {len(df)} turnaround candidates")
>>> print(df[["symbol", "pe_ratio", "margin_improvement"]].head())

See also

value_screen: Pure value screen. quality_screen: Ensure turnarounds have staying power. insider_buying_screen: Insider confidence in the turnaround.

insider_buying_screen(min_buys=3, days=90, *, min_market_cap=500000000, country='US', limit=50, fmp_client=None)[source]

Screen for stocks with significant recent insider buying activity.

Insiders (officers, directors, 10 %+ owners) are the most informed participants in a stock. Academic research consistently shows that insider purchases predict positive future returns, especially cluster buying (multiple insiders buying in a short window). Insider sales are less informative (often driven by diversification/taxes).

This screen identifies stocks with multiple insider buy transactions in the recent period – a strong signal of management confidence.

When to use:
  • Confirmation signal: use alongside value/quality screens.

  • Contrarian buying: insiders buying during market selloffs.

  • Special situations: new CEO buying, founder increasing stake.

  • Pair with turnaround_screen() for high-conviction turnarounds.

Parameters:
  • min_buys (int, default: 3) – Minimum number of insider purchase transactions in the lookback window. 3+ is significant cluster buying.

  • days (int, default: 90) – Lookback period in calendar days. Default 90 (one quarter).

  • min_market_cap (int, default: 500000000) – Minimum market cap in USD.

  • country (str, default: 'US') – Country filter.

  • limit (int, default: 50) – Maximum results.

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Returns:

  • symbol (str) – Ticker symbol.

  • buy_count (int) – Number of insider purchases.

  • total_value (float) – Total dollar value of purchases.

  • unique_insiders (int) – Number of distinct insiders buying.

  • market_cap (float) – Market capitalisation.

Return type:

DataFrame

Example

>>> from wraquant.fundamental.screening import insider_buying_screen
>>> df = insider_buying_screen(min_buys=5, days=60)
>>> print(f"Found {len(df)} stocks with cluster insider buying")
>>> print(df[["symbol", "buy_count", "total_value"]].head())

References

Lakonishok, J. & Lee, I. (2001). “Are Insider Trades Informative?” Review of Financial Studies, 14(1), 79–111.

See also

turnaround_screen: Combine with insider buying for conviction. quality_screen: Verify fundamentals back the insider thesis.

momentum_value_screen(top_n=30, *, min_market_cap=1000000000, country='US', fmp_client=None)[source]

Screen combining value (low PE) with price momentum (positive 6M return).

Value and momentum are the two most persistent and well-documented factors in equity returns. They are negatively correlated, making a combined strategy more robust than either alone. This screen identifies stocks that are both cheap (low P/E) and in an uptrend (positive 6-month price return), capturing the sweet spot where value is being recognized by the market.

When to use:
  • Multi-factor portfolio construction: value + momentum is the classic two-factor strategy (Asness et al., 2013).

  • Avoid value traps: momentum filter ensures the market is beginning to recognize the value.

  • Tactical allocation: identify undervalued stocks with improving price action.

Mathematical formulation:

Value Score = percentile_rank(1/PE) (higher = cheaper) Momentum Score = percentile_rank(6M return) Combined Score = Value Score + Momentum Score Select top N by Combined Score.

Parameters:
  • top_n (int, default: 30) – Number of top-ranked stocks to return.

  • min_market_cap (int, default: 1000000000) – Minimum market cap in USD.

  • country (str, default: 'US') – Country filter.

  • fmp_client (Any | None, default: None) – Optional FMPClient instance.

Returns:

  • symbol (str) – Ticker symbol.

  • pe_ratio (float) – P/E ratio.

  • earnings_yield (float) – 1/PE (higher = cheaper).

  • price_return_6m (float) – 6-month price return.

  • value_rank (float) – Rank by earnings yield (1 = best).

  • momentum_rank (float) – Rank by 6M return (1 = best).

  • combined_rank (float) – Sum of value and momentum ranks (lower = better).

  • market_cap (float) – Market capitalisation.

Return type:

DataFrame

Example

>>> from wraquant.fundamental.screening import momentum_value_screen
>>> df = momentum_value_screen(top_n=20)
>>> print(df[["symbol", "pe_ratio", "price_return_6m",
...           "combined_rank"]].head())

References

Asness, C. S., Moskowitz, T. J., & Pedersen, L. H. (2013). “Value and Momentum Everywhere.” Journal of Finance, 68(3), 929–985.

See also

value_screen: Pure value screen. magic_formula_screen: Value + quality (similar concept).