From 6b70aa0936cace1ffb955ffaa8377758cc14f974 Mon Sep 17 00:00:00 2001 From: discountry Date: Tue, 7 Oct 2025 14:12:32 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=E7=BD=91=E6=A0=BC?= =?UTF-8?q?=E5=BC=95=E6=93=8E=EF=BC=8C=E4=BC=98=E5=8C=96=E5=B9=B3=E4=BB=93?= =?UTF-8?q?=E9=80=BB=E8=BE=91=E4=BB=A5=E7=8B=AC=E7=AB=8B=E5=A4=84=E7=90=86?= =?UTF-8?q?=E5=87=8F=E4=BB=93=E8=AE=A2=E5=8D=95=EF=BC=8C=E5=A2=9E=E5=BC=BA?= =?UTF-8?q?=E4=B9=B0=E5=8D=96=E6=A1=A3=E4=BD=8D=E6=98=A0=E5=B0=84=E7=AE=A1?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/strategy/grid-engine.ts | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/src/strategy/grid-engine.ts b/src/strategy/grid-engine.ts index c3f9adc..6e0d009 100644 --- a/src/strategy/grid-engine.ts +++ b/src/strategy/grid-engine.ts @@ -637,7 +637,10 @@ export class GridEngine { if (this.config.direction !== "short") { for (const { level, levelPrice } of belowPrice) { - if (shortCloseRequirements.has(level)) continue; + // 平仓档位和开仓档位独立:检查是否有减仓订单占用此档位 + const hasCloseOrder = longCloseRequirements.has(level); + if (hasCloseOrder) continue; + const exposure = this.longExposure.get(level) ?? 0; if (exposure >= this.config.orderSize - EPSILON) continue; if (remainingLongHeadroom < this.config.orderSize - EPSILON) continue; @@ -654,7 +657,10 @@ export class GridEngine { if (this.config.direction !== "long") { for (const { level, levelPrice } of abovePrice) { - if (longCloseRequirements.has(level)) continue; + // 平仓档位和开仓档位独立:检查是否有减仓订单占用此档位 + const hasCloseOrder = shortCloseRequirements.has(level); + if (hasCloseOrder) continue; + const exposure = this.shortExposure.get(level) ?? 0; if (exposure >= this.config.orderSize - EPSILON) continue; if (remainingShortHeadroom < this.config.orderSize - EPSILON) continue; @@ -1008,12 +1014,30 @@ export class GridEngine { if (side === "BUY") this.buyLevelIndices.push(i); else this.sellLevelIndices.push(i); } + // 为每个买入档位分配独立的卖出档位,实现一对一映射 + const sellLevels = this.levelMeta.filter(meta => meta.side === "SELL").sort((a, b) => a.index - b.index); + let sellLevelIndex = 0; + for (const meta of this.levelMeta) { if (meta.side === "BUY") { - const target = this.levelMeta.find((candidate) => candidate.index > meta.index && candidate.side === "SELL"); - meta.closeTarget = target?.index ?? null; - if (target) target.closeSources.push(meta.index); + // 为每个买入档位分配下一个可用的卖出档位 + if (sellLevelIndex < sellLevels.length) { + const targetSellLevel = sellLevels[sellLevelIndex]!; + meta.closeTarget = targetSellLevel.index; + targetSellLevel.closeSources.push(meta.index); + sellLevelIndex++; + } else { + // 如果没有足够的卖出档位,使用最后一个卖出档位 + const lastSellLevel = sellLevels[sellLevels.length - 1]; + if (lastSellLevel) { + meta.closeTarget = lastSellLevel.index; + lastSellLevel.closeSources.push(meta.index); + } else { + meta.closeTarget = null; + } + } } else { + // 卖出档位:找到对应的买入档位(用于空单平仓) for (let j = meta.index - 1; j >= 0; j -= 1) { if (this.levelMeta[j]!.side === "BUY") { meta.closeTarget = this.levelMeta[j]!.index;