nx_bond_gate.nx source
↩ module page · 111 lines · 5410 B
1// nx_bond_gate.nx -- FIXED-INCOME VALUATION GATE.
2// Proves exact integer bond pricing (par/discount/premium), yield-to-maturity by bisection round-tripping
3// on both sides of par, the fail-closed refusals (unreachable price, non-positive price), Macaulay and
4// modified duration, current yield, and price monotonicity in yield. All values hand-verified with the
5// same downward-truncation the library uses. license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
6
7import "nx_bond_lib.nx"
8import "nx_matter_lib.nx"
9
10func bg_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
11func bg_putn(v: i64) -> i64 {
12 let t: *u8 = sys_mmap(32)
13 var o: i64 = 0
14 var m: i64 = v
15 if m < 0 { t[o] = 45 as u8; o = o + 1; m = 0 - m }
16 let d: *u8 = sys_mmap(32)
17 var k: i64 = 0
18 if m == 0 { d[0] = 48 as u8; k = 1 }
19 while m > 0 { d[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
20 var i: i64 = 0
21 while i < k { t[o] = d[k - 1 - i]; o = o + 1; i = i + 1 }
22 sys_write(1, t, o)
23 return 0
24}
25func bg_ck(cnt: *i64, name: *u8, got: i64, want: i64) -> i64 {
26 if got == want {
27 cnt[0] = cnt[0] + 1
28 bg_puts(" PASS " as *u8); bg_puts(name); bg_puts(" = " as *u8); bg_putn(got); bg_puts("\n" as *u8)
29 return 1
30 }
31 cnt[1] = cnt[1] + 1
32 bg_puts(" FAIL " as *u8); bg_puts(name); bg_puts(" got " as *u8); bg_putn(got)
33 bg_puts(" want " as *u8); bg_putn(want); bg_puts("\n" as *u8)
34 return 0
35}
36func bg_ckstr(cnt: *i64, name: *u8, got: *u8, want: *u8) -> i64 {
37 if mt_streq(got, want) == 1 {
38 cnt[0] = cnt[0] + 1
39 bg_puts(" PASS " as *u8); bg_puts(name); bg_puts(" = " as *u8); bg_puts(got); bg_puts("\n" as *u8)
40 return 1
41 }
42 cnt[1] = cnt[1] + 1
43 bg_puts(" FAIL " as *u8); bg_puts(name); bg_puts(" got " as *u8); bg_puts(got); bg_puts("\n" as *u8)
44 return 0
45}
46
47func main(argc: i64, argv: *i64) -> i64 {
48 let cnt: *i64 = sys_mmap(16) as *i64
49 cnt[0] = 0
50 cnt[1] = 0
51
52 bg_puts("NISHI-BOND-GATE (fixed-income: price, YTM by bisection, duration, integer-exact, fail-closed)\n" as *u8)
53
54 // 10pct coupon, face 1000, 3 periods. ---- PRICE across the yield curve ----
55 bg_ck(cnt, "B0 max price at ytm 0 = 1300 (all cashflows undiscounted)" as *u8, bond_price(1000, 1000, 3, 0), 1300)
56 bg_ck(cnt, "B1 price at ytm 10pct ~ par = 998 (integer truncation)" as *u8, bond_price(1000, 1000, 3, 1000), 998)
57 bg_ck(cnt, "B2 price at ytm 15pct = 883 (DISCOUNT, yield above coupon)" as *u8, bond_price(1000, 1000, 3, 1500), 883)
58 bg_ck(cnt, "B3 price at ytm 5pct = 1134 (PREMIUM, yield below coupon)" as *u8, bond_price(1000, 1000, 3, 500), 1134)
59
60 // ---- monotonicity: price strictly decreasing in yield ----
61 let p5: i64 = bond_price(1000, 1000, 3, 500)
62 let p10: i64 = bond_price(1000, 1000, 3, 1000)
63 let p15: i64 = bond_price(1000, 1000, 3, 1500)
64 var mono: i64 = 0
65 if p5 > p10 {
66 if p10 > p15 { mono = 1 }
67 }
68 bg_ck(cnt, "B4 price strictly decreasing in yield (5>10>15pct)" as *u8, mono, 1)
69
70 // ---- *YTM by bisection: recover the yield, prove it round-trips to the exact input price ----
71 bg_ck(cnt, "B5 YTM round-trip on discount price 883" as *u8,
72 bond_price(1000, 1000, 3, bond_ytm(883, 1000, 1000, 3)), 883)
73 bg_ck(cnt, "B6 YTM round-trip on premium price 1134" as *u8,
74 bond_price(1000, 1000, 3, bond_ytm(1134, 1000, 1000, 3)), 1134)
75
76 // ---- *FAIL-CLOSED: a price above the zero-yield max (1300) needs a negative yield -> REFUSE ----
77 bg_ck(cnt, "B7 unreachable price 1400 -> BOND_NO_CONVERGE" as *u8, bond_ytm(1400, 1000, 1000, 3), BOND_NO_CONVERGE)
78 bg_ck(cnt, "B8 price 0 -> BOND_BAD" as *u8, bond_ytm(0, 1000, 1000, 3), BOND_BAD)
79 bg_ck(cnt, "B8a negative price -> BOND_BAD" as *u8, bond_ytm(0 - 50, 1000, 1000, 3), BOND_BAD)
80
81 // ---- DURATION: PV-weighted average time, then modified = Macaulay/(1+y) ----
82 let mac: i64 = bond_macaulay_duration(1000, 1000, 3, 1000)
83 bg_ck(cnt, "B9 Macaulay duration = 2737 (2.737 periods, < 3yr maturity)" as *u8, mac, 2737)
84 let modd: i64 = bond_modified_duration(mac, 1000)
85 bg_ck(cnt, "B10 modified duration = 2488 (2.488)" as *u8, modd, 2488)
86 var modlt: i64 = 0
87 if modd < mac {
88 if modd > 0 { modlt = 1 }
89 }
90 bg_ck(cnt, "B11 modified duration < Macaulay (rate-risk invariant)" as *u8, modlt, 1)
91
92 // ---- current yield: for the discount bond it exceeds the 10pct coupon ----
93 bg_ck(cnt, "B12 current yield on price 883 = 1132 bp (11.32pct > 10pct coupon)" as *u8,
94 bond_current_yield_bp(1000, 1000, 883), 1132)
95
96 // ---- price classification vs face ----
97 bg_ckstr(cnt, "B13 class of 883 = DISCOUNT" as *u8, bond_price_class(883, 1000), "DISCOUNT" as *u8)
98 bg_ckstr(cnt, "B14 class of 1134 = PREMIUM" as *u8, bond_price_class(1134, 1000), "PREMIUM" as *u8)
99 bg_ckstr(cnt, "B15 class of 1000 = PAR" as *u8, bond_price_class(1000, 1000), "PAR" as *u8)
100
101 bg_puts("nx_bond_gate: pass=" as *u8); bg_putn(cnt[0])
102 bg_puts(" fail=" as *u8); bg_putn(cnt[1]); bg_puts("\n" as *u8)
103 if cnt[1] == 0 {
104 bg_puts("BOND nx_bond: VERDICT=GREEN (exact price + YTM by bisection + duration; refuses an unreachable yield)\n" as *u8)
105 sys_exit(0)
106 return 0
107 }
108 bg_puts("BOND nx_bond: VERDICT=RED\n" as *u8)
109 sys_exit(1)
110 return 1
111}