feat: 更新网格引擎,增强订单消失处理逻辑,优化平仓订单管理与目标检测

This commit is contained in:
discountry
2025-10-07 22:26:32 +08:00
parent e524c225f2
commit fef7604490
+30 -11
View File
@@ -490,10 +490,15 @@ export class GridEngine {
} }
} }
if (handledByCloseTracking) continue; if (handledByCloseTracking) continue;
// Otherwise, treat as an open order disappearance and mark pending // Otherwise, treat disappearance as potential open fill: confirm by position delta increase
if (meta) { if (meta) {
if (meta.side === "BUY") this.pendingLongLevels.add(meta.level); const absNow = Math.abs(this.position.positionAmt);
else this.pendingShortLevels.add(meta.level); if (absNow > this.lastAbsPositionAmt + EPSILON) {
if (meta.side === "BUY") this.pendingLongLevels.add(meta.level);
else this.pendingShortLevels.add(meta.level);
} else {
// Likely canceled or expired; do not mark pending
}
} }
} }
this.lastOpenOrderKeys = currentKeys; this.lastOpenOrderKeys = currentKeys;
@@ -511,6 +516,8 @@ export class GridEngine {
for (const level of this.buyLevelIndices) { for (const level of this.buyLevelIndices) {
const levelPrice = this.gridLevels[level]!; const levelPrice = this.gridLevels[level]!;
if (levelPrice >= price - halfTick) continue; if (levelPrice >= price - halfTick) continue;
// do not open if this level is currently a close target for any pending source
if (this.isTargetOfPendingShort(level) || this.isTargetOfPendingLong(level)) continue;
if (this.pendingLongLevels.has(level)) continue; // wait until close filled if (this.pendingLongLevels.has(level)) continue; // wait until close filled
const key = this.getOrderKey("BUY", this.formatPrice(levelPrice), false); const key = this.getOrderKey("BUY", this.formatPrice(levelPrice), false);
if (activeKeySet.has(key)) continue; // already has open order if (activeKeySet.has(key)) continue; // already has open order
@@ -521,6 +528,7 @@ export class GridEngine {
for (const level of this.sellLevelIndices) { for (const level of this.sellLevelIndices) {
const levelPrice = this.gridLevels[level]!; const levelPrice = this.gridLevels[level]!;
if (levelPrice <= price + halfTick) continue; if (levelPrice <= price + halfTick) continue;
if (this.isTargetOfPendingShort(level) || this.isTargetOfPendingLong(level)) continue;
if (this.pendingShortLevels.has(level)) continue; if (this.pendingShortLevels.has(level)) continue;
const key = this.getOrderKey("SELL", this.formatPrice(levelPrice), false); const key = this.getOrderKey("SELL", this.formatPrice(levelPrice), false);
if (activeKeySet.has(key)) continue; if (activeKeySet.has(key)) continue;
@@ -532,24 +540,35 @@ export class GridEngine {
const target = this.levelMeta[source]?.closeTarget; const target = this.levelMeta[source]?.closeTarget;
if (target == null) continue; if (target == null) continue;
const priceStr = this.formatPrice(this.gridLevels[target]!); const priceStr = this.formatPrice(this.gridLevels[target]!);
// always desire a close order; allow placement even if an identical open exists const closeKey = this.getOrderKey("SELL", priceStr, false);
desired.push({ level: target, side: "SELL", price: priceStr, amount: this.config.orderSize, reduceOnly: false }); if (!activeKeySet.has(closeKey)) {
desired.push({ level: target, side: "SELL", price: priceStr, amount: this.config.orderSize, reduceOnly: false });
} else {
// Map existing identical open as the close so disappearance clears pending
if (!this.closeKeyBySourceLevel.has(source)) {
this.closeKeyBySourceLevel.set(source, closeKey);
}
}
} }
for (const source of this.pendingShortLevels) { for (const source of this.pendingShortLevels) {
const target = this.levelMeta[source]?.closeTarget; const target = this.levelMeta[source]?.closeTarget;
if (target == null) continue; if (target == null) continue;
const priceStr = this.formatPrice(this.gridLevels[target]!); const priceStr = this.formatPrice(this.gridLevels[target]!);
desired.push({ level: target, side: "BUY", price: priceStr, amount: this.config.orderSize, reduceOnly: false }); const closeKey = this.getOrderKey("BUY", priceStr, false);
if (!activeKeySet.has(closeKey)) {
desired.push({ level: target, side: "BUY", price: priceStr, amount: this.config.orderSize, reduceOnly: false });
} else {
if (!this.closeKeyBySourceLevel.has(source)) {
this.closeKeyBySourceLevel.set(source, closeKey);
}
}
} }
// Place desired orders // Place desired orders
this.desiredOrders = desired; this.desiredOrders = desired;
for (const d of desired) { for (const d of desired) {
const key = this.getOrderKey(d.side, d.price, d.reduceOnly); const key = this.getOrderKey(d.side, d.price, d.reduceOnly);
const isCloseDesired = if (activeKeySet.has(key)) continue;
(d.side === "SELL" && this.isTargetOfPendingLong(d.level)) ||
(d.side === "BUY" && this.isTargetOfPendingShort(d.level));
if (activeKeySet.has(key) && !isCloseDesired) continue;
try { try {
const placed = await placeOrder( const placed = await placeOrder(
this.exchange, this.exchange,
@@ -566,7 +585,7 @@ export class GridEngine {
undefined, undefined,
{ priceTick: this.config.priceTick, qtyStep: this.config.qtyStep, skipDedupe: true } { priceTick: this.config.priceTick, qtyStep: this.config.qtyStep, skipDedupe: true }
); );
if (placed && isCloseDesired) { if (placed && ((d.side === "SELL" && this.isTargetOfPendingLong(d.level)) || (d.side === "BUY" && this.isTargetOfPendingShort(d.level)))) {
this.closeKeyBySourceLevel.set( this.closeKeyBySourceLevel.set(
this.findSourceForCloseTarget(d.level, d.side), this.findSourceForCloseTarget(d.level, d.side),
key key