import { test } from 'node:test'; import { strict as assert } from 'node:assert'; import { buildDealerMap, contractGex, contractVex, isDegradedDealerMap, isHealthyDealerMap, isSaneIv, layer0Educator, resolveGreeks, type DealerMap, } from '../dealerExposureEngine.ts'; import type { NormalizedOptionSurface, NormalizedOptionRow } from '../../options/types.ts'; function row(partial: Partial & Pick): NormalizedOptionRow { return { openInterest: 1000, volume: 100, bid: 1, ask: 1.1, mid: 1.05, iv: 0.25, greeks: { gamma: 0.02, vanna: 0.1, delta: 0.5 }, ...partial, }; } function surface(rows: NormalizedOptionRow[], spot = 100): NormalizedOptionSurface { return { symbol: 'TEST', spot, asOf: '2026-08-06T15:00:00.000Z', providerId: 'synthetic', delaySeconds: 900, oiFreshness: 'prior_session', rows, provenance: { fetchedAt: '2026-08-06T15:00:00.000Z', sourceKind: 'yfinance', rawSourceId: 'synthetic:TEST', }, }; } test('contractGex: call positive, put negative', () => { const call = contractGex(0.01, 100, 100, 'call'); const put = contractGex(0.01, 100, 100, 'put'); assert.ok(call > 0); assert.ok(put < 0); assert.equal(call, -put); }); test('contractVex uses the same S² notional as GEX', () => { const gex = contractGex(0.02, 1000, 44, 'call'); const vex = contractVex(0.02, 1000, 44, 'call'); assert.equal(vex, gex); assert.equal(contractVex(0.02, 1000, 44, 'put'), -vex); }); test('buildDealerMap: expiries sorted nearest-left and capped', () => { const rows: NormalizedOptionRow[] = []; for (const exp of ['2026-08-08', '2026-08-15', '2026-08-22', '2026-09-19', '2026-10-17', '2026-11-21', '2026-12-19', '2027-01-16']) { rows.push(row({ strike: 100, expiry: exp, right: 'call', openInterest: 500 })); rows.push(row({ strike: 100, expiry: exp, right: 'put', openInterest: 400, greeks: { gamma: 0.015, vanna: -0.05 } })); } const map = buildDealerMap(surface(rows), { maxExpiries: 4 }); assert.equal(map.expiries.length, 4); assert.equal(map.expiries[0], '2026-08-08'); assert.ok(map.expiries.every((e, i) => i === 0 || e >= map.expiries[i - 1])); assert.equal(map.providerId, 'synthetic'); assert.equal(map.convention, 'classic_call_pos_put_neg'); assert.match(map.delayNote, /prior session/i); }); test('buildDealerMap: king is max absolute GEX cell', () => { const map = buildDealerMap( surface([ row({ strike: 95, expiry: '2026-08-15', right: 'put', openInterest: 2000, greeks: { gamma: 0.03, vanna: 0.05 } }), row({ strike: 100, expiry: '2026-08-15', right: 'call', openInterest: 500, greeks: { gamma: 0.02, vanna: 0.05 } }), row({ strike: 105, expiry: '2026-08-15', right: 'call', openInterest: 5000, greeks: { gamma: 0.04, vanna: 0.05 } }), ]), ); assert.ok(map.levels.king); assert.equal(map.levels.king!.strike, 105); assert.ok(map.cells.some((c) => c.isKing && c.strike === 105)); }); test('buildDealerMap: skips rows without OI', () => { const map = buildDealerMap( surface([ row({ strike: 100, expiry: '2026-08-15', right: 'call', openInterest: null }), row({ strike: 100, expiry: '2026-08-15', right: 'put', openInterest: 0 }), row({ strike: 101, expiry: '2026-08-15', right: 'call', openInterest: 1000 }), ]), ); assert.ok(map.byStrike.some((s) => s.strike === 101)); assert.ok(!map.byStrike.some((s) => s.strike === 100)); assert.ok(map.coveragePct < 100); }); test('buildDealerMap: BSM fill when gamma missing but IV present', () => { // Asymmetric OI so call/put GEX does not cancel to zero at the strike. const map = buildDealerMap( surface([ row({ strike: 100, expiry: '2026-09-15', right: 'call', openInterest: 5000, iv: 0.3, greeks: null, }), row({ strike: 100, expiry: '2026-09-15', right: 'put', openInterest: 1000, iv: 0.3, greeks: null, }), ]), ); assert.ok(map.cells.length >= 1); assert.ok(map.bsmFillPct > 0); assert.ok(map.byStrike.some((s) => s.absGex > 0), `byStrike=${JSON.stringify(map.byStrike)}`); }); test('withExposureConvention: dealer_inventory negates classic GEX and keeps king strike', async () => { const { withExposureConvention } = await import('../dealerExposureEngine.ts'); const classic = buildDealerMap( surface([ row({ strike: 100, expiry: '2026-08-15', right: 'call', openInterest: 5000, greeks: { gamma: 0.04, vanna: 0.05 } }), row({ strike: 95, expiry: '2026-08-15', right: 'put', openInterest: 2000, greeks: { gamma: 0.03, vanna: 0.05 } }), ], 100), ); assert.equal(classic.convention, 'classic_call_pos_put_neg'); const dealer = withExposureConvention(classic, 'dealer_inventory'); assert.equal(dealer.convention, 'dealer_inventory'); assert.ok(classic.levels.king && dealer.levels.king); assert.equal(classic.levels.king!.strike, dealer.levels.king!.strike); assert.ok( Math.abs(classic.levels.king!.gex + dealer.levels.king!.gex) < 1e-6, 'king gex should flip sign', ); for (const c of classic.cells) { const d = dealer.cells.find((x) => x.strike === c.strike && x.expiry === c.expiry); assert.ok(d); assert.ok(Math.abs(c.gex + d!.gex) < 1e-6, `cell ${c.strike} gex should flip`); } // Round-trip const back = withExposureConvention(dealer, 'classic_call_pos_put_neg'); assert.equal(back.convention, 'classic_call_pos_put_neg'); assert.ok(Math.abs(back.levels.king!.gex - classic.levels.king!.gex) < 1e-6); }); test('buildDealerMap: flip between +GEX and −GEX strikes', () => { // Strong + below and − above (or vice versa) to create a sign change const map = buildDealerMap( surface([ row({ strike: 90, expiry: '2026-08-15', right: 'call', openInterest: 8000, greeks: { gamma: 0.03, vanna: 0.1 } }), row({ strike: 95, expiry: '2026-08-15', right: 'call', openInterest: 6000, greeks: { gamma: 0.03, vanna: 0.1 } }), row({ strike: 105, expiry: '2026-08-15', right: 'put', openInterest: 8000, greeks: { gamma: 0.03, vanna: 0.1 } }), row({ strike: 110, expiry: '2026-08-15', right: 'put', openInterest: 6000, greeks: { gamma: 0.03, vanna: 0.1 } }), ], 100), ); // puts contribute negative GEX, calls positive → flip exists between clusters assert.ok(map.levels.flip != null, 'expected a polarity flip'); }); test('buildDealerMap: detects rug_setup pattern', () => { // +GEX above spot, large −GEX pivot below, more −GEX stack const map = buildDealerMap( surface([ row({ strike: 110, expiry: '2026-08-15', right: 'call', openInterest: 10000, greeks: { gamma: 0.05, vanna: 0.1 } }), row({ strike: 95, expiry: '2026-08-15', right: 'put', openInterest: 12000, greeks: { gamma: 0.05, vanna: 0.1 } }), row({ strike: 90, expiry: '2026-08-15', right: 'put', openInterest: 9000, greeks: { gamma: 0.04, vanna: 0.1 } }), row({ strike: 85, expiry: '2026-08-15', right: 'put', openInterest: 8000, greeks: { gamma: 0.04, vanna: 0.1 } }), row({ strike: 100, expiry: '2026-08-15', right: 'call', openInterest: 500, greeks: { gamma: 0.01, vanna: 0.05 } }), ], 100), ); assert.ok( map.patterns.some((p) => p.id === 'rug_setup'), `patterns=${JSON.stringify(map.patterns)}`, ); }); test('buildDealerMap: detects whipsaw with air pocket between edges', () => { // Strong nodes at 90 and 110, near-zero middle const rows: NormalizedOptionRow[] = [ row({ strike: 90, expiry: '2026-08-15', right: 'call', openInterest: 15000, greeks: { gamma: 0.05, vanna: 0.1 } }), row({ strike: 110, expiry: '2026-08-15', right: 'call', openInterest: 15000, greeks: { gamma: 0.05, vanna: 0.1 } }), ]; for (const k of [95, 98, 100, 102, 105]) { rows.push( row({ strike: k, expiry: '2026-08-15', right: 'call', openInterest: 10, greeks: { gamma: 0.0001, vanna: 0.001 }, }), ); } const map = buildDealerMap(surface(rows, 100)); assert.ok(map.levels.airPockets.length >= 1, 'expected air pocket'); assert.ok( map.patterns.some((p) => p.id === 'whipsaw'), `patterns=${JSON.stringify(map.patterns)} scenario=${map.scenario}`, ); }); test('buildDealerMap: velocity growing near air pocket', () => { const curr = buildDealerMap( surface([ row({ strike: 90, expiry: '2026-08-15', right: 'call', openInterest: 5000, greeks: { gamma: 0.04, vanna: 0.1 } }), row({ strike: 100, expiry: '2026-08-15', right: 'call', openInterest: 50, greeks: { gamma: 0.001, vanna: 0.01 } }), row({ strike: 110, expiry: '2026-08-15', right: 'call', openInterest: 5000, greeks: { gamma: 0.04, vanna: 0.1 } }), ]), ); const prior: Pick = { asOf: '2026-08-05T15:00:00.000Z', byStrike: curr.byStrike.map((s) => s.strike === 90 ? { ...s, absGex: s.absGex * 0.3, gex: s.gex * 0.3 } : { ...s, absGex: s.absGex * 0.9, gex: s.gex * 0.9 }, ), levels: curr.levels, }; const withVel = buildDealerMap( surface([ row({ strike: 90, expiry: '2026-08-15', right: 'call', openInterest: 5000, greeks: { gamma: 0.04, vanna: 0.1 } }), row({ strike: 100, expiry: '2026-08-15', right: 'call', openInterest: 50, greeks: { gamma: 0.001, vanna: 0.01 } }), row({ strike: 110, expiry: '2026-08-15', right: 'call', openInterest: 5000, greeks: { gamma: 0.04, vanna: 0.1 } }), ]), { prior }, ); assert.ok(withVel.velocity); assert.ok(withVel.velocity!.nodes.some((n) => n.growing)); }); test('buildDealerMap: OPEX Friday banner within 5 days', () => { // 2026-08-07 is a Friday const map = buildDealerMap( surface([ row({ strike: 100, expiry: '2026-08-07', right: 'call', openInterest: 1000 }), ]), ); assert.equal(map.opexHints.isNearOpex, true); assert.match(map.opexHints.note, /OPEX|Near-term/i); }); test('layer0Educator: no buy/sell imperatives', () => { const map = buildDealerMap( surface([ row({ strike: 100, expiry: '2026-08-15', right: 'call', openInterest: 3000 }), row({ strike: 95, expiry: '2026-08-15', right: 'put', openInterest: 3000 }), ]), ); const copy = layer0Educator(map); const blob = JSON.stringify(copy).toLowerCase(); assert.equal(copy.footer, 'Educational observation only.'); assert.ok(!/\bbuy\b/.test(blob) || blob.includes('buying pressure') === false); // Primary rule: no "you should buy/sell" assert.ok(!/you should (buy|sell)/i.test(blob)); assert.ok(!/\b(buy|sell) (calls|puts|shares|stock)\b/i.test(blob)); assert.ok(copy.questions.length >= 3); assert.ok(copy.caveats.some((c) => /prior session|delay/i.test(c))); }); test('buildDealerMap: engine has no vendor coupling (synthetic only path)', () => { // Sanity: providerId synthetic preserved; computation local_engine const map = buildDealerMap( surface([row({ strike: 50, expiry: '2026-08-20', right: 'call', openInterest: 100 })]), ); assert.equal(map.computation, 'local_engine'); assert.equal(map.unit, 'usd_delta_adj_gamma_notional'); assert.ok(map.quality); assert.ok(['ok', 'degraded', 'poor'].includes(map.quality.greekQuality)); }); test('isSaneIv: rejects premarket junk and absurd IV', () => { assert.equal(isSaneIv(1e-5), false); assert.equal(isSaneIv(0.02), false); assert.equal(isSaneIv(0.25), true); assert.equal(isSaneIv(4.0), false); assert.equal(isSaneIv(null), false); }); test('resolveGreeks: skips junk IV without vendor gamma', () => { const r = resolveGreeks( row({ strike: 100, expiry: '2026-09-15', right: 'call', openInterest: 1000, iv: 1e-5, greeks: null, }), 100, '2026-08-07T13:14:00.000Z', 0.04, 0, ); assert.equal(r.skippedBadIv, true); assert.equal(r.gamma, 0); }); test('resolveGreeks: uses fallback IV hint when current IV is junk', () => { const r = resolveGreeks( row({ strike: 100, expiry: '2026-09-15', right: 'call', openInterest: 5000, iv: 1e-5, greeks: null, }), 100, '2026-08-07T13:14:00.000Z', 0.04, 0, { fallbackIv: 0.35 }, ); assert.equal(r.skippedBadIv, false); assert.ok(r.gamma > 0); assert.equal(r.usedFallbackIv, true); assert.equal(r.usedBsm, true); }); test('buildDealerMap: junk IV rows skipped; quality poor when most OI unusable', () => { // No sane IV on the surface → no ATM proxy → skip const map = buildDealerMap( surface([ row({ strike: 100, expiry: '2026-09-15', right: 'call', openInterest: 9000, iv: 1e-5, greeks: null, }), row({ strike: 100, expiry: '2026-09-15', right: 'put', openInterest: 8000, iv: 0.01, greeks: null, }), row({ strike: 105, expiry: '2026-09-15', right: 'call', openInterest: 100, iv: 0.02, greeks: null, }), ]), ); assert.ok(map.quality.skippedBadIv >= 2, `skipped=${map.quality.skippedBadIv}`); assert.equal(map.quality.greekQuality, 'poor'); assert.ok(isDegradedDealerMap(map)); assert.equal(isHealthyDealerMap(map), false); }); test('buildDealerMap: ATM IV proxy fills junk rows when some sane IV exists', () => { const map = buildDealerMap( surface([ row({ strike: 100, expiry: '2026-09-15', right: 'call', openInterest: 5000, iv: 1e-5, greeks: null, }), row({ strike: 100, expiry: '2026-09-15', right: 'put', openInterest: 1000, iv: 1e-5, greeks: null, }), row({ strike: 101, expiry: '2026-09-15', right: 'call', openInterest: 200, iv: 0.28, greeks: null, }), ]), ); assert.ok(map.byStrike.some((s) => s.absGex > 0)); assert.ok(map.quality.fallbackIvUses >= 1 || map.quality.included >= 1); }); test('buildDealerMap: ivHints restore structure when live IV is junk', () => { const map = buildDealerMap( surface([ row({ strike: 100, expiry: '2026-09-15', right: 'call', openInterest: 5000, iv: 1e-5, greeks: null, }), row({ strike: 100, expiry: '2026-09-15', right: 'put', openInterest: 1000, iv: 1e-5, greeks: null, }), ]), { ivHints: { '2026-09-15|100|call': 0.3, '2026-09-15|100|put': 0.3, }, }, ); assert.ok(map.byStrike.some((s) => s.absGex > 0)); assert.ok(map.quality.fallbackIvUses >= 1); assert.ok(map.quality.greekQuality !== 'poor' || map.cells.length > 0); });