code wiki / (root) / nx_debt_strategy.nx

nx_debt_strategy.nx source

↩ module page · 112 lines · 4664 B

1// nx_debt_strategy.nx -- FIRST module of the comprehensive money system: a debt-payoff strategist. Given a set of 2// debts (balance, APR, minimum) and a monthly extra budget, it simulates AVALANCHE (attack highest APR -> minimises 3// interest) vs SNOWBALL (attack lowest balance -> fastest wins, motivation) month-by-month with real compounding, 4// and reports months-to-freedom + total interest for each. Integer-exact (cents / basis-points), deterministic, no 5// look-ahead. Budget-covers-minimums assumed; if a person cannot even meet minimums the sim returns months=-1 6// (UNDERWATER -- an honest hardship signal that routes to the relief modules). license_tier: ORIGINAL 7import "nx_syscalls.nx" 8const DS_MAGIC_120000: i64 = 120000 9 10const DS_FIELDS: i64 = 3 // per debt: [balance_cents, apr_bps, min_payment_cents] 11const DS_AVALANCHE: i64 = 0 12const DS_SNOWBALL: i64 = 1 13const DS_MAX_MONTHS: i64 = 6000 // 500-year guard -> underwater sentinel 14 15// one month's simple interest in cents: balance * (apr_bps/10000) / 12 = balance*apr_bps/120000. 16func ds_month_interest(balance: i64, apr_bps: i64) -> i64 { 17 if balance <= 0 { return 0 } 18 return balance * apr_bps / DS_MAGIC_120000 19} 20 21// index of the highest-APR debt still owed (avalanche target), or -1 if all paid. 22func ds_avalanche_target(bal: *i64, debts: *i64, n: i64) -> i64 { 23 var best: i64 = 0 - 1; var bestapr: i64 = 0 - 1; var i: i64 = 0 24 while i < n { 25 if bal[i] > 0 { 26 let apr: i64 = debts[i*DS_FIELDS+1] 27 if apr > bestapr { bestapr = apr; best = i } 28 } 29 i = i + 1 30 } 31 return best 32} 33 34// index of the smallest-balance debt still owed (snowball target), or -1 if all paid. 35func ds_snowball_target(bal: *i64, n: i64) -> i64 { 36 var best: i64 = 0 - 1; var bestbal: i64 = 0; var i: i64 = 0 37 while i < n { 38 if bal[i] > 0 { 39 if best == (0 - 1) { best = i; bestbal = bal[i] } 40 else { if bal[i] < bestbal { bestbal = bal[i]; best = i } } 41 } 42 i = i + 1 43 } 44 return best 45} 46 47// simulate payoff. strategy = DS_AVALANCHE|DS_SNOWBALL. out[0]=months (-1 = underwater), out[1]=total interest cents. 48func ds_simulate(debts: *i64, n: i64, extra: i64, strategy: i64, out: *i64) -> i64 { 49 let bal: *i64 = sys_mmap(8 * (n + 2)) as *i64 50 var budget: i64 = extra 51 var i: i64 = 0 52 while i < n { bal[i] = debts[i*DS_FIELDS+0]; budget = budget + debts[i*DS_FIELDS+2]; i = i + 1 } 53 var months: i64 = 0 54 var total_interest: i64 = 0 55 var run: i64 = 1 56 while run == 1 { 57 var owed: i64 = 0; var j: i64 = 0 58 while j < n { if bal[j] > 0 { owed = 1 } j = j + 1 } 59 if owed == 0 { run = 0 } 60 else { 61 if months >= DS_MAX_MONTHS { run = 0; months = 0 - 1 } 62 else { 63 var a: i64 = 0 64 while a < n { 65 if bal[a] > 0 { 66 let it: i64 = ds_month_interest(bal[a], debts[a*DS_FIELDS+1]) 67 bal[a] = bal[a] + it; total_interest = total_interest + it 68 } 69 a = a + 1 70 } 71 var avail: i64 = budget 72 var m: i64 = 0 73 while m < n { 74 if bal[m] > 0 { 75 var p: i64 = debts[m*DS_FIELDS+2] 76 if p > bal[m] { p = bal[m] } 77 if p > avail { p = avail } 78 bal[m] = bal[m] - p; avail = avail - p 79 } 80 m = m + 1 81 } 82 var casc: i64 = 1 83 while casc == 1 { 84 if avail <= 0 { casc = 0 } 85 else { 86 var t: i64 = 0 - 1 87 if strategy == DS_SNOWBALL { t = ds_snowball_target(bal, n) } 88 else { t = ds_avalanche_target(bal, debts, n) } 89 if t < 0 { casc = 0 } 90 else { 91 var p2: i64 = avail 92 if p2 > bal[t] { p2 = bal[t] } 93 bal[t] = bal[t] - p2; avail = avail - p2 94 } 95 } 96 } 97 months = months + 1 98 } 99 } 100 } 101 out[0] = months 102 out[1] = total_interest 103 return months 104} 105 106// can the person even make the minimums after interest? 1 = yes, 0 = underwater (routes to relief modules). 107func ds_can_meet_minimums(debts: *i64, n: i64, extra: i64) -> i64 { 108 let out: *i64 = sys_mmap(16) as *i64 109 ds_simulate(debts, n, extra, DS_AVALANCHE, out) 110 if out[0] < 0 { return 0 } 111 return 1 112}