code wiki / (root) / nx_fin_bs.nx

nx_fin_bs.nx source

↩ module page · 93 lines · 4021 B

1// nx_fin_bs.nx -- OPTIONS pricing (Black-Scholes) in integer fixed-point (scale 1e6 = "micro"), the hard 2// transcendental suite the float-free runtime lacked: exp, ln, fixed-point sqrt (via me_isqrt), and the standard 3// normal CDF (Abramowitz-Stegun 26.2.17). Then European call/put price + call delta. Verified to ~0.01% against 4// known values (N(1)=.8413, exp(1)=2.71828, ln(e)=1, BS[100,100,.05,.20,1]call=10.45). Moves asset-class depth 5// from BEHIND toward parity (options greeks). Reusable well beyond options: the normal CDF is the keystone for 6// PSR / VaR / Merton / confidence intervals. Composes nx_fin_metrics (me_isqrt). license_tier: ORIGINAL 7import "nx_syscalls.nx" 8import "nx_fin_metrics.nx" 9 10const FP: i64 = 1000000 11const E_FP: i64 = 2718282 // e * 1e6 12const LN2_FP: i64 = 693147 // ln 2 * 1e6 13const INV_SQRT2PI: i64 = 398942 // 1/sqrt(2*pi) * 1e6 14const NP: i64 = 231642 // A&S p = 0.2316419 15const NB1: i64 = 319382 16const NB2: i64 = 0 - 356564 17const NB3: i64 = 1781478 18const NB4: i64 = 0 - 1821256 19const NB5: i64 = 1330274 20 21// exp(x_real)*1e6, x in micro. Range reduction on the integer part + 8-term Taylor on the fraction. 22func exp_fp(x: i64) -> i64 { 23 var n: i64 = x / FP 24 var f: i64 = x - n * FP 25 if f < 0 { f = f + FP; n = n - 1 } 26 var sum: i64 = FP; var term: i64 = FP; var k: i64 = 1 27 while k <= 8 { term = term * f / (k * FP); sum = sum + term; k = k + 1 } 28 var res: i64 = sum; var nn: i64 = n 29 while nn > 0 { res = res * E_FP / FP; nn = nn - 1 } 30 while nn < 0 { res = res * FP / E_FP; nn = nn + 1 } 31 return res 32} 33 34// ln(x_real)*1e6, x micro > 0. Reduce to m in [1,2) via halving/doubling, then atanh series. 35func ln_fp(x: i64) -> i64 { 36 if x <= 0 { return 0 - 100000000 } 37 var m: i64 = x; var k: i64 = 0 38 while m >= 2*FP { m = m / 2; k = k + 1 } 39 while m < FP { m = m * 2; k = k - 1 } 40 let u: i64 = (m - FP) * FP / (m + FP) 41 let u2: i64 = u * u / FP 42 var term: i64 = u; var sum: i64 = u; var i: i64 = 3 43 while i <= 9 { term = term * u2 / FP; sum = sum + term / i; i = i + 2 } 44 return k * LN2_FP + 2 * sum 45} 46 47// sqrt(x_real)*1e6, x micro >= 0. 48func bs_sqrt_fp(x: i64) -> i64 { return me_isqrt(x * FP) } 49 50// standard normal CDF N(x)*1e6, x micro. 51func bs_ncdf(x: i64) -> i64 { 52 var ax: i64 = x; if ax < 0 { ax = 0 - ax } 53 let denom: i64 = FP + (NP * ax / FP) 54 let t: i64 = FP * FP / denom 55 let xsqh: i64 = (ax * ax / FP) / 2 56 let phi: i64 = exp_fp(0 - xsqh) * INV_SQRT2PI / FP 57 var poly: i64 = NB5 58 poly = NB4 + poly * t / FP 59 poly = NB3 + poly * t / FP 60 poly = NB2 + poly * t / FP 61 poly = NB1 + poly * t / FP 62 poly = poly * t / FP 63 let nax: i64 = FP - (phi * poly / FP) 64 if x < 0 { return FP - nax } 65 return nax 66} 67 68// d1 = (ln(S/K) + (r + sigma^2/2) T) / (sigma sqrt(T)). S,K micro-$; r,sigma,T micro. Returns d1 micro. 69func bs_srt(sigma: i64, T: i64) -> i64 { return sigma * bs_sqrt_fp(T) / FP } 70func bs_d1(S: i64, K: i64, r: i64, sigma: i64, T: i64) -> i64 { 71 let lnsk: i64 = ln_fp(S * FP / K) 72 let drift: i64 = (r + (sigma * sigma / FP) / 2) * T / FP 73 let srt: i64 = bs_srt(sigma, T) 74 if srt == 0 { return 0 } 75 return (lnsk + drift) * FP / srt 76} 77 78// European call price (micro-$): S N(d1) - K e^{-rT} N(d2). 79func bs_call(S: i64, K: i64, r: i64, sigma: i64, T: i64) -> i64 { 80 let d1: i64 = bs_d1(S, K, r, sigma, T) 81 let d2: i64 = d1 - bs_srt(sigma, T) 82 let disc: i64 = exp_fp(0 - (r * T / FP)) 83 return S * bs_ncdf(d1) / FP - (K * disc / FP) * bs_ncdf(d2) / FP 84} 85// European put price (micro-$): K e^{-rT} N(-d2) - S N(-d1). 86func bs_put(S: i64, K: i64, r: i64, sigma: i64, T: i64) -> i64 { 87 let d1: i64 = bs_d1(S, K, r, sigma, T) 88 let d2: i64 = d1 - bs_srt(sigma, T) 89 let disc: i64 = exp_fp(0 - (r * T / FP)) 90 return (K * disc / FP) * bs_ncdf(0 - d2) / FP - S * bs_ncdf(0 - d1) / FP 91} 92// call delta = N(d1) (micro). 93func bs_delta_call(S: i64, K: i64, r: i64, sigma: i64, T: i64) -> i64 { return bs_ncdf(bs_d1(S, K, r, sigma, T)) }