From f991215d8602f19c9687bf7ea2354c65f9a8b3d9 Mon Sep 17 00:00:00 2001 From: discountry Date: Thu, 9 Oct 2025 20:18:13 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E5=88=9B=E5=BB=BA=E5=92=8C=E6=AD=A2=E6=8D=9F=E9=80=BB=E8=BE=91?= =?UTF-8?q?=EF=BC=8C=E7=A1=AE=E4=BF=9D=E5=8F=82=E6=95=B0=E5=A4=84=E7=90=86?= =?UTF-8?q?=E7=AC=A6=E5=90=88=E4=BA=A4=E6=98=93=E6=89=80=E8=A6=81=E6=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/order-coordinator.ts | 7 ++++--- src/exchanges/aster/client.ts | 23 ++++++++++++++++++++++- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/core/order-coordinator.ts b/src/core/order-coordinator.ts index aa8b37e..9b0ddbd 100644 --- a/src/core/order-coordinator.ts +++ b/src/core/order-coordinator.ts @@ -253,8 +253,7 @@ export async function placeStopLossOrder( quantity, triggerType: "STOP_LOSS", }; - // 部分交易所(例如 Paradex)要求 STOP_MARKET 同时提供 price 字段 - params.price = params.stopPrice; + // Avoid forcing price for STOP_MARKET globally; keep this exchange-specific in gateways await deduplicateOrders(adapter, symbol, openOrders, locks, timers, pendings, type, side, log); lockOperating(locks, timers, pendings, type, log); try { @@ -296,7 +295,9 @@ export async function placeTrailingStopOrder( symbol, side, type, - quantity: roundQtyDownToStep(quantity, qtyStep), + // Do not round down trailing-stop quantity to avoid underflowing small positions to zero; + // let the exchange adapter handle precise quantization. + quantity, reduceOnly: "true", activationPrice: roundDownToTick(activationPrice, priceTick), callbackRate, diff --git a/src/exchanges/aster/client.ts b/src/exchanges/aster/client.ts index 71d3204..b4c172b 100644 --- a/src/exchanges/aster/client.ts +++ b/src/exchanges/aster/client.ts @@ -778,7 +778,28 @@ export class AsterRestClient { } async createOrder(params: CreateOrderParams): Promise { - const payload: Record = { ...params }; + // Sanitize and normalize params for Aster futures API. Paradex-specific flags + // like reduceOnly/closePosition on STOP/TRAILING should not leak here. + const payload: Record = {}; + payload.symbol = String(params.symbol).toUpperCase(); + payload.side = params.side; + payload.type = params.type; + if (params.timeInForce !== undefined) payload.timeInForce = params.timeInForce; + if (params.price !== undefined) payload.price = params.price; + if (params.stopPrice !== undefined) payload.stopPrice = params.stopPrice; + if (params.activationPrice !== undefined) payload.activationPrice = params.activationPrice; + if (params.callbackRate !== undefined) payload.callbackRate = params.callbackRate; + if (params.quantity !== undefined) payload.quantity = Math.abs(params.quantity); + + // Aster rejects reduceOnly/closePosition for certain order types (e.g. STOP/TRAILING). + // Keep the behavior exchange-specific by stripping them here for Aster. + const type = String(params.type).toUpperCase(); + const isStopOrTrailing = type === "STOP_MARKET" || type === "TRAILING_STOP_MARKET"; + if (!isStopOrTrailing) { + if (params.reduceOnly !== undefined) payload.reduceOnly = params.reduceOnly; + if (params.closePosition !== undefined) payload.closePosition = params.closePosition; + } + const response = await this.signedRequest({ path: "/fapi/v1/order", method: "POST", params: payload }); return toOrderFromRest(response); }