Skip to content
markpaper

src/numbers/minimums.test.ts

v0.2.1 · 3.8 KB

Download file
import { describe, expect, it } from 'vitest';
import { LighterNumbersError } from './errors.js';
import { meetsMinimums, meetsMinimumsExact } from './minimums.js';

describe('meetsMinimums', () => {
  it('passes the dollar gate and fails the lot gate (the 21706 case)', () => {
    // A synthetic order of 0.0071 at mark ~1605 is $11.40: above $10, below the 0.01 lot minimum.
    const r = meetsMinimums({ notional: 0.0071 * 1605, baseAmount: 0.0071, minBaseAmount: 0.01, minQuoteUsd: 10 });
    expect(r.ok).toBe(false);
    expect(r.failing).toEqual(['min_base_amount']);
  });

  it('passes the lot gate and fails the dollar gate', () => {
    // 0.04 lots at ~$90 is $3.6: above the lot minimum, below $10.
    const r = meetsMinimums({ notional: 3.6, baseAmount: 0.04, minBaseAmount: 0.04, minQuoteUsd: 10 });
    expect(r.ok).toBe(false);
    expect(r.failing).toEqual(['min_quote_amount']);
  });

  it('reports both when both fail, none when both pass', () => {
    expect(meetsMinimums({ notional: 5, baseAmount: 0.001, minBaseAmount: 0.01, minQuoteUsd: 10 }).failing).toEqual([
      'min_quote_amount',
      'min_base_amount',
    ]);
    expect(meetsMinimums({ notional: 16, baseAmount: 0.01, minBaseAmount: 0.01, minQuoteUsd: 10 }).ok).toBe(true);
  });

  it('tolerates the float product of an exact $10', () => {
    const notional = 3 * 3.3333333333333335; // 10.000000000000002 or 9.999999999999998 depending on operands
    expect(meetsMinimums({ notional: 0.7 * 14.285714285714286, baseAmount: 1, minQuoteUsd: 10 }).ok).toBe(true);
    expect(meetsMinimums({ notional, baseAmount: 1, minQuoteUsd: 10 }).ok).toBe(true);
    expect(meetsMinimums({ notional: 9.99, baseAmount: 1, minQuoteUsd: 10 }).ok).toBe(false);
  });

  it('treats a missing or zero minimum as "no minimum"', () => {
    expect(meetsMinimums({ notional: 1, baseAmount: 0.0001 }).ok).toBe(true);
    expect(meetsMinimums({ notional: 1, baseAmount: 0.0001, minBaseAmount: 0, minQuoteUsd: 0 }).ok).toBe(true);
  });

  it('rejects invalid input', () => {
    expect(() => meetsMinimums({ notional: Number.NaN, baseAmount: 1 })).toThrow(LighterNumbersError);
    expect(() => meetsMinimums({ notional: 1, baseAmount: -1 })).toThrow(LighterNumbersError);
    expect(() => meetsMinimums({ notional: 1, baseAmount: 1, minQuoteUsd: -10 })).toThrow(LighterNumbersError);
  });
});

describe('meetsMinimumsExact', () => {
  const grid = { sizeDecimals: 4, priceDecimals: 2 };

  it('does integer arithmetic on the wire values', () => {
    // 0.0071 * 1605.00 = 11.3955 >= 10; 0.0071 < 0.0100
    const r = meetsMinimumsExact({
      ...grid,
      baseAmount: 71,
      price: 160500,
      minBaseAmount: '0.0100',
      minQuoteUsd: '10.000000',
    });
    expect(r.failing).toEqual(['min_base_amount']);
    expect(r.notional).toBeCloseTo(11.3955, 6);
    expect(r.baseAmount).toBe(0.0071);
  });

  it('accepts an order worth exactly the minimum', () => {
    // 0.0100 * 1000.00 = 10.000000
    const r = meetsMinimumsExact({
      ...grid,
      baseAmount: 100,
      price: 100000,
      minBaseAmount: '0.0100',
      minQuoteUsd: '10.000000',
    });
    expect(r.ok).toBe(true);
    // one tick below
    const below = meetsMinimumsExact({ ...grid, baseAmount: 100, price: 99999, minQuoteUsd: '10.000000' });
    expect(below.failing).toEqual(['min_quote_amount']);
  });

  it('handles minimums with more decimals than the grid', () => {
    const r = meetsMinimumsExact({ ...grid, baseAmount: 100, price: 100000, minBaseAmount: '0.010000001' });
    expect(r.failing).toEqual(['min_base_amount']);
  });

  it('rejects malformed input', () => {
    expect(() => meetsMinimumsExact({ ...grid, baseAmount: 1.5, price: 1 })).toThrow(LighterNumbersError);
    expect(() => meetsMinimumsExact({ ...grid, baseAmount: 1, price: 1, minQuoteUsd: 'ten' })).toThrow(
      LighterNumbersError,
    );
  });
});
All files