mirror of
https://github.com/discountry/ritmex-bot.git
synced 2026-09-09 08:18:07 +00:00
refactor(core): give order functions a parameter object
placeOrder took 13 positional arguments; the other five order functions took 9-13. The leading six — adapter, symbol, openOrders, locks, timers, pendings — were the same values at all 36 call sites, and every engine spelled them out again for each order it placed. Introduce Parameter Object: OrderContext holds what is fixed for an engine's lifetime (exposed once via a lazily-built this.orderContext), and each function takes a named request. A wrong argument order is now a compile error rather than a silently misrouted order. The type change surfaced dead weight: placeOrder's opts.priceTick was never read by its body, yet five engines passed it. Removed. Also finishes the PrecisionSyncer migration — grid-engine was the ninth copy and was missed last round, so it still carried the uncleared retry timer. Extract Function: normalizeQuantity replaces the round-down-but-never-to-zero block that appeared in all five order functions. 250 pass; tsc and oxlint clean.
This commit is contained in:
@@ -1,7 +1,12 @@
|
||||
import { describe, expect, it, vi, beforeEach, afterAll } from "vitest";
|
||||
import type { ExchangeAdapter } from "../src/exchanges/adapter";
|
||||
import type { Order } from "../src/exchanges/types";
|
||||
import type { OrderLockMap, OrderTimerMap, OrderPendingMap } from "../src/core/order-coordinator";
|
||||
import type {
|
||||
OrderContext,
|
||||
OrderLockMap,
|
||||
OrderPendingMap,
|
||||
OrderTimerMap,
|
||||
} from "../src/core/order-coordinator";
|
||||
import {
|
||||
deduplicateOrders,
|
||||
placeOrder,
|
||||
@@ -60,130 +65,76 @@ describe("order-coordinator", () => {
|
||||
process.env.EXCHANGE = originalExchange;
|
||||
});
|
||||
|
||||
it("deduplicates orders by type and side", async () => {
|
||||
/** One order context plus handles on the pieces the assertions poke at. */
|
||||
function createContext() {
|
||||
const adapter = createMockExchange();
|
||||
const locks: OrderLockMap = {};
|
||||
const timers: OrderTimerMap = {};
|
||||
const pending: OrderPendingMap = {};
|
||||
const log = vi.fn();
|
||||
const ctx: OrderContext = { adapter, symbol: "BTCUSDT", locks, timers, pendings: pending, log };
|
||||
return { ctx, adapter, locks, timers, pending, log };
|
||||
}
|
||||
|
||||
it("deduplicates orders by type and side", async () => {
|
||||
const { ctx, adapter, log } = createContext();
|
||||
const openOrders: Order[] = [
|
||||
{ ...baseOrder, orderId: 1 },
|
||||
{ ...baseOrder, orderId: 2 },
|
||||
];
|
||||
await deduplicateOrders(adapter, "BTCUSDT", openOrders, locks, timers, pending, "LIMIT", "BUY", log);
|
||||
await deduplicateOrders(ctx, openOrders, "LIMIT", "BUY");
|
||||
expect(adapter.cancelOrders).toHaveBeenCalledWith({ symbol: "BTCUSDT", orderIdList: [2] });
|
||||
expect(log).toHaveBeenCalledWith("order", expect.stringContaining("去重撤销重复"));
|
||||
});
|
||||
|
||||
it("places limit orders and records pending id", async () => {
|
||||
const adapter = createMockExchange();
|
||||
const locks: OrderLockMap = {};
|
||||
const timers: OrderTimerMap = {};
|
||||
const pending: OrderPendingMap = {};
|
||||
const log = vi.fn();
|
||||
await placeOrder(
|
||||
adapter,
|
||||
"BTCUSDT",
|
||||
[],
|
||||
locks,
|
||||
timers,
|
||||
pending,
|
||||
"BUY",
|
||||
"100",
|
||||
1,
|
||||
log,
|
||||
false
|
||||
);
|
||||
const { ctx, adapter, pending } = createContext();
|
||||
await placeOrder(ctx, { openOrders: [], side: "BUY", price: "100", amount: 1, reduceOnly: false });
|
||||
expect(adapter.createOrder).toHaveBeenCalled();
|
||||
expect(pending.MARKET).toBeUndefined();
|
||||
expect(pending.LIMIT).toBe(String(baseOrder.orderId));
|
||||
});
|
||||
|
||||
it("places market order and unlocks after completion", async () => {
|
||||
const adapter = createMockExchange();
|
||||
const locks: OrderLockMap = {};
|
||||
const timers: OrderTimerMap = {};
|
||||
const pending: OrderPendingMap = {};
|
||||
const log = vi.fn();
|
||||
await placeMarketOrder(
|
||||
adapter,
|
||||
"BTCUSDT",
|
||||
[],
|
||||
locks,
|
||||
timers,
|
||||
pending,
|
||||
"SELL",
|
||||
1,
|
||||
log,
|
||||
true
|
||||
);
|
||||
const { ctx, adapter, pending } = createContext();
|
||||
await placeMarketOrder(ctx, { openOrders: [], side: "SELL", amount: 1, reduceOnly: true });
|
||||
expect(adapter.createOrder).toHaveBeenCalled();
|
||||
expect(pending.MARKET).toBe(String(baseOrder.orderId));
|
||||
});
|
||||
|
||||
it("places stop loss order only when valid", async () => {
|
||||
const adapter = createMockExchange();
|
||||
const locks: OrderLockMap = {};
|
||||
const timers: OrderTimerMap = {};
|
||||
const pending: OrderPendingMap = {};
|
||||
const log = vi.fn();
|
||||
await placeStopLossOrder(
|
||||
adapter,
|
||||
"BTCUSDT",
|
||||
[],
|
||||
locks,
|
||||
timers,
|
||||
pending,
|
||||
"SELL",
|
||||
99,
|
||||
1,
|
||||
100,
|
||||
log
|
||||
);
|
||||
const { ctx, adapter, log } = createContext();
|
||||
await placeStopLossOrder(ctx, {
|
||||
openOrders: [],
|
||||
side: "SELL",
|
||||
stopPrice: 99,
|
||||
quantity: 1,
|
||||
lastPrice: 100,
|
||||
});
|
||||
expect(adapter.createOrder).toHaveBeenCalled();
|
||||
expect(log).toHaveBeenCalledWith("stop", expect.stringContaining("STOP_MARKET"));
|
||||
});
|
||||
|
||||
it("places trailing stop order", async () => {
|
||||
const adapter = createMockExchange();
|
||||
const locks: OrderLockMap = {};
|
||||
const timers: OrderTimerMap = {};
|
||||
const pending: OrderPendingMap = {};
|
||||
const log = vi.fn();
|
||||
await placeTrailingStopOrder(
|
||||
adapter,
|
||||
"BTCUSDT",
|
||||
[],
|
||||
locks,
|
||||
timers,
|
||||
pending,
|
||||
"SELL",
|
||||
101,
|
||||
1,
|
||||
0.2,
|
||||
log
|
||||
);
|
||||
const { ctx, adapter, log } = createContext();
|
||||
await placeTrailingStopOrder(ctx, {
|
||||
openOrders: [],
|
||||
side: "SELL",
|
||||
activationPrice: 101,
|
||||
quantity: 1,
|
||||
callbackRate: 0.2,
|
||||
});
|
||||
expect(adapter.createOrder).toHaveBeenCalled();
|
||||
expect(log).toHaveBeenCalledWith("order", expect.stringContaining("挂动态止盈单"));
|
||||
});
|
||||
|
||||
it("market close cancels open orders before placing close order", async () => {
|
||||
const adapter = createMockExchange();
|
||||
const locks: OrderLockMap = {};
|
||||
const timers: OrderTimerMap = {};
|
||||
const pending: OrderPendingMap = {};
|
||||
const log = vi.fn();
|
||||
await marketClose(
|
||||
adapter,
|
||||
"BTCUSDT",
|
||||
[{ ...baseOrder, orderId: 2 }],
|
||||
locks,
|
||||
timers,
|
||||
pending,
|
||||
"SELL",
|
||||
1,
|
||||
log
|
||||
);
|
||||
const { ctx, adapter, log } = createContext();
|
||||
await marketClose(ctx, {
|
||||
openOrders: [{ ...baseOrder, orderId: 2 }],
|
||||
side: "SELL",
|
||||
quantity: 1,
|
||||
});
|
||||
expect(adapter.createOrder).toHaveBeenCalled();
|
||||
expect(log).toHaveBeenCalledWith("close", expect.stringContaining("市价平仓"));
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user