nx_fin_metrics.nx source
↩ module page · 47 lines · 2234 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"
8import "nx_vecmath.nx"
9
10// floor(sqrt(n)) by Newton's method. n<=0 -> 0.
11func me_isqrt(n: i64) -> i64 { return vm_isqrt(n) }
12
13func 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 }
14
15// mean of the returns (same units as input, e.g. bps)
16func me_mean(vals: *i64, n: i64) -> i64 { if n <= 0 { return 0 } return me_sum(vals, n) / n }
17
18// population standard deviation (same units as input)
19func me_stddev(vals: *i64, n: i64) -> i64 {
20 if n <= 0 { return 0 }
21 let mean: i64 = me_mean(vals, n)
22 var ss: i64 = 0; var i: i64 = 0
23 while i < n { let d: i64 = vals[i] - mean; ss = ss + d*d; i = i + 1 }
24 return me_isqrt(ss / n)
25}
26
27// Sharpe ratio x1000 = (mean - rf) / stddev. rf in same units as returns. sd==0 -> 0.
28func me_sharpe_milli(vals: *i64, n: i64, rf: i64) -> i64 {
29 let sd: i64 = me_stddev(vals, n)
30 if sd <= 0 { return 0 }
31 return (me_mean(vals, n) - rf) * 1000 / sd
32}
33
34// downside deviation vs a minimum-acceptable-return (mar); only returns below mar contribute; denominator = n.
35func me_downside_dev(vals: *i64, n: i64, mar: i64) -> i64 {
36 if n <= 0 { return 0 }
37 var ss: i64 = 0; var i: i64 = 0
38 while i < n { if vals[i] < mar { let d: i64 = vals[i] - mar; ss = ss + d*d } i = i + 1 }
39 return me_isqrt(ss / n)
40}
41
42// Sortino ratio x1000 = (mean - mar) / downside_dev. dd==0 -> 0.
43func me_sortino_milli(vals: *i64, n: i64, mar: i64) -> i64 {
44 let dd: i64 = me_downside_dev(vals, n, mar)
45 if dd <= 0 { return 0 }
46 return (me_mean(vals, n) - mar) * 1000 / dd
47}