SPECIFICITY, INC. (SPTY, CIK 1840102, Services-Advertising Agencies)
re-run · ask a question about this company
Did the stock fall, and when? (Item 4.02 notices)
Daily adjusted closes, Yahoo Finance chart endpoint (unofficial), adjusted close; best-effort, may be wrong or missing. Returns run from the last close before day 0 to the close N trading days later. 'Minus IWM' subtracts the Russell 2000 ETF's return over the same dates. Descriptive only: not a prediction and not a causal estimate.
8-K filed 2026-05-26, accepted 2026-05-22 21:41 ET (after the 4:00 pm close, so day 0 is the next trading day) — notice
Day 0 = 2026-05-26; base = last close before day 0 (2026-05-22, 0.28).
| Window (trading days) | Dates (close to close) | Raw | Minus IWM | |
|---|---|---|---|---|
| day 0 | 2026-05-22 → 2026-05-26 | +0.0% | -1.9% | |
| 0–1 | 2026-05-22 → 2026-05-27 | -15.4% | -17.2% | |
| 0–5 | 2026-05-22 → 2026-06-02 | +71.4% | +69.1% | |
| 0–30 | 2026-05-22 → 2026-07-09 | -10.7% | -15.2% |
No other 8-K (besides exhibits) filed between the day before the notice and day 0.
Securities class actions after the notice
- 2026-05-26 notice: CourtListener unavailable, retry later
CourtListener RECAP docket search (caption names the company; securities nature of suit, '10b-5' or 'Securities Exchange Act'). A docket may concern a different matter; open it. A miss is not proof there is no suit.
Explanation (model, from the reference numbers)
Restatement history. Three relevant filings: an 8-K Item 4.02 (filed 2026-05-26) signaling non-reliance on previously issued financials; a 10-K/A (filed 2026-06-05) restating FY2025; and an earlier 10-Q/A (filed 2022-12-29) for Q3 2022.
What changed. The 10-K/A vs. the original 10-K (filed 2026-03-31) shows FY2025 net loss widened from $(473,147) to $(554,067) (+17%), COGS up ~5% to $650,188, and operating cash outflow worsening from $(89,807) to $(149,371) (−66%). Separately, the Q3 2023 10-Q as re-filed 2025-04-23 shows revenue cut ~5% (Q3 alone −15%), COGS nearly doubled for the quarter, and depreciation halved — consistent with the 2022 10-Q/A pattern of prior-period corrections. Balance-sheet deltas also appear between the FY2025 10-K and the 2026-08-19 10-Q (current liabilities +10.6%, convertible notes current +27%), which may be reclassification or recast rather than restatement — the filings themselves will say which.
Screens. M-score and F-score could not be computed — insufficient XBRL data (missing SGA and receivables; long-term debt imputed as zero). These are published academic screens approximated from XBRL, not the SEC's non-public Accounting Quality Model, and a score would be a screen, not a prediction.
Benford. 123 values from the 10-K/A show nonconformity (MAD 0.0413, chi² 17.65) — a red-flag indicator only, not evidence of anything.
SEC scrutiny. The SEC does not "audit" companies; scrutiny comes via Corp Fin filing reviews (CORRESP/UPLOAD letters) or, rarely, Enforcement. Here: no comment letters on file and no AAER name matches — no evidence of a filing review.
Cross-check note. Our delta count (56) missed one delta found in the reference set (57); no value mismatches. The reference numbers are used as authoritative.
Screens (fiscal year ending 2025-12-31)
Beneish M-score: — (flag above −1.78; insufficient_data)
Same screen on numbers as first reported: —
Dechow F-score: — (flag at ≥ 1.0; insufficient_data)
Benford MAD: 0.0413 n=123, Nigrini: nonconformity
Missing-data notes
- M-score not computed — missing: receivables, sga
- M-score imputation: ltd=0 (no long-term debt tag)
- F-score not computed — missing: receivables
- F-score inputs treated as 0 (no XBRL fact): debt_issued, inventory, lt_investments, ltd, st_investments
Model's sandboxed calculation vs reference code
1 check(s) failed: 1 wrong value(s), 0 not reported by the model. The reference numbers are used everywhere on this page.
| Metric | Reference | Model | |
|---|---|---|---|
| M-score | — | — | match |
| F-score | — | — | match |
| Benford MAD | 0.0413 | 0.0413 | match |
| deltas (count) | 57.0000 | 56.0000 | MISMATCH (wrong value) |
What changed between filings (57 line-item/periods, since 2022)
Original = earliest filed value for that period; restated = latest. Changes can be restatement, reclassification or recast — read the filings.
| Item | XBRL tag | Period | Original | Latest | Change |
|---|---|---|---|---|---|
| current_assets | AssetsCurrent | → 2025-12-31 | $5,534 10-K 2026-03-31 | $5,737 10-Q 2026-08-19 | +3.7% |
| current_liabilities | LiabilitiesCurrent | → 2025-12-31 | $1.14M 10-K 2026-03-31 | $1.26M 10-Q 2026-08-19 | +10.6% |
| total_liabilities | Liabilities | → 2025-12-31 | $2.14M 10-K 2026-03-31 | $2.26M 10-Q 2026-08-19 | +5.7% |
| debt_current | ConvertibleNotesPayableCurrent | → 2025-12-31 | $429,736 10-K 2026-03-31 | $546,010 10-Q 2026-08-19 | +27.1% |
| cash | CashAndCashEquivalentsAtCarryingValue | → 2025-12-31 | $1,784 10-K 2026-03-31 | $1,987 10-Q 2026-08-19 | +11.4% |
| cogs | CostOfRevenue | 2025-01-01 → 2025-12-31 | $618,803 10-K 2026-03-31 | $650,188 10-K/A 2026-06-05 | +5.1% |
| net_income | NetIncomeLoss | 2025-01-01 → 2025-12-31 | −$473,147 10-K 2026-03-31 | −$554,067 10-K/A 2026-06-05 | -17.1% |
| cfo | NetCashProvidedByUsedInOperatingActivities | 2025-01-01 → 2025-12-31 | −$89,807 10-K 2026-03-31 | −$149,371 10-K/A 2026-06-05 | -66.3% |
| stock_issued | ProceedsFromIssuanceOfCommonStock | 2023-01-01 → 2023-12-31 | $290,502 10-K 2025-03-03 | $49,253 10-K 2025-06-24 | -83.0% |
| revenue | Revenues | 2023-01-01 → 2023-09-30 | $805,317 10-Q 2023-11-20 | $765,232 10-Q 2025-04-23 | -5.0% |
| revenue | Revenues | 2023-07-01 → 2023-09-30 | $320,271 10-Q 2023-11-20 | $270,888 10-Q 2025-04-23 | -15.4% |
| cogs | CostOfRevenue | 2023-01-01 → 2023-09-30 | $263,825 10-Q 2023-11-20 | $379,724 10-Q 2025-04-23 | +43.9% |
| cogs | CostOfRevenue | 2023-07-01 → 2023-09-30 | $26,625 10-Q 2023-11-20 | $53,374 10-Q 2025-04-23 | +100.5% |
| depreciation | Depreciation | 2023-01-01 → 2023-09-30 | $7,834 10-Q 2023-11-20 | $3,895 10-Q 2025-04-23 | -50.3% |
| cash | CashCashEquivalentsRestrictedCashAndRestrictedCashEquivalents | → 2023-09-30 | $5,317 10-Q 2023-11-20 | $4,555 10-Q 2025-04-23 | -14.3% |
| net_income | NetIncomeLoss | 2023-01-01 → 2023-09-30 | −$874,855 10-Q 2023-11-20 | −$635,445 10-Q 2025-04-23 | +27.4% |
| net_income | NetIncomeLoss | 2023-07-01 → 2023-09-30 | $52,466 10-Q 2023-11-20 | −$64,597 10-Q 2025-04-23 | -223.1% |
| cfo | NetCashProvidedByUsedInOperatingActivities | 2023-01-01 → 2023-09-30 | −$645,831 10-Q 2023-11-20 | −$421,604 10-Q 2025-04-23 | +34.7% |
| stock_issued | ProceedsFromIssuanceOfCommonStock | 2023-01-01 → 2023-09-30 | $175,000 10-Q 2023-11-20 | $49,253 10-Q 2025-04-23 | -71.9% |
| revenue | Revenues | 2023-01-01 → 2023-06-30 | $485,046 10-Q 2023-08-04 | $494,344 10-Q 2025-04-23 | +1.9% |
| revenue | Revenues | 2023-04-01 → 2023-06-30 | $279,188 10-Q 2023-08-04 | $287,983 10-Q 2025-04-23 | +3.1% |
| cogs | CostOfRevenue | 2023-01-01 → 2023-06-30 | $237,200 10-Q 2023-08-04 | $326,350 10-Q 2025-04-23 | +37.6% |
| cogs | CostOfRevenue | 2023-04-01 → 2023-06-30 | $142,995 10-Q 2023-08-04 | $200,522 10-Q 2025-04-23 | +40.2% |
| depreciation | Depreciation | 2023-01-01 → 2023-06-30 | $5,194 10-Q 2023-08-04 | $2,597 10-Q 2025-04-23 | -50.0% |
| net_income | NetIncomeLoss | 2023-01-01 → 2023-06-30 | −$927,321 10-Q 2023-08-04 | −$570,848 10-Q 2025-04-23 | +38.4% |
| net_income | NetIncomeLoss | 2023-04-01 → 2023-06-30 | −$433,253 10-Q 2023-08-04 | −$330,498 10-Q 2025-04-23 | +23.7% |
| cfo | NetCashProvidedByUsedInOperatingActivities | 2023-01-01 → 2023-06-30 | −$558,727 10-Q 2023-08-04 | −$455,030 10-Q 2025-04-23 | +18.6% |
| stock_issued | ProceedsFromIssuanceOfCommonStock | 2023-01-01 → 2023-06-30 | $75,000 10-Q 2023-08-04 | $49,253 10-Q 2025-04-23 | -34.3% |
| revenue | Revenues | 2023-01-01 → 2023-03-31 | $231,118 10-Q 2023-05-18 | $206,361 10-Q 2025-04-17 | -10.7% |
| cogs | CostOfRevenue | 2023-01-01 → 2023-03-31 | $94,205 10-Q 2023-05-18 | $125,828 10-Q 2025-04-17 | +33.6% |
| depreciation | Depreciation | 2023-01-01 → 2023-03-31 | $2,583 10-Q 2023-05-18 | $1,298 10-Q 2025-04-17 | -49.8% |
| cash | CashCashEquivalentsRestrictedCashAndRestrictedCashEquivalents | → 2023-03-31 | $50,089 10-Q 2023-05-18 | $32,329 10-Q 2025-04-17 | -35.5% |
| net_income | NetIncomeLoss | 2023-01-01 → 2023-03-31 | −$468,808 10-Q 2023-05-18 | −$240,350 10-Q 2025-04-17 | +48.7% |
| cfo | NetCashProvidedByUsedInOperatingActivities | 2023-01-01 → 2023-03-31 | −$326,932 10-Q 2023-05-18 | −$307,646 10-Q 2025-04-17 | +5.9% |
| revenue | Revenues | 2022-01-01 → 2022-12-31 | $1.15M 10-K 2023-03-30 | $1.18M 10-K 2025-03-03 | +3.0% |
| cogs | CostOfRevenue | 2022-01-01 → 2022-12-31 | $592,102 10-K 2023-03-30 | $765,888 10-K 2025-03-03 | +29.3% |
| receivables | AccountsReceivableNetCurrent | → 2022-12-31 | $8,182 10-K 2023-03-30 | $5,000 10-K 2025-03-03 | -38.9% |
| current_assets | AssetsCurrent | → 2022-12-31 | $266,375 10-K 2023-03-30 | $30,178 10-K 2025-03-03 | -88.7% |
| ppe | PropertyPlantAndEquipmentNet | → 2022-12-31 | $70,722 10-K 2023-03-30 | $10,739 10-K 2025-03-03 | -84.8% |
| depreciation | Depreciation | 2022-01-01 → 2022-12-31 | $9,982 10-K 2023-03-30 | $4,546 10-K 2025-03-03 | -54.5% |
| total_assets | Assets | → 2022-12-31 | $401,729 10-K 2023-03-30 | $160,557 10-K 2025-03-03 | -60.0% |
| current_liabilities | LiabilitiesCurrent | → 2022-12-31 | $369,343 10-K 2023-03-30 | $384,589 10-K 2025-03-03 | +4.1% |
| total_liabilities | Liabilities | → 2022-12-31 | $1.39M 10-K 2023-03-30 | $1.41M 10-K 2025-03-03 | +1.2% |
| net_income | NetIncomeLoss | 2022-01-01 → 2022-12-31 | −$4.34M 10-K 2023-03-30 | −$3.78M 10-K 2025-03-03 | +13.0% |
| cfo | NetCashProvidedByUsedInOperatingActivities | 2022-01-01 → 2022-12-31 | −$2.04M 10-K 2023-03-30 | −$2.00M 10-K 2025-03-03 | +1.7% |
| revenue | Revenues | 2022-01-01 → 2022-09-30 | $933,821 10-Q 2022-11-21 | $649,510 10-Q 2023-11-20 | -30.4% |
| revenue | Revenues | 2022-07-01 → 2022-09-30 | $662,971 10-Q 2022-11-21 | $378,660 10-Q 2023-11-20 | -42.9% |
| cogs | CostOfRevenue | 2022-01-01 → 2022-09-30 | $489,760 10-Q 2022-11-21 | $261,250 10-Q 2023-11-20 | -46.7% |
| cogs | CostOfRevenue | 2022-07-01 → 2022-09-30 | $338,870 10-Q 2022-11-21 | $110,360 10-Q 2023-11-20 | -67.4% |
| depreciation | Depreciation | 2022-01-01 → 2022-09-30 | $7,342 10-Q 2022-11-21 | $4,769 10-Q 2023-11-20 | -35.0% |
| cash | CashCashEquivalentsRestrictedCashAndRestrictedCashEquivalents | → 2022-09-30 | $172,594 10-Q 2022-11-21 | $26,140 10-Q 2023-11-20 | -84.9% |
| net_income | NetIncomeLoss | 2022-07-01 → 2022-09-30 | −$949,525 10-Q 2022-11-21 | −$576,923 10-Q 2023-11-20 | +39.2% |
| cfo | NetCashProvidedByUsedInOperatingActivities | 2022-01-01 → 2022-09-30 | −$1.71M 10-Q 2022-11-21 | −$1.12M 10-Q 2023-11-20 | +34.4% |
| stock_issued | ProceedsFromIssuanceOfCommonStock | 2022-01-01 → 2022-09-30 | $1.26M 10-Q 2022-11-21 | $471,967 10-Q 2023-11-20 | -62.6% |
| current_assets | AssetsCurrent | → 2022-09-30 | $213,226 10-Q 2022-11-21 | $584,595 10-Q/A 2022-12-29 | +174.2% |
| total_assets | Assets | → 2022-09-30 | $361,499 10-Q 2022-11-21 | $732,868 10-Q/A 2022-12-29 | +102.7% |
| net_income | NetIncomeLoss | 2022-01-01 → 2022-09-30 | −$2.28M 10-Q 2022-11-21 | −$3.51M 10-Q/A 2022-12-29 | -53.6% |
Non-reliance (8-K 4.02) and amended filings
| Filed | Form | Items | |
|---|---|---|---|
| 2026-06-05 | 10-K/A | open | |
| 2026-05-26 | 8-K | 4.02 | open |
| 2022-12-29 | 10-Q/A | open |
SEC comment-letter correspondence (CORRESP / UPLOAD)
Shows a filing review happened; content not summarized here.
| 2022-09-21 | CORRESP | open |
| 2022-09-20 | UPLOAD | open |
| 2022-05-27 | CORRESP | open |
| 2022-05-27 | UPLOAD | open |
| 2021-09-14 | CORRESP | open |
| 2021-09-03 | UPLOAD | open |
| 2021-08-13 | UPLOAD | open |
| 2021-07-21 | UPLOAD | open |
AAER (enforcement release) name match
- No AAER whose respondent name matches this company (exact normalized name; individuals charged alone would not match).
Sandbox attempt log
reply
sst = (dWC + dNCO + dFIN) / avgTA if avgTA else None
ch_rec = (rec_t2 - rec_p2) / avgTA if avgTA else None
ch_inv = (inv_t - inv_p) / avgTA if avgTA else None
soft = (ta_t - ppe_t2 - cash_t) / ta_t if ta_t else None
cs_t = rev_t2 - (rec_t2 - rec_p2)
cs_p = (rev_p2 - (rec_p2 - rec_pp)) if rec_pp is not None else rev_p2
ch_cs = (cs_t - cs_p) / cs_p if cs_p else None
avgTA_p = (ta_p + ta_pp) / 2 if (ta_p is not None and ta_pp is not None) else None
ch_roa = (ni_t2 / avgTA - ni_p2 / avgTA_p) if (avgTA and avgTA_p) else None
issue = 1 if (si_t > 0 or di_t > 0) else 0
f_vars = {"avgTA": avgTA, "rsst_acc": rsst, "ch_rec": ch_rec, "ch_inv": ch_inv,
"soft_assets": soft, "ch_cs": ch_cs, "ch_roa": ch_roa, "issue": issue}
if any(v is None for v in [rsst, ch_rec, ch_inv, soft, ch_cs, ch_roa]):
f_score = None
else:
x = (-7.893 + 0.790 * rsst + 2.518 * ch_rec + 1.191 * ch_inv
+ 1.979 * soft + 0.171 * ch_cs - 0.932 * ch_roa + 1.029 * issue)
f_score = (math.exp(x) / (1 + math.exp(x))) / 0.0037
except Exception:
f_score = None
# ---------------- 4. Benford ----------------
accns = {}
for tag, node in USGAAP.items():
for unit, fs in node.get("units", {}).items():
if unit != "USD":
continue
for f in fs:
form = f.get("form", "")
if form in ("10-K", "10-K/A") and f.get("accn"):
accns[f["accn"]] = f
best = None
for accn, f in accns.items():
if best is None or (f.get("filed", "") > best[1].get("filed", "")):
best = (accn, f)
benford = None
if best:
accn = best[0]
seen = set()
vals = []
for tag, node in USGAAP.items():
for f in node.get("units", {}).get("USD", []):
if f.get("accn") != accn:
continue
v = abs(f["val"])
if v < 10:
continue
key = (tag, f.get("start"), f["end"])
if key in seen:
continue
seen.add(key)
vals.append(v)
n = len(vals)
if n:
counts = {d_: 0 for d_ in range(1, 10)}
for v in vals:
counts[int(str(v)[0])] += 1
mad = sum(abs(counts[d_] / n - math.log10(1 + 1 / d_)) for d_ in range(1, 10)) / 9
benford = {"accession": accn, "form": best[1].get("form"), "filed": best[1].get("filed"),
"n": n, "mad": mad}
# ---------------- 5. restatement filings ----------------
notices = []
for f in FIL:
form = f.get("form", "")
items = f.get("items") or ""
if form == "8-K" and "4.02" in str(items):
notices.append(f)
elif form in ("10-K/A", "10-Q/A"):
notices.append(f)
notices.sort(key=lambda x: x.get("filed", ""))
first_402 = min((f["filed"] for f in notices if f.get("form") == "8-K"), default=None)
resp = [f for f in FIL if f.get("form") in ("CORRESP", "UPLOAD")
and first_402 and f.get("filed", "") > first_402]
result = {
rr-sbx-16feec7df7 removed
code the model wrote (11842 chars)
import json, math
from datetime import date, datetime
def D(s):
return datetime.strptime(s, "%Y-%m-%d").date()
CF = json.load(open("/data/companyfacts.json"))
FILINGS = json.load(open("/data/filings.json")).get("filings", [])
TAG_MAP = json.load(open("/data/tag_map.json"))
AVAIL = json.load(open("/data/available_tags.json"))
USGAAP = CF.get("facts", {}).get("us-gaap", {})
def facts_of(tag):
try:
return USGAAP[tag]["units"]["USD"]
except Exception:
return []
INSTANT = {'cash','current_assets','current_liabilities','debt_current','inventory',
'lt_investments','ltd','ppe','receivables','st_investments','total_assets','total_liabilities'}
FY = {"t": "2025-12-31", "p": "2024-12-31", "pp": "2023-12-31"}
def _match(f, target, instant, annual_only):
try:
end = D(f["end"])
except Exception:
return False
if abs((end - target).days) > 10:
return False
has_start = "start" in f
if instant:
return not has_start
if not has_start:
return False
dur = (end - D(f["start"])).days
if annual_only and not (350 <= dur <= 380):
return False
return True
def _pick(cands):
cands = sorted(cands, key=lambda x: (x.get("filed", ""), x.get("accn", "")))
return cands[-1]
def _single(item, endstr, instant, annual_only):
target = D(endstr)
for tag in TAG_MAP.get(item, []):
if "+" in tag:
continue
cands = [f for f in facts_of(tag) if _match(f, target, instant, annual_only)]
if cands:
return _pick(cands)["val"], tag
return None, None
def _combo(item, endstr, instant, annual_only):
target = D(endstr)
for spec in TAG_MAP.get(item, []):
if "+" not in spec:
continue
parts = spec.split("+")
per_filing = {}
ok = True
for tag in parts:
cands = [f for f in facts_of(tag) if _match(f, target, instant, annual_only)]
if not cands:
ok = False
break
best = _pick(cands)
key = (best.get("filed", ""), best.get("accn", ""))
per_filing.setdefault(key, {})[tag] = best["val"]
if ok and per_filing:
key = max(per_filing)
if len(per_filing[key]) == len(parts):
return sum(per_filing[key].values()), spec
return None, None
def get_annual(item, endstr):
if item in INSTANT:
v, t = _single(item, endstr, True, False)
if v is None:
v, t = _combo(item, endstr, True, False)
return v, t
v, t = _single(item, endstr, False, True)
if v is None:
v, t = _combo(item, endstr, False, True)
return v, t
def get_flow_any(item, endstr):
v, t = _single(item, endstr, False, False)
if v is None:
v, t = _combo(item, endstr, False, False)
return v, t
# ---------------- 1. deltas ----------------
deltas = []
for item, tags in TAG_MAP.items():
for tag in tags:
if "+" in tag:
continue
groups = {}
for f in facts_of(tag):
try:
end = D(f["end"])
except Exception:
continue
if end < D("2022-01-01"):
continue
groups.setdefault((f.get("start"), f["end"]), []).append(f)
for (start, end), fs in groups.items():
if len({f["val"] for f in fs}) < 2:
continue
fs = sorted(fs, key=lambda x: (x.get("filed", ""), x.get("accn", "")))
orig, rest = fs[0], fs[-1]
o, r = orig["val"], rest["val"]
if o == 0:
continue
pct = (r - o) / abs(o)
if abs(pct) < 0.005:
continue
deltas.append({"item": item, "tag": tag, "start": start, "end": end,
"original": o, "original_form": orig.get("form"),
"restated": r, "restated_form": rest.get("form"),
"restated_filed": rest.get("filed"), "pct_change": pct})
deltas.sort(key=lambda x: (x["item"], x["end"] or "", x["start"] or ""))
# ---------------- 2. Beneish ----------------
def div(a, b):
if a is None or b in (None, 0):
return None
return a / b
V = {}
missing = []
for item in ["revenue", "cogs", "sga", "depreciation", "net_income", "cfo"]:
for per in ("t", "p"):
if per == "p" and item in ("net_income", "cfo", "depreciation"):
continue
val, _ = get_annual(item, FY[per])
V[f"{item}_{per}"] = val
if val is None:
missing.append(f"{item}_{per}")
for item in ["receivables", "current_assets", "ppe", "total_assets",
"current_liabilities", "ltd"]:
for per in ("t", "p"):
val, _ = get_annual(item, FY[per])
V[f"{item}_{per}"] = val
if val is None and item != "ltd":
missing.append(f"{item}_{per}")
imputed = []
m_score = None
ratios = {}
try:
rec_t, rec_p = V["receivables_t"], V["receivables_p"]
rev_t, rev_p = V["revenue_t"], V["revenue_p"]
cogs_t, cogs_p = V["cogs_t"], V["cogs_p"]
ca_t, ca_p = V["current_assets_t"], V["current_assets_p"]
ppe_t, ppe_p = V["ppe_t"], V["ppe_p"]
ta_t, ta_p = V["total_assets_t"], V["total_assets_p"]
cl_t, cl_p = V["current_liabilities_t"], V["current_liabilities_p"]
ltd_t = V["ltd_t"] if V["ltd_t"] is not None else 0
ltd_p = V["ltd_p"] if V["ltd_p"] is not None else 0
ni_t, cfo_t = V["net_income_t"], V["cfo_t"]
dep_t, dep_p = V["depreciation_t"], V["depreciation_p"]
sga_t, sga_p = V["sga_t"], V["sga_p"]
DSRI = div(div(rec_t, rev_t), div(rec_p, rev_p))
gm_t = div(rev_t - cogs_t, rev_t)
gm_p = div(rev_p - cogs_p, rev_p)
GMI = div(gm_p, gm_t)
AQI = div(1 - div(ca_t + ppe_t, ta_t), 1 - div(ca_p + ppe_p, ta_p))
SGI = div(rev_t, rev_p)
if dep_t is None or dep_p is None or not ppe_t or not ppe_p:
DEPI = 1.0; imputed.append("DEPI")
else:
DEPI = div(div(dep_p, dep_p + ppe_p), div(dep_t, dep_t + ppe_t))
if sga_t is None or sga_p is None:
SGAI = 1.0; imputed.append("SGAI")
else:
SGAI = div(div(sga_t, rev_t), div(sga_p, rev_p))
LVGI = div(div(cl_t + ltd_t, ta_t), div(cl_p + ltd_p, ta_p))
TATA = div(ni_t - cfo_t, ta_t)
ratios = {"DSRI": DSRI, "GMI": GMI, "AQI": AQI, "SGI": SGI,
"DEPI": DEPI, "SGAI": SGAI, "LVGI": LVGI, "TATA": TATA}
bad = [k for k, v in ratios.items() if v is None and k not in ("DEPI", "SGAI")]
if bad:
m_score = None
missing = sorted(set(missing) | set(bad))
else:
m_score = (-4.84 + 0.920*DSRI + 0.528*GMI + 0.404*AQI + 0.892*SGI
+ 0.115*DEPI - 0.172*SGAI + 4.679*TATA - 0.327*LVGI)
except Exception:
m_score = None
# ---------------- 3. Dechow F-score ----------------
def g(item, per):
return get_annual(item, FY[per])[0]
def z(v):
return 0 if v is None else v
f_vars = {}
f_score = None
try:
ta_t, ta_p, ta_pp = g("total_assets", "t"), g("total_assets", "p"), g("total_assets", "pp")
ca_t, ca_p = g("current_assets", "t"), g("current_assets", "p")
cash_t, cash_p = g("cash", "t"), g("cash", "p")
sti_t, sti_p = z(g("st_investments", "t")), z(g("st_investments", "p"))
lti_t, lti_p = z(g("lt_investments", "t")), z(g("lt_investments", "p"))
cl_t, cl_p = g("current_liabilities", "t"), g("current_liabilities", "p")
dc_t, dc_p = z(g("debt_current", "t")), z(g("debt_current", "p"))
ltd_t2, ltd_p2 = z(g("ltd", "t")), z(g("ltd", "p"))
tl_t, tl_p = g("total_liabilities", "t"), g("total_liabilities", "p")
ppe_t2, ppe_p2 = g("ppe", "t"), g("ppe", "p")
inv_t, inv_p = z(g("inventory", "t")), z(g("inventory", "p"))
rec_t2, rec_p2 = g("receivables", "t"), g("receivables", "p")
rec_pp = g("receivables", "pp")
rev_t2, rev_p2 = g("revenue", "t"), g("revenue", "p")
ni_t2, ni_p2 = g("net_income", "t"), g("net_income", "p")
si_t = z(get_flow_any("stock_issued", FY["t"])[0]) if TAG_MAP.get("stock_issued") else 0
di_t = z(get_flow_any("debt_issued", FY["t"])[0]) if TAG_MAP.get("debt_issued") else 0
avgTA = (ta_t + ta_p) / 2
dWC = ((ca_t - cash_t - sti_t) - (cl_t - dc_t)) - ((ca_p - cash_p - sti_p) - (cl_p - dc_p))
dNCO = ((ta_t - ca_t - lti_t) - (tl_t - cl_t - ltd_t2)) - ((ta_p - ca_p - lti_p) - (tl_p - cl_p - ltd_p2))
dFIN = ((sti_t + lti_t) - (ltd_t2 + dc_t)) - ((sti_p + lti_p) - (ltd_p2 + dc_p))
rsst = (dWC + dNCO + dFIN) / avgTA
ch_rec = (rec_t2 - rec_p2) / avgTA
ch_inv = (inv_t - inv_p) / avgTA
soft = (ta_t - ppe_t2 - cash_t) / ta_t
cs_t = rev_t2 - (rec_t2 - rec_p2)
cs_p = (rev_p2 - (rec_p2 - rec_pp)) if rec_pp is not None else rev_p2
ch_cs = (cs_t - cs_p) / cs_p
avgTA_p = (ta_p + ta_pp) / 2
ch_roa = ni_t2 / avgTA - ni_p2 / avgTA_p
issue = 1 if (si_t > 0 or di_t > 0) else 0
f_vars = {"avgTA": avgTA, "rsst_acc": rsst, "ch_rec": ch_rec, "ch_inv": ch_inv,
"soft_assets": soft, "ch_cs": ch_cs, "ch_roa": ch_roa, "issue": issue}
x = (-7.893 + 0.790*rsst + 2.518*ch_rec + 1.191*ch_inv + 1.979*soft
+ 0.171*ch_cs - 0.932*ch_roa + 1.029*issue)
f_score = (math.exp(x) / (1 + math.exp(x))) / 0.0037
except Exception:
f_score = None
# ---------------- 4. Benford ----------------
accn_info = {}
for tag, node in USGAAP.items():
for f in node.get("units", {}).get("USD", []):
if f.get("form") in ("10-K", "10-K/A") and f.get("accn"):
cur = accn_info.get(f["accn"])
if cur is None or f.get("filed", "") > cur.get("filed", ""):
accn_info[f["accn"]] = f
benford = None
if accn_info:
accn = max(accn_info, key=lambda a: accn_info[a].get("filed", ""))
seen, vals = set(), []
for tag, node in USGAAP.items():
for f in node.get("units", {}).get("USD", []):
if f.get("accn") != accn:
continue
v = abs(f["val"])
if v < 10:
continue
key = (tag, f.get("start"), f["end"])
if key in seen:
continue
seen.add(key)
vals.append(v)
n = len(vals)
if n:
counts = {d: 0 for d in range(1, 10)}
for v in vals:
counts[int(str(v)[0])] += 1
mad = sum(abs(counts[d]/n - math.log10(1 + 1/d)) for d in range(1, 10)) / 9
benford = {"accession": accn, "form": accn_info[accn].get("form"),
"filed": accn_info[accn].get("filed"), "n": n, "mad": mad}
# ---------------- 5. restatement filings ----------------
notices = []
for f in FILINGS:
form = f.get("form", "")
items = str(f.get("items") or "")
if (form == "8-K" and "4.02" in items) or form in ("10-K/A", "10-Q/A"):
notices.append({"form": form, "filed": f.get("filed"), "url": f.get("url"), "items": f.get("items")})
notices.sort(key=lambda x: x.get("filed") or "")
first_402 = min((n["filed"] for n in notices if n["form"] == "8-K" and n.get("filed")), default=None)
resp = [{"form": f.get("form"), "filed": f.get("filed"), "url": f.get("url")}
for f in FILINGS
if f.get("form") in ("CORRESP", "UPLOAD") and first_402
and (f.get("filed") or "") > first_402]
result = {
"deltas": deltas,
"beneish": {"m_score": m_score, "ratios": ratios, "imputed": imputed,
"missing": sorted(set(missing)) if m_score is None else []},
"dechow_f": {"f_score": f_score, "variables": f_vars},
"benford": benford,
"restatement_filings": {"notices": notices,
"corresp_upload_after_first_402": resp,
"count_corresp_upload": len(resp)},
}
json.dump(result, open("/out/result.json", "w"), indent=2, default=str)