VectorFin/Glossary/Piotroski F-Score
Quant Finance

What is Piotroski F-Score?

A 9-point accounting-based score that separates financially strong companies from weak ones using signals of profitability, leverage, and operating efficiency.

In Plain English

In the late 1990s, Stanford accounting professor Joseph Piotroski noticed something paradoxical about value stocks: on average, they underperformed despite appearing cheap by traditional measures. The culprit was a subset of truly distressed, deteriorating companies masquerading as value plays. His 2000 paper described a simple 9-point scoring system to separate the genuinely improving value companies (high F-Score) from the ones heading toward bankruptcy (low F-Score).

The F-Score works like a physical for a company's finances. Instead of looking at valuation multiples, it examines nine binary signals — each scores 0 or 1 — spread across three categories: Is the company profitable and improving its profitability? Is it reducing debt and improving liquidity? Is it becoming more efficient at turning assets into sales? A company that passes all nine tests gets a score of 9. One that fails all nine gets a 0.

The elegance is in the simplicity. You don't need to forecast cash flows or estimate discount rates. You just read the financial statements and check whether things are getting better or worse. A score of 7-9 signals a fundamentally improving company; 0-2 signals a deteriorating one. Piotroski showed that a long/short strategy based on this score generated substantial alpha even after accounting for transaction costs.

What makes the F-Score valuable in a quantitative context is that it's entirely rules-based, reproducible, and requires no subjective judgment. Every analyst applying it to the same company at the same time will get the same answer. This makes it ideal as a component in systematic factor models and multi-signal composite scores.

Technical Definition

The F-Score sums nine binary indicators F\_i ∈ \{0, 1\}:

Profitability (4 signals):

  • F₁: ROA > 0 (net income / beginning total assets)
  • F₂: Operating cash flow > 0
  • F₃: Change in ROA > 0 (ROA improved vs prior year)
  • F₄: Accruals < 0 (operating cash flow > ROA; cash earnings exceed accrual earnings)

Leverage, Liquidity, Source of Funds (3 signals):

  • F₅: Change in leverage < 0 (long-term debt ratio decreased)
  • F₆: Change in current ratio > 0 (liquidity improved)
  • F₇: No dilutive share issuance in prior year

Operating Efficiency (2 signals):

  • F₈: Change in gross margin > 0
  • F₉: Change in asset turnover > 0 (revenue / total assets improved)

Total F-Score = Σᵢ₌₁⁹ Fᵢ ∈ \{0, 1, ..., 9\}

The accrual signal (F₄) is particularly powerful: companies whose earnings significantly exceed their cash flows are more likely to see earnings reversals, a phenomenon related to the accrual anomaly documented by Sloan (1996).

How VectorFin Uses This

The Piotroski F-Score is one of several components in VectorFin's whystock_score composite signal. The whystock_score table in signals/whystock_score includes the F-Score breakdown alongside momentum, analyst sentiment, and NLP-derived quality signals from earnings calls.

F-Score components are updated nightly as new 10-K and 10-Q filings are processed. The bitemporal design ensures backtests use the F-Score that would have been computable from filings available at the backtest date, avoiding look-ahead bias from restated financials.

GET https://api.vectorfinancials.com/v1/signals/whystock-score/{ticker}?date=2024-10-01

The response includes components.piotroski_f_score alongside the composite score.

Code Example

import requests
import pandas as pd

API_BASE = "https://api.vectorfinancials.com"
API_KEY = "vf_your_api_key_here"

# Screen for high F-Score companies in the S&P 500 universe
tickers = ["AAPL", "MSFT", "JPM", "JNJ", "XOM", "WMT", "PG", "UNH", "HD", "CVX"]

results = []
for ticker in tickers:
    resp = requests.get(
        f"{API_BASE}/v1/signals/whystock-score/{ticker}",
        params={"date": "2024-10-01"},
        headers={"X-API-Key": API_KEY},
    )
    if resp.ok:
        data = resp.json()
        results.append({
            "ticker": ticker,
            "composite_score": data["score"],
            "f_score": data["components"]["piotroski_f_score"],
            "momentum": data["components"]["momentum"],
        })

df = pd.DataFrame(results)

# Filter: high F-Score fundamentals (7+) with positive momentum
quality_momentum = df[(df["f_score"] >= 7) & (df["momentum"] > 0)]
print("High quality + positive momentum candidates:")
print(quality_momentum.sort_values("composite_score", ascending=False).to_string(index=False))

Put Piotroski F-Score to work in your pipeline

Access AI-ready financial data — embeddings, signals, Iceberg tables.