nx_piotroski_gate.nx source
↩ module page · 111 lines · 5921 B
1// nx_piotroski_gate.nx -- F-SCORE INDEPENDENT GATE.
2// Proves the exact 9-point score on a strong / weak / mixed firm, the grade mapping, and the fail-closed
3// composition with nx_xbrl: a firm with any missing (XB_NO_FACT) metric scores INCOMPLETE, not a number.
4// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
5
6import "nx_piotroski_lib.nx"
7
8func pg_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
9func pg_putn(v: i64) -> i64 {
10 let t: *u8 = sys_mmap(32)
11 var o: i64 = 0
12 var m: i64 = v
13 if m < 0 { t[o] = 45 as u8; o = o + 1; m = 0 - m }
14 let d: *u8 = sys_mmap(32)
15 var k: i64 = 0
16 if m == 0 { d[0] = 48 as u8; k = 1 }
17 while m > 0 { d[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
18 var i: i64 = 0
19 while i < k { t[o] = d[k - 1 - i]; o = o + 1; i = i + 1 }
20 sys_write(1, t, o)
21 return 0
22}
23func pg_ck(cnt: *i64, name: *u8, got: i64, want: i64) -> i64 {
24 if got == want {
25 cnt[0] = cnt[0] + 1
26 pg_puts(" PASS " as *u8); pg_puts(name); pg_puts(" = " as *u8); pg_putn(got); pg_puts("\n" as *u8)
27 return 1
28 }
29 cnt[1] = cnt[1] + 1
30 pg_puts(" FAIL " as *u8); pg_puts(name); pg_puts(" got " as *u8); pg_putn(got)
31 pg_puts(" want " as *u8); pg_putn(want); pg_puts("\n" as *u8)
32 return 0
33}
34func pg_ckstr(cnt: *i64, name: *u8, got: *u8, want: *u8) -> i64 {
35 if mt_streq(got, want) == 1 {
36 cnt[0] = cnt[0] + 1
37 pg_puts(" PASS " as *u8); pg_puts(name); pg_puts(" = " as *u8); pg_puts(got); pg_puts("\n" as *u8)
38 return 1
39 }
40 cnt[1] = cnt[1] + 1
41 pg_puts(" FAIL " as *u8); pg_puts(name); pg_puts(" got " as *u8); pg_puts(got); pg_puts("\n" as *u8)
42 return 0
43}
44
45// fill a metrics array. order = the declared contract.
46func pg_fill(m: *i64, ni: i64, roa: i64, roap: i64, cfo: i64, ltd: i64, ltdp: i64, cur: i64, curp: i64, sh: i64, shp: i64, gm: i64, gmp: i64, at: i64, atp: i64) -> i64 {
47 m[0] = ni; m[1] = roa; m[2] = roap; m[3] = cfo; m[4] = ltd; m[5] = ltdp; m[6] = cur
48 m[7] = curp; m[8] = sh; m[9] = shp; m[10] = gm; m[11] = gmp; m[12] = at; m[13] = atp
49 return 0
50}
51
52func main(argc: i64, argv: *i64) -> i64 {
53 let cnt: *i64 = sys_mmap(16) as *i64
54 cnt[0] = 0
55 cnt[1] = 0
56
57 pg_puts("NISHI-PIOTROSKI-GATE (F-score 0..9 fundamental strength, integer-exact, fail-closed on missing facts)\n" as *u8)
58
59 let m: *i64 = sys_mmap(8 * PIO_N) as *i64
60
61 // ---- P1: a STRONG firm passing all 9 tests -> 9 ----
62 // ROA 800bp>0, improving from 600; CFO 1200>0 and > NI 1000; LTD 2000<2500; current 15000>14000;
63 // shares 500<=500; gross margin 4200>4000; asset turnover 9000>8500.
64 pg_fill(m, 1000, 800, 600, 1200, 2000, 2500, 15000, 14000, 500, 500, 4200, 4000, 9000, 8500)
65 pg_ck(cnt, "P1 all-9-pass firm -> F-score 9" as *u8, pio_fscore(m), 9)
66 pg_ckstr(cnt, "P1a grade = STRONG" as *u8, pio_grade(pio_fscore(m)), "STRONG" as *u8)
67
68 // ---- P2: a WEAK firm failing all 9 -> 0 ----
69 // ROA -300<0, worsening from 100; CFO -200<0 and < NI -100; LTD 3000>2500; current 13000<14000;
70 // shares 600>500 (dilution); gross margin 3800<4000; asset turnover 8000<8500.
71 pg_fill(m, 0 - 100, 0 - 300, 100, 0 - 200, 3000, 2500, 13000, 14000, 600, 500, 3800, 4000, 8000, 8500)
72 pg_ck(cnt, "P2 all-9-fail firm -> F-score 0" as *u8, pio_fscore(m), 0)
73 pg_ckstr(cnt, "P2a grade = WEAK" as *u8, pio_grade(pio_fscore(m)), "WEAK" as *u8)
74
75 // ---- P3: a MIXED firm -- exactly the 4 profitability tests pass, the other 5 fail -> 4 ----
76 // ROA 500>0, improving from 400, CFO 900>0 and > NI 800 (T1-4 pass); LTD 3000>2500 (T5 fail),
77 // current 13000<14000 (T6 fail), shares 600>500 (T7 fail), gm 3800<4000 (T8 fail), at 8000<8500 (T9 fail).
78 pg_fill(m, 800, 500, 400, 900, 3000, 2500, 13000, 14000, 600, 500, 3800, 4000, 8000, 8500)
79 pg_ck(cnt, "P3 mixed firm (4 profitability pass, 5 fail) -> F-score 4" as *u8, pio_fscore(m), 4)
80 pg_ckstr(cnt, "P3a grade = MODERATE" as *u8, pio_grade(pio_fscore(m)), "MODERATE" as *u8)
81
82 // ---- P4: individual-test isolation -- flip ONLY the dilution test (T7) on the strong firm -> 8 ----
83 pg_fill(m, 1000, 800, 600, 1200, 2000, 2500, 15000, 14000, 700, 500, 4200, 4000, 9000, 8500)
84 pg_ck(cnt, "P4 strong firm but with dilution (shares 700>500) -> 8" as *u8, pio_fscore(m), 8)
85
86 // ---- P5: T4 accruals -- CFO must EXCEED net income; equal CFO==NI fails T4 -> 8 ----
87 pg_fill(m, 1200, 800, 600, 1200, 2000, 2500, 15000, 14000, 500, 500, 4200, 4000, 9000, 8500)
88 pg_ck(cnt, "P5 CFO == NI (not >) fails the accruals test -> 8" as *u8, pio_fscore(m), 8)
89
90 // ---- P6: FAIL-CLOSED -- a missing fact (PIO_NA, == nx_xbrl XB_NO_FACT) -> INCOMPLETE, not a number ----
91 pg_fill(m, 1000, 800, 600, 1200, 2000, 2500, 15000, 14000, 500, 500, 4200, 4000, 9000, 8500)
92 m[6] = PIO_NA // current ratio could not be parsed from the filing
93 pg_ck(cnt, "P6 a missing metric -> PIO_INCOMPLETE (-1), not a fabricated score" as *u8, pio_fscore(m), PIO_INCOMPLETE)
94 pg_ckstr(cnt, "P6a grade = INCOMPLETE (surfaced, not silently WEAK)" as *u8, pio_grade(pio_fscore(m)), "INCOMPLETE" as *u8)
95 pg_ck(cnt, "P6b pio_complete = 0 on missing data" as *u8, pio_complete(m), 0)
96
97 // ---- P7: complete data reports complete ----
98 pg_fill(m, 1000, 800, 600, 1200, 2000, 2500, 15000, 14000, 500, 500, 4200, 4000, 9000, 8500)
99 pg_ck(cnt, "P7 full data -> pio_complete = 1" as *u8, pio_complete(m), 1)
100
101 pg_puts("nx_piotroski_gate: pass=" as *u8); pg_putn(cnt[0])
102 pg_puts(" fail=" as *u8); pg_putn(cnt[1]); pg_puts("\n" as *u8)
103 if cnt[1] == 0 {
104 pg_puts("F-SCORE nx_piotroski: VERDICT=GREEN (exact 9-point score; INCOMPLETE on a missing fact, never a fabricated number)\n" as *u8)
105 sys_exit(0)
106 return 0
107 }
108 pg_puts("F-SCORE nx_piotroski: VERDICT=RED\n" as *u8)
109 sys_exit(1)
110 return 1
111}