src/markets/status.test.ts
v0.2.0 · 3.7 KB
import { describe, expect, it } from 'vitest';
import { effectiveLongWeight, initialMarginFraction, maxLeverageFromWeights } from './leverage.js';
import { acceptedOrderTypes, canActivateMarket, isPostOnlyMarket, marketMode, resolveMarketMode } from './status.js';
describe('maxLeverageFromWeights', () => {
it('follows 1 / (1 - weight): 0.98 -> 50x, 0.9 -> 10x, 0.8 -> 5x', () => {
expect(maxLeverageFromWeights(0.98)).toBe(50);
expect(maxLeverageFromWeights(0.9)).toBe(10);
expect(maxLeverageFromWeights(0.8)).toBe(5);
expect(maxLeverageFromWeights(0.5)).toBe(2);
expect(maxLeverageFromWeights(0.001)).toBe(1);
});
it('rejects weights outside (0, 1)', () => {
expect(() => maxLeverageFromWeights(0)).toThrow(RangeError);
expect(() => maxLeverageFromWeights(1)).toThrow(RangeError);
expect(() => maxLeverageFromWeights(Number.NaN)).toThrow(RangeError);
});
it('initial margin fraction is 1 - weight; effective weight prefers a valid subaccount value', () => {
expect(initialMarginFraction(0.98)).toBeCloseTo(0.02, 12);
expect(() => initialMarginFraction(1.2)).toThrow(RangeError);
expect(effectiveLongWeight(0.95, 0.98)).toBe(0.95);
expect(effectiveLongWeight(0, 0.98)).toBe(0.98);
expect(effectiveLongWeight(null, 0.98)).toBe(0.98);
expect(effectiveLongWeight(1, 0.98)).toBe(0.98);
});
});
describe('marketMode', () => {
it('live accepts everything', () => {
const m = marketMode('live');
expect(m.accepts).toEqual(['default', 'ioc', 'fok', 'post_only']);
expect(m.postOnly).toBe(false);
expect(m.verified).toBe(true);
});
it('post_only accepts ONLY post_only and refuses the rest with 2117', () => {
const m = marketMode('post_only');
expect(m.accepts).toEqual(['post_only']);
expect(m.postOnly).toBe(true);
expect(m.refusalCode).toBe(2117);
expect(m.verified).toBe(true);
expect(acceptedOrderTypes('post_only')).not.toContain('ioc');
expect(acceptedOrderTypes('post_only')).not.toContain('default');
});
it('not_tradable accepts nothing (2069)', () => {
const m = marketMode('not_tradable');
expect(m.accepts).toEqual([]);
expect(m.refusalCode).toBe(2069);
expect(m.verified).toBe(true);
});
it('reduce_only statuses are taker reduce-only by name and marked unverified', () => {
for (const s of ['reduce_only', 'soft_reduce_only']) {
const m = marketMode(s);
expect(m.accepts).toEqual(['ioc', 'fok']);
expect(m.reduceOnlyOnly).toBe(true);
expect(m.verified).toBe(false);
}
});
it('unknown statuses fail closed', () => {
const m = marketMode('something_new');
expect(m.accepts).toEqual([]);
expect(m.verified).toBe(false);
expect(m.status).toBe('something_new');
});
});
describe('resolveMarketMode / isPostOnlyMarket', () => {
it('publishes the mode only from a fresh cache', () => {
expect(resolveMarketMode('post_only', true)?.postOnly).toBe(true);
expect(resolveMarketMode('post_only', false)).toBeUndefined();
expect(resolveMarketMode(undefined, true)).toBeUndefined();
expect(isPostOnlyMarket('post_only', true)).toBe(true);
expect(isPostOnlyMarket('live', true)).toBe(false);
expect(isPostOnlyMarket('live', false)).toBeUndefined();
expect(isPostOnlyMarket(undefined, true)).toBeUndefined();
});
});
describe('canActivateMarket', () => {
it('blocks new activation on not_tradable / unknown, allows live and post_only', () => {
expect(canActivateMarket('live')).toBe(true);
expect(canActivateMarket('post_only')).toBe(true);
expect(canActivateMarket('not_tradable')).toBe(false);
expect(canActivateMarket('weird')).toBe(false);
expect(canActivateMarket(undefined)).toBe(false);
});
});