nx_beneish_lib.nx source
↩ module page · 56 lines · 3183 B
1// nx_beneish_lib.nx -- BENEISH M-SCORE: forensic detection of earnings manipulation, integer-exact.
2//
3// The fourth lens on a company, and the darkest one: Altman asks if it is in distress, Piotroski if it
4// is strengthening, mgmt-grade if the stewards are good -- Beneish asks if the numbers are being COOKED.
5// It is the standard forensic-accounting model (Beneish 1999); students using it flagged Enron a year
6// before it collapsed. Eight indices comparing this year to last across receivables, margins, asset
7// quality, sales growth, depreciation, SG&A, accruals, and leverage combine into one M-score; above the
8// -1.78 threshold the profile matches known manipulators.
9//
10// M = -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
11//
12// NO FLOATS: each index is scaled x1000 (a ratio of 1.000 -> 1000, i.e. basis points), coefficients are
13// scaled x1000, the accumulation is x1e6, and the result is returned as M x1000 -- exact integer
14// arithmetic, the only reproducible way to score fraud. Threshold is DATA.
15//
16// ★FAIL-CLOSED + COMPOSES: the missing-input sentinel BEN_NA is the pipeline value (-999999999). If any
17// index could not be computed from the filings, ben_mscore returns BEN_INCOMPLETE rather than a fraud
18// verdict on partial data -- accusing a company of cooking books on missing facts is the worst false signal.
19// license_tier: ORIGINAL No hw writes (Rule 26). LIB.
20
21import "nx_matter_lib.nx"
22const BEN_MAGIC_4840000: i64 = 4840000
23const BEN_MAGIC_4679: i64 = 4679
24
25const BEN_NA: i64 = 0 - 999999999 // a required index is missing (matches the pipeline sentinel)
26const BEN_INCOMPLETE: i64 = 0 - 2000000000 // M not computable (well outside the real M x1000 range)
27const BEN_THRESHOLD: i64 = 0 - 1780 // M x1000; ABOVE this the profile matches manipulators
28
29// M-score x1000 from the eight indices, each scaled x1000 (1.000 ratio -> 1000).
30func ben_mscore(dsri: i64, gmi: i64, aqi: i64, sgi: i64, depi: i64, sgai: i64, tata: i64, lvgi: i64) -> i64 {
31 if dsri == BEN_NA { return BEN_INCOMPLETE }
32 if gmi == BEN_NA { return BEN_INCOMPLETE }
33 if aqi == BEN_NA { return BEN_INCOMPLETE }
34 if sgi == BEN_NA { return BEN_INCOMPLETE }
35 if depi == BEN_NA { return BEN_INCOMPLETE }
36 if sgai == BEN_NA { return BEN_INCOMPLETE }
37 if tata == BEN_NA { return BEN_INCOMPLETE }
38 if lvgi == BEN_NA { return BEN_INCOMPLETE }
39 // coefficients x1000; index x1000; product x1e6; constant -4.84 x1e6 = -4840000
40 let raw: i64 = 0 - BEN_MAGIC_4840000 + 920 * dsri + 528 * gmi + 404 * aqi + 892 * sgi + 115 * depi - 172 * sgai + BEN_MAGIC_4679 * tata - 327 * lvgi
41 return raw / 1000
42}
43
44// 1 if the M-score is above the manipulation threshold. INCOMPLETE is NOT a manipulator flag (returns 0).
45func ben_is_manipulator(m1000: i64) -> i64 {
46 if m1000 == BEN_INCOMPLETE { return 0 }
47 if m1000 > BEN_THRESHOLD { return 1 }
48 return 0
49}
50
51// human verdict.
52func ben_verdict_str(m1000: i64) -> *u8 {
53 if m1000 == BEN_INCOMPLETE { return "INCOMPLETE" as *u8 }
54 if m1000 > BEN_THRESHOLD { return "MANIPULATION-RISK" as *u8 }
55 return "LIKELY-CLEAN" as *u8
56}