From 8923e2d2b8eee351bf528a2cc76f44dc06469bf8 Mon Sep 17 00:00:00 2001 From: discountry Date: Thu, 25 Sep 2025 20:38:23 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=BC=BA=E8=B6=8B=E5=8A=BF=E5=BC=95?= =?UTF-8?q?=E6=93=8E=E9=80=BB=E8=BE=91=EF=BC=8C=E6=B7=BB=E5=8A=A0=E5=85=A5?= =?UTF-8?q?=E5=9C=BA=E9=A2=91=E7=8E=87=E6=8E=A7=E5=88=B6=E5=92=8C=E6=AD=A2?= =?UTF-8?q?=E6=8D=9F=E5=90=8E=E5=86=B7=E5=8D=B4=E6=9C=BA=E5=88=B6=EF=BC=8C?= =?UTF-8?q?=E7=A1=AE=E4=BF=9D=E5=9C=A8=E7=89=B9=E5=AE=9A=E6=97=B6=E9=97=B4?= =?UTF-8?q?=E5=86=85=E9=99=90=E5=88=B6=E9=87=8D=E5=A4=8D=E5=85=A5=E5=9C=BA?= =?UTF-8?q?=EF=BC=8C=E6=8F=90=E9=AB=98=E4=BA=A4=E6=98=93=E7=AD=96=E7=95=A5?= =?UTF-8?q?=E7=9A=84=E7=A8=B3=E5=AE=9A=E6=80=A7=E5=92=8C=E6=9C=89=E6=95=88?= =?UTF-8?q?=E6=80=A7=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/trend-engine.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/core/trend-engine.ts b/src/core/trend-engine.ts index 04b1449..5861709 100644 --- a/src/core/trend-engine.ts +++ b/src/core/trend-engine.ts @@ -83,6 +83,11 @@ export class TrendEngine { private cancelAllRequested = false; private readonly pendingCancelOrders = new Set(); + // 控制入场频率:同一分钟内最多入场一次 + private lastEntryMinute: number | null = null; + // 止损后冷却:止损发生后的 60s 内忽略 SMA 入场信号 + private lastStopLossAt: number | null = null; + private ordersSnapshotReady = false; private startupLogged = false; private entryPricePendingLogged = false; @@ -287,6 +292,19 @@ export class TrendEngine { private async handleOpenPosition(currentPrice: number, currentSma: number): Promise { this.entryPricePendingLogged = false; + const now = Date.now(); + const currentMinute = Math.floor(now / 60_000); + // 止损后的冷却期:60s 内不允许基于 SMA 穿越再次入场 + if (this.lastStopLossAt != null && now - this.lastStopLossAt < 60_000) { + const remaining = Math.max(0, 60_000 - (now - this.lastStopLossAt)); + this.tradeLog.push("info", `止损后冷却中 ${(remaining / 1000).toFixed(0)}s,忽略入场信号`); + return; + } + // 同一分钟只允许一次入场 + if (this.lastEntryMinute != null && this.lastEntryMinute === currentMinute) { + this.tradeLog.push("info", "本分钟已入场,忽略新的 SMA 入场信号"); + return; + } if (this.lastPrice == null) { this.lastPrice = currentPrice; return; @@ -313,8 +331,10 @@ export class TrendEngine { } if (this.lastPrice > currentSma && currentPrice < currentSma) { await this.submitMarketOrder("SELL", currentPrice, "下穿SMA30,市价开空"); + this.lastEntryMinute = currentMinute; } else if (this.lastPrice < currentSma && currentPrice > currentSma) { await this.submitMarketOrder("BUY", currentPrice, "上穿SMA30,市价开多"); + this.lastEntryMinute = currentMinute; } } @@ -574,6 +594,8 @@ export class TrendEngine { { qtyStep: this.config.qtyStep } ); this.tradeLog.push("close", `止损平仓: ${direction === "long" ? "SELL" : "BUY"}`); + // 记录止损时间以便短期内抑制再次入场 + this.lastStopLossAt = Date.now(); } catch (err) { if (isUnknownOrderError(err)) { this.tradeLog.push("order", "止损平仓时目标订单已不存在");