code wiki / (root) / nx_money_situation.nx

nx_money_situation.nx source

↩ module page · 83 lines · 5201 B

1// nx_money_situation.nx -- the FOUNDATION of the comprehensive money system: a person's whole financial SITUATION 2// (income, expenses, debts, assets, goals) + the derived metrics every module reads so advice is "optimal for THEIR 3// situation", not generic. Integer-exact (cents / basis-points). Composes nx_debt_strategy (debts layout + underwater 4// check). The keystone is sit_priority(): given the WHOLE picture it routes to the right focus -- relief, emergency 5// fund, debt, or invest -- the decision the operator asked for. license_tier: ORIGINAL 6import "nx_syscalls.nx" 7import "nx_debt_strategy.nx" // DS_FIELDS + ds_can_meet_minimums (debts[i*3] = [balance, apr_bps, min]) 8const SIT_MAGIC_10000: i64 = 10000 9 10// Situation = a flat i64 array (SIT_FIELDS long) + a separate debts array. 11const SIT_INCOME_GROSS: i64 = 0 // monthly gross income cents 12const SIT_INCOME_NET: i64 = 1 // monthly after-tax income cents 13const SIT_EXPENSES_ESSENTIAL: i64 = 2 // monthly essentials cents (housing/food/utilities/transport/insurance) 14const SIT_SPEND_DISCRETIONARY: i64 = 3 // monthly discretionary spend cents 15const SIT_CASH: i64 = 4 // liquid cash/savings cents 16const SIT_RETIREMENT: i64 = 5 // retirement assets cents 17const SIT_OTHER_ASSETS: i64 = 6 // other assets cents 18const SIT_DEPENDENTS: i64 = 7 // number of dependents 19const SIT_EMPLOYED: i64 = 8 // 1 = employed, 0 = not 20const SIT_EFUND_TARGET_MONTHS: i64 = 9 // desired emergency fund, in months of essentials 21const SIT_FIELDS: i64 = 16 22 23// documented lending/planning STANDARDS (not tuned magic -- cited conventions): 24const DTI_HEALTHY_BPS: i64 = 3600 // back-end DTI < 36% = healthy (conventional-mortgage guideline) 25const DTI_STRESSED_BPS: i64 = 4300 // 36-43% stretched; > 43% exceeds the CFPB ability-to-repay QM limit 26const EFUND_MIN_MONTHS: i64 = 3 // 3-6 months of essentials = standard emergency fund floor 27 28// --- aggregates --- 29func sit_total_debt(debts: *i64, ndebts: i64) -> i64 { var s: i64 = 0; var i: i64 = 0; while i < ndebts { s = s + debts[i*DS_FIELDS+0]; i = i + 1 } return s } 30func sit_total_min(debts: *i64, ndebts: i64) -> i64 { var s: i64 = 0; var i: i64 = 0; while i < ndebts { s = s + debts[i*DS_FIELDS+2]; i = i + 1 } return s } 31func sit_total_assets(sit: *i64) -> i64 { return sit[SIT_CASH] + sit[SIT_RETIREMENT] + sit[SIT_OTHER_ASSETS] } 32func sit_net_worth(sit: *i64, debts: *i64, ndebts: i64) -> i64 { return sit_total_assets(sit) - sit_total_debt(debts, ndebts) } 33 34// --- monthly cash flow --- 35// surplus = net income - essentials - discretionary - debt minimums (what is actually left; may be negative) 36func sit_monthly_surplus(sit: *i64, debts: *i64, ndebts: i64) -> i64 { 37 return sit[SIT_INCOME_NET] - sit[SIT_EXPENSES_ESSENTIAL] - sit[SIT_SPEND_DISCRETIONARY] - sit_total_min(debts, ndebts) 38} 39// max debt paydown = what COULD attack debt if discretionary spend were fully cut (feeds ds_simulate extra). Floored at 0. 40func sit_max_debt_paydown(sit: *i64, debts: *i64, ndebts: i64) -> i64 { 41 let v: i64 = sit[SIT_INCOME_NET] - sit[SIT_EXPENSES_ESSENTIAL] - sit_total_min(debts, ndebts) 42 if v < 0 { return 0 } 43 return v 44} 45 46// --- ratios (basis points) --- 47func sit_dti_bps(sit: *i64, debts: *i64, ndebts: i64) -> i64 { 48 if sit[SIT_INCOME_GROSS] <= 0 { return 0 } 49 return sit_total_min(debts, ndebts) * SIT_MAGIC_10000 / sit[SIT_INCOME_GROSS] 50} 51func sit_savings_rate_bps(sit: *i64, debts: *i64, ndebts: i64) -> i64 { 52 if sit[SIT_INCOME_NET] <= 0 { return 0 } 53 return sit_monthly_surplus(sit, debts, ndebts) * SIT_MAGIC_10000 / sit[SIT_INCOME_NET] 54} 55 56// --- emergency fund --- 57func sit_efund_months_x100(sit: *i64) -> i64 { // months of essentials covered by cash, x100 58 if sit[SIT_EXPENSES_ESSENTIAL] <= 0 { return 0 } 59 return sit[SIT_CASH] * 100 / sit[SIT_EXPENSES_ESSENTIAL] 60} 61func sit_efund_target_cents(sit: *i64) -> i64 { return sit[SIT_EFUND_TARGET_MONTHS] * sit[SIT_EXPENSES_ESSENTIAL] } 62func sit_efund_gap_cents(sit: *i64) -> i64 { let g: i64 = sit_efund_target_cents(sit) - sit[SIT_CASH]; if g < 0 { return 0 } return g } 63 64// --- health (documented thresholds) --- 0 = healthy(<36%), 1 = stretched(36-43%), 2 = distress(>43%) 65func sit_dti_status(sit: *i64, debts: *i64, ndebts: i64) -> i64 { 66 let d: i64 = sit_dti_bps(sit, debts, ndebts) 67 if d < DTI_HEALTHY_BPS { return 0 } 68 if d <= DTI_STRESSED_BPS { return 1 } 69 return 2 70} 71 72// --- the KEYSTONE: situation-aware priority router. What should THIS person focus on FIRST? --- 73// 0 = CRISIS / relief (negative monthly surplus, or debt underwater even at max paydown) -> hardship modules 74// 1 = build EMERGENCY FUND (below the standard floor) 75// 2 = attack DEBT (has debt, emergency fund ok) 76// 3 = INVEST / grow (no debt, emergency fund ok, surplus) 77func sit_priority(sit: *i64, debts: *i64, ndebts: i64) -> i64 { 78 if sit_monthly_surplus(sit, debts, ndebts) < 0 { return 0 } 79 if ds_can_meet_minimums(debts, ndebts, sit_max_debt_paydown(sit, debts, ndebts)) == 0 { return 0 } 80 if sit_efund_months_x100(sit) < EFUND_MIN_MONTHS * 100 { return 1 } 81 if sit_total_debt(debts, ndebts) > 0 { return 2 } 82 return 3 83}