feat: 更新网格引擎,优化等待分类机制,增强账户快照确认逻辑以处理消失订单

This commit is contained in:
discountry
2025-10-08 01:45:12 +08:00
parent 20c73eba21
commit 801543488e
+10 -10
View File
@@ -98,8 +98,8 @@ export class GridEngine {
// temporarily block re-opening at that level to avoid immediate re-placement. // temporarily block re-opening at that level to avoid immediate re-placement.
private readonly levelReopenBlockUntil = new Map<number, number>(); private readonly levelReopenBlockUntil = new Map<number, number>();
private static readonly DISAPPEAR_REOPEN_COOLDOWN_MS = 2000; private static readonly DISAPPEAR_REOPEN_COOLDOWN_MS = 2000;
// Track levels awaiting classification after disappearance when neither fill nor cancel is confirmed // Track levels awaiting classification after disappearance until next account snapshot confirms
private readonly awaitingClassification = new Map<number, { until: number; absAtStart: number }>(); private readonly awaitingClassification = new Map<number, { accountVerAtStart: number; absAtStart: number }>();
private static readonly AWAIT_CLASSIFICATION_TIMEOUT_MS = 2500; private static readonly AWAIT_CLASSIFICATION_TIMEOUT_MS = 2500;
private sidesLocked = false; private sidesLocked = false;
private startupCleaned = false; private startupCleaned = false;
@@ -135,6 +135,7 @@ export class GridEngine {
private running: boolean; private running: boolean;
private stopReason: string | null = null; private stopReason: string | null = null;
private lastUpdated: number | null = null; private lastUpdated: number | null = null;
private accountVersion = 0;
constructor(private readonly config: GridConfig, private readonly exchange: ExchangeAdapter, options: EngineOptions = {}) { constructor(private readonly config: GridConfig, private readonly exchange: ExchangeAdapter, options: EngineOptions = {}) {
this.tradeLog = createTradeLog(this.config.maxLogEntries); this.tradeLog = createTradeLog(this.config.maxLogEntries);
@@ -210,6 +211,7 @@ export class GridEngine {
(snapshot) => { (snapshot) => {
this.accountSnapshot = snapshot; this.accountSnapshot = snapshot;
this.position = getPosition(snapshot, this.config.symbol); this.position = getPosition(snapshot, this.config.symbol);
this.accountVersion += 1;
this.lastAbsPositionAmt = Math.abs(this.position.positionAmt); this.lastAbsPositionAmt = Math.abs(this.position.positionAmt);
if (!this.feedArrived.account) { if (!this.feedArrived.account) {
this.feedArrived.account = true; this.feedArrived.account = true;
@@ -529,9 +531,8 @@ export class GridEngine {
this.awaitingClassification.delete(meta.level); this.awaitingClassification.delete(meta.level);
this.levelReopenBlockUntil.delete(meta.level); this.levelReopenBlockUntil.delete(meta.level);
} else { } else {
// unknown: defer decision until position/account updates or timeout // unknown: defer decision until we see a newer account snapshot
const until = this.now() + GridEngine.AWAIT_CLASSIFICATION_TIMEOUT_MS; this.awaitingClassification.set(meta.level, { accountVerAtStart: this.accountVersion, absAtStart: this.lastAbsPositionAmt });
this.awaitingClassification.set(meta.level, { until, absAtStart: this.lastAbsPositionAmt });
this.blockLevelReopen(meta.level); this.blockLevelReopen(meta.level);
} }
} }
@@ -541,20 +542,19 @@ export class GridEngine {
this.lastKeyMeta = keyToMeta; this.lastKeyMeta = keyToMeta;
this.lastOrderIdsByKey = currentOrderIdsByKey; this.lastOrderIdsByKey = currentOrderIdsByKey;
// Resolve any awaiting classifications based on updated account snapshot or timeout // Resolve awaiting classifications only after a new account snapshot
if (this.awaitingClassification.size) { if (this.awaitingClassification.size) {
const nowTs = this.now();
for (const [level, info] of Array.from(this.awaitingClassification.entries())) { for (const [level, info] of Array.from(this.awaitingClassification.entries())) {
const absNow = Math.abs(this.position.positionAmt); const absNow = Math.abs(this.position.positionAmt);
if (absNow > info.absAtStart + EPSILON) { if (this.accountVersion > info.accountVerAtStart && absNow > info.absAtStart + EPSILON) {
// treat as filled // treat as filled
const side = this.levelMeta[level]?.side === "BUY" ? "BUY" : "SELL"; const side = this.levelMeta[level]?.side === "BUY" ? "BUY" : "SELL";
if (side === "BUY") this.pendingLongLevels.add(level); if (side === "BUY") this.pendingLongLevels.add(level);
else this.pendingShortLevels.add(level); else this.pendingShortLevels.add(level);
this.awaitingClassification.delete(level); this.awaitingClassification.delete(level);
this.levelReopenBlockUntil.delete(level); this.levelReopenBlockUntil.delete(level);
} else if (nowTs >= info.until) { } else if (this.accountVersion > info.accountVerAtStart && !(absNow > info.absAtStart + EPSILON)) {
// timeout -> treat as canceled // after new account snapshot without increased exposure -> canceled
this.awaitingClassification.delete(level); this.awaitingClassification.delete(level);
this.levelReopenBlockUntil.delete(level); this.levelReopenBlockUntil.delete(level);
} }