UNPKG

kestrel.markets

Version:

A typed, token-efficient language + runtime for agentic trading: agents author bounded plans, the runtime fires them at the tick. CLI + typed library + MCP server.

245 lines (242 loc) 7.6 kB
// src/fair/detmath.ts var _buf = new ArrayBuffer(8); var _f64 = new Float64Array(_buf); var _u32 = new Uint32Array(_buf); _f64[0] = 1; var HI = _u32[1] === 1072693248 ? 1 : 0; var LO = 1 - HI; function getHighWord(x) { _f64[0] = x; return _u32[HI] >>> 0; } function getLowWord(x) { _f64[0] = x; return _u32[LO] >>> 0; } function withHighWord(x, hw) { _f64[0] = x; _u32[HI] = hw >>> 0; return _f64[0]; } function fromWords(hw, lw) { _u32[HI] = hw >>> 0; _u32[LO] = lw >>> 0; return _f64[0]; } var CANONICAL_NAN = fromWords(2146959360, 0); var one = 1; var halF = [0.5, -0.5]; var o_threshold = 709.782712893384; var u_threshold = -745.1332191019411; var ln2HI = [0.6931471803691238, -0.6931471803691238]; var ln2LO = [0.00000000019082149292705877, -0.00000000019082149292705877]; var invln2 = 1.4426950408889634; var P1 = 0.16666666666666602; var P2 = -0.0027777777777015593; var P3 = 0.00006613756321437934; var P4 = -0.0000016533902205465252; var P5 = 0.000000041381367970572385; var huge = 1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000; var twom1000 = 0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009332636185032189; function dexp(x) { let hi = 0; let lo = 0; let k = 0; let hx = getHighWord(x); const xsb = hx >>> 31 & 1; hx &= 2147483647; if (hx >= 1082535490) { if (hx >= 2146435072) { const lx = getLowWord(x); if ((hx & 1048575 | lx) !== 0) return x + x; return xsb === 0 ? x : 0; } if (x > o_threshold) return huge * huge; if (x < u_threshold) return twom1000 * twom1000; } if (hx > 1071001154) { if (hx < 1072734898) { hi = x - ln2HI[xsb]; lo = ln2LO[xsb]; k = 1 - xsb - xsb; } else { k = invln2 * x + halF[xsb] | 0; const t2 = k; hi = x - t2 * ln2HI[0]; lo = t2 * ln2LO[0]; } x = hi - lo; } else if (hx < 1043333120) { if (huge + x > one) return one + x; } else { k = 0; } const t = x * x; const c = x - t * (P1 + t * (P2 + t * (P3 + t * (P4 + t * P5)))); let y; if (k === 0) return one - (x * c / (c - 2) - x); y = one - (lo - x * c / (2 - c) - hi); if (k >= -1021) { const hy2 = getHighWord(y); return withHighWord(y, hy2 + (k << 20) >>> 0); } const hy = getHighWord(y); return withHighWord(y, hy + (k + 1000 << 20) >>> 0) * twom1000; } var ln2_hi = 0.6931471803691238; var ln2_lo = 0.00000000019082149292705877; var two54 = 18014398509481984; var Lg1 = 0.6666666666666735; var Lg2 = 0.3999999999940942; var Lg3 = 0.2857142874366239; var Lg4 = 0.22222198432149784; var Lg5 = 0.1818357216161805; var Lg6 = 0.15313837699209373; var Lg7 = 0.14798198605116586; var zero = 0; function dlog(x) { let hx = getHighWord(x) | 0; const lx = getLowWord(x); let k = 0; if (hx < 1048576) { if ((hx & 2147483647 | lx) === 0) return -two54 / zero; if (hx < 0) return CANONICAL_NAN; k -= 54; x *= two54; hx = getHighWord(x) | 0; } if (hx >= 2146435072) return x + x; k += (hx >> 20) - 1023; hx &= 1048575; const i = hx + 614244 & 1048576; x = withHighWord(x, (hx | i ^ 1072693248) >>> 0); k += i >> 20; const f = x - 1; if ((1048575 & 2 + hx) < 3) { if (f === zero) { if (k === 0) return zero; const dk0 = k; return dk0 * ln2_hi + dk0 * ln2_lo; } const R0 = f * f * (0.5 - 0.3333333333333333 * f); if (k === 0) return f - R0; const dk1 = k; return dk1 * ln2_hi - (R0 - dk1 * ln2_lo - f); } const s = f / (2 + f); const dk = k; const z = s * s; let i2 = hx - 398458; const w = z * z; const j = 440401 - hx; const t1 = w * (Lg2 + w * (Lg4 + w * Lg6)); const t2 = z * (Lg1 + w * (Lg3 + w * (Lg5 + w * Lg7))); i2 |= j; const R = t2 + t1; if (i2 > 0) { const hfsq = 0.5 * f * f; if (k === 0) return f - (hfsq - s * (hfsq + R)); return dk * ln2_hi - (hfsq - (s * (hfsq + R) + dk * ln2_lo) - f); } if (k === 0) return f - s * (f - R); return dk * ln2_hi - (s * (f - R) - dk * ln2_lo - f); } // src/fair/black76.ts var MS_PER_YEAR = 365 * 24 * 60 * 60 * 1000; function erf(x) { const sign = x < 0 ? -1 : 1; const ax = Math.abs(x); const t = 1 / (1 + 0.3275911 * ax); const y = 1 - ((((1.061405429 * t - 1.453152027) * t + 1.421413741) * t - 0.284496736) * t + 0.254829592) * t * dexp(-ax * ax); return sign * y; } function normCdf(x) { return 0.5 * (1 + erf(x / Math.SQRT2)); } var INV_SQRT_2PI = 0.3989422804014327; function normPdf(x) { return INV_SQRT_2PI * dexp(-0.5 * x * x); } function greeks(p) { const { forward, strike, tauYears, sigma, right } = p; if (!(tauYears > 0) || !(sigma > 0) || !(forward > 0) || !(strike > 0)) { const itm = right === "C" ? forward > strike : forward < strike; const delta2 = right === "C" ? itm ? 1 : 0 : itm ? -1 : 0; return { delta: delta2, gamma: 0, vega: 0, theta: 0 }; } const sqrtT = Math.sqrt(tauYears); const vol = sigma * sqrtT; const d1 = (dlog(forward / strike) + 0.5 * vol * vol) / vol; const pdf = normPdf(d1); const delta = right === "C" ? normCdf(d1) : normCdf(d1) - 1; const gamma = pdf / (forward * vol); const vega = forward * pdf * sqrtT; const theta = -(forward * pdf * sigma) / (2 * sqrtT); return { delta, gamma, vega, theta }; } function intrinsic(spot, strike, right) { return right === "C" ? Math.max(spot - strike, 0) : Math.max(strike - spot, 0); } function black76(p) { const { forward, strike, tauYears, sigma, right } = p; const intr = intrinsic(forward, strike, right); if (!(tauYears > 0) || !(sigma > 0) || !(forward > 0) || !(strike > 0)) { return intr; } const sqrtT = Math.sqrt(tauYears); const vol = sigma * sqrtT; const d1 = (dlog(forward / strike) + 0.5 * vol * vol) / vol; const d2 = d1 - vol; const price = right === "C" ? forward * normCdf(d1) - strike * normCdf(d2) : strike * normCdf(-d2) - forward * normCdf(-d1); return Math.max(price, intr); } function impliedVol(p) { const { forward, strike, tauYears, price, right } = p; if (!Number.isFinite(price) || price <= 0) return null; if (!(tauYears > 0) || !(forward > 0) || !(strike > 0)) return null; const intr = intrinsic(forward, strike, right); if (price <= intr) return null; const asymptote = right === "C" ? forward : strike; if (price >= asymptote) return null; const value = (sigma) => black76({ forward, strike, tauYears, sigma, right }); let lo = 0.00000001; let hi = 5; const MAX_HI = 50; let vhi = value(hi); for (let guard = 0;vhi < price && hi < MAX_HI && guard < 64; guard++) { hi = Math.min(hi * 2, MAX_HI); vhi = value(hi); } if (vhi < price) return null; for (let i = 0;i < 128; i++) { const midSigma = 0.5 * (lo + hi); const v = value(midSigma); if (Math.abs(v - price) < 0.000000000001) return midSigma; if (v < price) lo = midSigma; else hi = midSigma; if (hi - lo < 0.000000000001) break; } return 0.5 * (lo + hi); } export { dexp, MS_PER_YEAR, greeks, intrinsic, black76, impliedVol };