Files
ritmex-bot/tests/maker-points-defense-rest.test.ts
T
discountry 7acb3c3b82 fix(exchanges): restore type-check safety net and repair dead balance guards
tsconfig compiled docs/ (vendored ccxt samples) and @types/react was missing,
so 269 tsc errors buried the real ones. Scoping the project and adding the
missing type packages left 42 genuine errors in src/, which exposed two bugs:

- lighter: assertSpotBalance's buy branch and createOrder's spot-sell guard
  compared the {available, wallet} object against a number, so both guards
  were dead. Introduce SpotAssetBalance with a precomputed 'effective' field
  (Extract Class) and route all three call sites through it.
- offset-maker: the below-min-sell branch logged 'skip sell' but pushed the
  SELL order anyway, and pushed a possibly-null price. Both branches now
  match their working sibling.

Also: widen LighterOrder's is_ask/reduce_only to BooleanFlag (the wire format
flags.ts already parses), extract sellableBase (Extract Function, 3 copies),
delete two scripts importing a module that does not exist, add a typecheck
script. tsc --noEmit: 269 -> 0 errors; 218 tests still pass.
2026-07-29 20:27:40 +08:00

174 lines
4.6 KiB
TypeScript

import { afterEach, describe, expect, it, vi } from "vitest";
import type { ExchangeAdapter } from "../src/exchanges/adapter";
import type { AccountSnapshot, Depth, Kline, Order, Ticker } from "../src/exchanges/types";
import { MakerPointsEngine } from "../src/strategy/maker-points-engine";
class StubAdapter implements ExchangeAdapter {
id = "standx";
cancelAllCount = 0;
openOrders: Order[] | Error = [];
accountSnapshot: AccountSnapshot | null = null;
supportsTrailingStops(): boolean {
return false;
}
watchAccount(_cb: (snapshot: AccountSnapshot) => void): void {}
watchOrders(_cb: (orders: Order[]) => void): void {}
watchDepth(_symbol: string, _cb: (depth: Depth) => void): void {}
watchTicker(_symbol: string, _cb: (ticker: Ticker) => void): void {}
watchKlines(_symbol: string, _interval: string, _cb: (klines: Kline[]) => void): void {}
async createOrder(): Promise<Order> {
throw new Error("not implemented");
}
async cancelOrder(): Promise<void> {}
async cancelOrders(): Promise<void> {}
async cancelAllOrders(): Promise<void> {
this.cancelAllCount += 1;
this.openOrders = [];
}
async queryOpenOrders(): Promise<Order[]> {
if (this.openOrders instanceof Error) throw this.openOrders;
return this.openOrders;
}
async queryAccountSnapshot(): Promise<AccountSnapshot | null> {
return this.accountSnapshot;
}
}
afterEach(() => {
vi.useRealTimers();
});
describe("MakerPointsEngine defense-mode REST polling", () => {
it("keeps trying to fetch open orders and cancel all when open orders exist", async () => {
vi.useFakeTimers();
const adapter = new StubAdapter();
adapter.openOrders = [
{
orderId: "1",
clientOrderId: "c1",
symbol: "BTC-USD",
side: "BUY",
type: "LIMIT",
status: "NEW",
price: "100",
origQty: "1",
executedQty: "0",
stopPrice: "0",
time: Date.now(),
updateTime: Date.now(),
reduceOnly: false,
closePosition: false,
},
];
const engine = new MakerPointsEngine(
{
symbol: "BTC-USD",
perOrderAmount: 0.01,
closeThreshold: 0,
stopLossUsd: 1,
refreshIntervalMs: 500,
maxLogEntries: 10,
maxCloseSlippagePct: 0.05,
priceTick: 0.1,
qtyStep: 0.001,
enableBand0To10: true,
enableBand10To30: false,
enableBand30To100: false,
band0To10Amount: 0.01,
band10To30Amount: 0.01,
band30To100Amount: 0.01,
minRepriceBps: 3,
enableBinanceDepthCancel: false,
filterMinDepth: 0,
},
adapter
);
(engine as any).enterDefenseMode({
standxDepthStale: true,
binanceStale: false,
binanceUnhealthy: false,
binanceHealthReason: null,
standxAccountStale: false,
accountInvalid: false,
standxRestUnhealthy: false,
standxRestConsecutiveErrors: 0,
standxRestLastError: null,
marginModeNotIsolated: false,
marginMode: "isolated",
standxDepthAge: 6000,
binanceAge: 0,
standxAccountAge: 0,
accountIssues: [],
});
await vi.waitFor(() => {
expect(adapter.cancelAllCount).toBeGreaterThanOrEqual(1);
});
engine.stop();
});
it("attempts cancel-all even if open-order query fails", async () => {
vi.useFakeTimers();
const adapter = new StubAdapter();
adapter.openOrders = new Error("boom");
const engine = new MakerPointsEngine(
{
symbol: "BTC-USD",
perOrderAmount: 0.01,
closeThreshold: 0,
stopLossUsd: 1,
refreshIntervalMs: 500,
maxLogEntries: 10,
maxCloseSlippagePct: 0.05,
priceTick: 0.1,
qtyStep: 0.001,
enableBand0To10: true,
enableBand10To30: false,
enableBand30To100: false,
band0To10Amount: 0.01,
band10To30Amount: 0.01,
band30To100Amount: 0.01,
minRepriceBps: 3,
enableBinanceDepthCancel: false,
filterMinDepth: 0,
},
adapter
);
(engine as any).enterDefenseMode({
standxDepthStale: true,
binanceStale: false,
binanceUnhealthy: false,
binanceHealthReason: null,
standxAccountStale: false,
accountInvalid: false,
standxRestUnhealthy: false,
standxRestConsecutiveErrors: 0,
standxRestLastError: null,
marginModeNotIsolated: false,
marginMode: "isolated",
standxDepthAge: 6000,
binanceAge: 0,
standxAccountAge: 0,
accountIssues: [],
});
await vi.waitFor(() => {
expect(adapter.cancelAllCount).toBeGreaterThanOrEqual(1);
});
engine.stop();
});
});