const { oddsToImplied, impliedToAmerican, devig, clv } = require('../../src/utils/odds'); describe('odds utility', () => { describe('oddsToImplied', () => { test('+150 → ~0.40', () => { expect(oddsToImplied(150)).toBeCloseTo(0.4, 3); }); test('-110 → ~0.524', () => { expect(oddsToImplied(-110)).toBeCloseTo(0.5238, 3); }); test('invalid input returns null', () => { expect(oddsToImplied(null)).toBeNull(); expect(oddsToImplied('abc')).toBeNull(); expect(oddsToImplied(0)).toBeNull(); }); }); describe('impliedToAmerican', () => { test('0.5 → -100', () => { expect(impliedToAmerican(0.5)).toBe(-100); }); test('0.4 → +150', () => { expect(impliedToAmerican(0.4)).toBe(150); }); test('round-trips with oddsToImplied within 1 cent', () => { // +100 and -100 both encode p=0.5 (even money) so we skip the boundary. for (const odds of [-200, -150, -110, +150, +250]) { const back = impliedToAmerican(oddsToImplied(odds)); expect(Math.abs(back - odds)).toBeLessThanOrEqual(1); } }); test('out-of-range prob returns null', () => { expect(impliedToAmerican(0)).toBeNull(); expect(impliedToAmerican(1)).toBeNull(); expect(impliedToAmerican(NaN)).toBeNull(); }); }); describe('devig', () => { test('symmetric -110/-110 normalizes to 0.5/0.5', () => { const { fairOver, fairUnder } = devig(-110, -110); expect(fairOver).toBeCloseTo(0.5, 5); expect(fairUnder).toBeCloseTo(0.5, 5); }); test('asymmetric -130/+110 — over more likely, both sum to 1.0', () => { const { fairOver, fairUnder } = devig(-130, +110); expect(fairOver + fairUnder).toBeCloseTo(1.0, 5); expect(fairOver).toBeGreaterThan(0.5); }); test('invalid odds returns null', () => { expect(devig(0, -110)).toBeNull(); expect(devig(-110, null)).toBeNull(); }); }); describe('clv', () => { test('positive when closing > graded', () => { expect(clv(0.50, 0.55)).toBeCloseTo(0.05, 5); }); test('negative when closing < graded', () => { expect(clv(0.55, 0.50)).toBeCloseTo(-0.05, 5); }); test('invalid input returns null', () => { expect(clv('x', 0.5)).toBeNull(); expect(clv(0.5, undefined)).toBeNull(); }); }); });