From 6a3b842a3bf6eba88c6f394f707c770083fbf882 Mon Sep 17 00:00:00 2001 From: discountry Date: Wed, 8 Oct 2025 20:05:02 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E5=B9=B3=E4=BB=93?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=E9=80=BB=E8=BE=91=EF=BC=8C=E9=81=BF=E5=85=8D?= =?UTF-8?q?=E5=9C=A8=E6=AD=A2=E6=8D=9F=E8=AE=A2=E5=8D=95=E4=B8=AD=E4=B8=8B?= =?UTF-8?q?=E8=B0=83=E6=95=B0=E9=87=8F=EF=BC=8C=E7=A1=AE=E4=BF=9D=E7=B2=BE?= =?UTF-8?q?=E7=A1=AE=E4=BC=A0=E9=80=92=E7=BB=99=E4=BA=A4=E6=98=93=E6=89=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/order-coordinator.ts | 3 ++- src/exchanges/paradex/gateway.ts | 19 ++++++++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/core/order-coordinator.ts b/src/core/order-coordinator.ts index b3adb80..aa8b37e 100644 --- a/src/core/order-coordinator.ts +++ b/src/core/order-coordinator.ts @@ -249,7 +249,8 @@ export async function placeStopLossOrder( reduceOnly: "true", closePosition: "true", timeInForce: "GTC", - quantity: roundQtyDownToStep(quantity, qtyStep), + // Do not round down stop quantity here to avoid underflow to exchange min; gateway will quantize precisely + quantity, triggerType: "STOP_LOSS", }; // 部分交易所(例如 Paradex)要求 STOP_MARKET 同时提供 price 字段 diff --git a/src/exchanges/paradex/gateway.ts b/src/exchanges/paradex/gateway.ts index d222c66..b164bce 100644 --- a/src/exchanges/paradex/gateway.ts +++ b/src/exchanges/paradex/gateway.ts @@ -566,8 +566,7 @@ export class ParadexGateway { } // Normalize amount for Paradex according to market precision/limits. - // Particularly important for STOP_MARKET closePosition orders where rounding - // upstream may result in 0 due to larger strategy qtyStep. + // For STOP_MARKET closePosition orders, prefer using the current position size. try { const market = typeof (this.exchange as any).market === "function" ? (this.exchange as any).market(symbol) @@ -577,9 +576,13 @@ export class ParadexGateway { // Only trust explicit exchange min limit; do NOT infer 1 from precision=0 const minAmount = Number.isFinite(limitMin) && limitMin > 0 ? limitMin : undefined; - // If closePosition is requested and amount is missing or too small, bump to minimum allowed + // If closePosition is requested and amount is missing or too small, prefer using current position size const isClosePosition = (extraParams as any).closePosition === true; if (isClosePosition) { + const posAbs = this.getCurrentPositionAbs(); + if (Number.isFinite(posAbs) && posAbs > 0) { + amount = posAbs; + } const current = Number(amount); if (!Number.isFinite(current) || current <= 0 || (minAmount !== undefined && current < minAmount)) { amount = (minAmount as number) ?? 1e-5; // fallback if market data is missing @@ -615,6 +618,16 @@ export class ParadexGateway { } } + private getCurrentPositionAbs(): number | undefined { + const snapshot = this.lastBalanceSnapshot; + if (!snapshot) return undefined; + const pos = (snapshot.positions || []).find((p) => p.symbol === this.displaySymbol); + if (!pos) return undefined; + const amt = Number(pos.positionAmt); + if (!Number.isFinite(amt)) return undefined; + return Math.abs(amt); + } + async cancelOrder(params: { symbol: string; orderId: number | string }): Promise { await this.ensureInitialized(params.symbol); try {