code wiki / (root) / nx_bond_gate.nx

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}