nx_fin_metrics.nx source
↩ module page · 52 lines · 2325 B
1// nx_fin_metrics.nx -- risk-adjusted performance metrics, i64 fixed-point, NO floats. Provides the integer
2// sqrt primitive (me_isqrt, Newton's method) the float-free runtime needs, then mean / standard-deviation /
3// SHARPE / downside-deviation / SORTINO over a returns array (per-period returns in basis points). Sharpe and
4// Sortino are returned in MILLI-units (x1000) for integer resolution. Risk-free / MAR passed in (default 0).
5// These are the numbers that grade an edge HONESTLY -- reward per unit of risk, not raw return. Pure; composes
6// with nx_fin_backtest's per-trade/per-period returns. license_tier: ORIGINAL
7import "nx_syscalls.nx"
8
9// floor(sqrt(n)) by Newton's method. n<=0 -> 0.
10func me_isqrt(n: i64) -> i64 {
11 if n <= 0 { return 0 }
12 var x: i64 = n
13 var y: i64 = (x + 1) / 2
14 while y < x { x = y; y = (x + n/x) / 2 }
15 return x
16}
17
18func me_sum(vals: *i64, n: i64) -> i64 { var s: i64 = 0; var i: i64 = 0; while i < n { s = s + vals[i]; i = i + 1 } return s }
19
20// mean of the returns (same units as input, e.g. bps)
21func me_mean(vals: *i64, n: i64) -> i64 { if n <= 0 { return 0 } return me_sum(vals, n) / n }
22
23// population standard deviation (same units as input)
24func me_stddev(vals: *i64, n: i64) -> i64 {
25 if n <= 0 { return 0 }
26 let mean: i64 = me_mean(vals, n)
27 var ss: i64 = 0; var i: i64 = 0
28 while i < n { let d: i64 = vals[i] - mean; ss = ss + d*d; i = i + 1 }
29 return me_isqrt(ss / n)
30}
31
32// Sharpe ratio x1000 = (mean - rf) / stddev. rf in same units as returns. sd==0 -> 0.
33func me_sharpe_milli(vals: *i64, n: i64, rf: i64) -> i64 {
34 let sd: i64 = me_stddev(vals, n)
35 if sd <= 0 { return 0 }
36 return (me_mean(vals, n) - rf) * 1000 / sd
37}
38
39// downside deviation vs a minimum-acceptable-return (mar); only returns below mar contribute; denominator = n.
40func me_downside_dev(vals: *i64, n: i64, mar: i64) -> i64 {
41 if n <= 0 { return 0 }
42 var ss: i64 = 0; var i: i64 = 0
43 while i < n { if vals[i] < mar { let d: i64 = vals[i] - mar; ss = ss + d*d } i = i + 1 }
44 return me_isqrt(ss / n)
45}
46
47// Sortino ratio x1000 = (mean - mar) / downside_dev. dd==0 -> 0.
48func me_sortino_milli(vals: *i64, n: i64, mar: i64) -> i64 {
49 let dd: i64 = me_downside_dev(vals, n, mar)
50 if dd <= 0 { return 0 }
51 return (me_mean(vals, n) - mar) * 1000 / dd
52}