code wiki / (root) / nx_var_lib.nx

nx_var_lib.nx source

↩ module page · 90 lines · 3955 B

1// nx_var_lib.nx -- F991 (risk core): VALUE-AT-RISK + EXPECTED SHORTFALL, integer-exact, fail-closed. 2// 3// A risk number computed from too few observations is worse than no number -- it looks authoritative and 4// is meaningless, and that is how blow-ups happen. So the sovereign property here is that the tail is 5// only reported when the data can support it: a 99% VaR needs at least ~100 observations to have a single 6// point in the tail, and asking for one from 10 data points REFUSES (fail-closed) rather than inventing a 7// quantile. Everything is integer minor units, no float -- a reproducible risk number an auditor can redo. 8// 9// Two measures: 10// Historical VaR -- the loss at the confidence quantile (the threshold a loss exceeds only (1-c) of the time) 11// Expected Shortfall (CVaR) -- the MEAN of the losses AT OR BEYOND the VaR quantile. This is the coherent, 12// sub-additive tail measure Basel III adopted over VaR; ES >= VaR always, and it sees 13// the shape of the tail VaR ignores. Reporting ES is the state of the art. 14// 15// Convention: losses[] are POSITIVE numbers (a loss of 500 = 500); larger = worse. 16// SCALE ENVELOPE (declared): var_sort is selection sort O(n^2) on a COPY (never mutates the caller's array); 17// a merge/quick sort is the rung for very long series. DRY: composes nx_matter_lib helpers. license_tier: ORIGINAL LIB. 18 19import "nx_matter_lib.nx" 20 21const VAR_BAD: i64 = 0 - 1 // invalid confidence (<=0 or >=100) 22const VAR_INSUFFICIENT: i64 = 0 - 2 // not enough observations for this confidence (fail-closed) 23 24// sort a[0..n) ascending, in place. selection sort (declared O(n^2)). 25func var_sort(a: *i64, n: i64) -> i64 { 26 var i: i64 = 0 27 while i < n { 28 var mn: i64 = i 29 var j: i64 = i + 1 30 while j < n { 31 if a[j] < a[mn] { mn = j } 32 j = j + 1 33 } 34 if mn != i { 35 let t: i64 = a[i] 36 a[i] = a[mn] 37 a[mn] = t 38 } 39 i = i + 1 40 } 41 return 0 42} 43 44// minimum observations to place at least one point in the (1-c) tail: ceil(100/(100-c)). 45func var_min_sample(confidence_pct: i64) -> i64 { 46 let d: i64 = 100 - confidence_pct 47 return (100 + d - 1) / d 48} 49 50// copy losses into a fresh sorted-ascending buffer; returns the buffer (or 0 on bad n). 51func var_sorted_copy(losses: *i64, n: i64) -> *i64 { 52 let s: *i64 = sys_mmap(8 * n) as *i64 53 var i: i64 = 0 54 while i < n { s[i] = losses[i]; i = i + 1 } 55 var_sort(s, n) 56 return s 57} 58 59// the quantile index for confidence c over n points. 60func var_index(n: i64, confidence_pct: i64) -> i64 { 61 var idx: i64 = (confidence_pct * n) / 100 62 if idx >= n { idx = n - 1 } 63 return idx 64} 65 66// ★HISTORICAL VaR at confidence c. Returns the tail-quantile loss, VAR_BAD on bad c, 67// VAR_INSUFFICIENT if n is too small for the confidence (fail-closed -- never invents a quantile). 68func var_historical(losses: *i64, n: i64, confidence_pct: i64) -> i64 { 69 if confidence_pct <= 0 { return VAR_BAD } 70 if confidence_pct >= 100 { return VAR_BAD } 71 if n < var_min_sample(confidence_pct) { return VAR_INSUFFICIENT } 72 let s: *i64 = var_sorted_copy(losses, n) 73 return s[var_index(n, confidence_pct)] 74} 75 76// ★EXPECTED SHORTFALL (CVaR): mean of the losses at or beyond the VaR quantile. Same fail-closed guards. 77// Integer mean (floor). ES >= VaR by construction. 78func var_expected_shortfall(losses: *i64, n: i64, confidence_pct: i64) -> i64 { 79 if confidence_pct <= 0 { return VAR_BAD } 80 if confidence_pct >= 100 { return VAR_BAD } 81 if n < var_min_sample(confidence_pct) { return VAR_INSUFFICIENT } 82 let s: *i64 = var_sorted_copy(losses, n) 83 let idx: i64 = var_index(n, confidence_pct) 84 var sum: i64 = 0 85 var cnt: i64 = 0 86 var i: i64 = idx 87 while i < n { sum = sum + s[i]; cnt = cnt + 1; i = i + 1 } 88 if cnt == 0 { return VAR_INSUFFICIENT } 89 return sum / cnt 90}