feat: 更新网格引擎,进一步简化状态管理,移除持久化逻辑,优化订单处理与目标检测

This commit is contained in:
discountry
2025-10-07 22:34:05 +08:00
parent fef7604490
commit ca559d0f82
+25 -18
View File
@@ -16,7 +16,6 @@ import {
} from "../core/order-coordinator";
import { StrategyEventEmitter } from "./common/event-emitter";
import { safeSubscribe, type LogHandler } from "./common/subscriptions";
// persistence removed per simplified rules
interface DesiredGridOrder {
level: number;
@@ -89,8 +88,6 @@ export class GridEngine {
private readonly levelMeta: LevelMeta[] = [];
private readonly buyLevelIndices: number[] = [];
private readonly sellLevelIndices: number[] = [];
// simplified: exposure maps removed; we only track pending-level states
// simplified state: track last seen open-order keys to detect fills, and pending exposures per-level
private lastOpenOrderKeys = new Set<string>();
private readonly pendingLongLevels = new Set<number>();
private readonly pendingShortLevels = new Set<number>();
@@ -130,7 +127,6 @@ export class GridEngine {
private running: boolean;
private stopReason: string | null = null;
private lastUpdated: number | null = null;
// fresh-start flags removed in simplified flow
constructor(private readonly config: GridConfig, private readonly exchange: ExchangeAdapter, options: EngineOptions = {}) {
this.tradeLog = createTradeLog(this.config.maxLogEntries);
@@ -140,7 +136,6 @@ export class GridEngine {
this.configValid = this.validateConfig();
this.gridLevels = this.computeGridLevels();
this.buildLevelMeta();
// no persistence restore in simplified flow
this.running = this.configValid;
if (!this.configValid) {
this.stopReason = "配置无效,已暂停网格";
@@ -317,7 +312,6 @@ export class GridEngine {
if (!Number.isFinite(price) || price === null) {
return;
}
// simplified: optional stop-loss logic preserved
if (this.shouldStop(price)) {
await this.haltGrid(price);
return;
@@ -339,7 +333,6 @@ export class GridEngine {
return getMidOrLast(this.depthSnapshot, this.tickerSnapshot);
}
// fresh-start and persistence logic removed in simplified flow
private tryLockSidesOnce(): void {
if (this.sidesLocked) return;
@@ -359,7 +352,6 @@ export class GridEngine {
return Math.min(Math.max(price, minLevel), maxLevel);
}
// persistence removed
private hasActiveOrders(): boolean {
return this.openOrders.some((order) => {
@@ -511,16 +503,20 @@ export class GridEngine {
const activeKeySet = new Set(
activeOrders.map((o) => this.getOrderKey(o.side, this.normalizePrice(o.price), o.reduceOnly === true))
);
const activeKeyCounts = new Map<string, number>();
for (const o of activeOrders) {
const k = this.getOrderKey(o.side, this.normalizePrice(o.price), false);
activeKeyCounts.set(k, (activeKeyCounts.get(k) ?? 0) + 1);
}
// opens below price (BUY)
for (const level of this.buyLevelIndices) {
const levelPrice = this.gridLevels[level]!;
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
const key = this.getOrderKey("BUY", this.formatPrice(levelPrice), false);
if (activeKeySet.has(key)) continue; // already has open order
const targetMax = this.isCloseDesiredForSideAtLevel("BUY", level) ? 2 : 1;
if ((activeKeyCounts.get(key) ?? 0) >= targetMax) continue;
desired.push({ level, side: "BUY", price: this.formatPrice(levelPrice), amount: this.config.orderSize, reduceOnly: false });
}
@@ -528,10 +524,10 @@ export class GridEngine {
for (const level of this.sellLevelIndices) {
const levelPrice = this.gridLevels[level]!;
if (levelPrice <= price + halfTick) continue;
if (this.isTargetOfPendingShort(level) || this.isTargetOfPendingLong(level)) continue;
if (this.pendingShortLevels.has(level)) continue;
const key = this.getOrderKey("SELL", this.formatPrice(levelPrice), false);
if (activeKeySet.has(key)) continue;
const targetMax = this.isCloseDesiredForSideAtLevel("SELL", level) ? 2 : 1;
if ((activeKeyCounts.get(key) ?? 0) >= targetMax) continue;
desired.push({ level, side: "SELL", price: this.formatPrice(levelPrice), amount: this.config.orderSize, reduceOnly: false });
}
@@ -541,7 +537,7 @@ export class GridEngine {
if (target == null) continue;
const priceStr = this.formatPrice(this.gridLevels[target]!);
const closeKey = this.getOrderKey("SELL", priceStr, false);
if (!activeKeySet.has(closeKey)) {
if ((activeKeyCounts.get(closeKey) ?? 0) < 2) {
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
@@ -555,7 +551,7 @@ export class GridEngine {
if (target == null) continue;
const priceStr = this.formatPrice(this.gridLevels[target]!);
const closeKey = this.getOrderKey("BUY", priceStr, false);
if (!activeKeySet.has(closeKey)) {
if ((activeKeyCounts.get(closeKey) ?? 0) < 2) {
desired.push({ level: target, side: "BUY", price: priceStr, amount: this.config.orderSize, reduceOnly: false });
} else {
if (!this.closeKeyBySourceLevel.has(source)) {
@@ -568,7 +564,9 @@ export class GridEngine {
this.desiredOrders = desired;
for (const d of desired) {
const key = this.getOrderKey(d.side, d.price, d.reduceOnly);
if (activeKeySet.has(key)) continue;
const isClose = (d.side === "SELL" && this.isTargetOfPendingLong(d.level)) || (d.side === "BUY" && this.isTargetOfPendingShort(d.level));
const maxAllowed = isClose ? 2 : 1;
if ((activeKeyCounts.get(key) ?? 0) >= maxAllowed) continue;
try {
const placed = await placeOrder(
this.exchange,
@@ -585,7 +583,10 @@ export class GridEngine {
undefined,
{ priceTick: this.config.priceTick, qtyStep: this.config.qtyStep, skipDedupe: true }
);
if (placed && ((d.side === "SELL" && this.isTargetOfPendingLong(d.level)) || (d.side === "BUY" && this.isTargetOfPendingShort(d.level)))) {
if (placed) {
activeKeyCounts.set(key, (activeKeyCounts.get(key) ?? 0) + 1);
}
if (placed && isClose) {
this.closeKeyBySourceLevel.set(
this.findSourceForCloseTarget(d.level, d.side),
key
@@ -634,6 +635,13 @@ export class GridEngine {
return false;
}
private isCloseDesiredForSideAtLevel(side: "BUY" | "SELL", level: number): boolean {
if (side === "SELL") {
return this.isTargetOfPendingLong(level);
}
return this.isTargetOfPendingShort(level);
}
private computeGridLevels(): number[] {
if (!this.configValid) return [];
const { lowerPrice, upperPrice, gridLevels } = this.config;
@@ -798,7 +806,6 @@ export class GridEngine {
return ref;
}
// exposure summation removed in simplified flow
private async cancelAllExistingOrdersOnStartup(): Promise<void> {
if (this.startupCleaned) return;