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
JavaScript
// 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 };