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:
discountry
2026-07-29 21:17:58 +08:00
parent 6f64dd5a0a
commit 5aecaabb16
10 changed files with 542 additions and 708 deletions
+30 -32
View File
@@ -20,7 +20,7 @@ import {
placeOrder,
unlockOperating,
} from "../core/order-coordinator";
import type { OrderLockMap, OrderPendingMap, OrderTimerMap } from "../core/order-coordinator";
import type { OrderContext, OrderLockMap, OrderPendingMap, OrderTimerMap } from "../core/order-coordinator";
import { makeOrderPlan } from "../core/lib/order-plan";
import { safeCancelOrder } from "../core/lib/orders";
import { RateLimitController } from "../core/lib/rate-limit";
@@ -127,6 +127,19 @@ export class MakerEngine {
this.bootstrap();
}
/** Bundles the fixed order-routing state; rebuilt lazily on first use. */
private get orderContext(): OrderContext {
return (this.orderContextCache ??= {
adapter: this.exchange,
symbol: this.config.symbol,
locks: this.locks,
timers: this.timers,
pendings: this.pending,
log: (type, detail) => this.tradeLog.push(type, detail),
});
}
private orderContextCache: OrderContext | null = null;
start(): void {
if (this.timer) return;
this.timer = setInterval(() => {
@@ -439,27 +452,18 @@ export class MakerEngine {
if (!target) continue;
if (target.amount < EPS) continue;
try {
await placeOrder(
this.exchange,
this.config.symbol,
this.openOrders,
this.locks,
this.timers,
this.pending,
target.side,
target.price, // 已经是字符串价格
target.amount,
(type, detail) => this.tradeLog.push(type, detail),
target.reduceOnly,
{
await placeOrder(this.orderContext, {
openOrders: this.openOrders,
side: target.side,
price: target.price,
amount: target.amount,
reduceOnly: target.reduceOnly,
guard: {
markPrice: getPosition(this.accountSnapshot, this.config.symbol).markPrice,
maxPct: this.config.maxCloseSlippagePct,
},
{
priceTick: this.precision.priceTick,
qtyStep: this.precision.qtyStep,
}
);
qtyStep: this.precision.qtyStep
});
} catch (error) {
if (isInsufficientBalanceError(error)) {
this.registerInsufficientBalance(error);
@@ -507,23 +511,17 @@ export class MakerEngine {
);
try {
await this.flushOrders();
await marketClose(
this.exchange,
this.config.symbol,
this.openOrders,
this.locks,
this.timers,
this.pending,
position.positionAmt > 0 ? "SELL" : "BUY",
absPosition,
(type, detail) => this.tradeLog.push(type, detail),
{
await marketClose(this.orderContext, {
openOrders: this.openOrders,
side: position.positionAmt > 0 ? "SELL" : "BUY",
quantity: absPosition,
guard: {
markPrice: position.markPrice,
expectedPrice: Number(closeSidePrice) || null,
maxPct: this.config.maxCloseSlippagePct,
},
{ qtyStep: this.precision.qtyStep }
);
qtyStep: this.precision.qtyStep
});
} catch (error) {
if (isUnknownOrderError(error)) {
this.tradeLog.push("order", t("log.maker.stopOrderMissing"));