From 6f05ee0da36548f25aa98716d49912ef8f6390df Mon Sep 17 00:00:00 2001 From: discountry Date: Wed, 24 Sep 2025 04:03:15 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20MakerEngine=E3=80=81Offset?= =?UTF-8?q?MakerEngine=20=E5=92=8C=20TrendEngine=EF=BC=8C=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E8=B4=A6=E6=88=B7=E3=80=81=E8=AE=A2=E5=8D=95=E3=80=81?= =?UTF-8?q?=E6=B7=B1=E5=BA=A6=E3=80=81=E4=BB=B7=E6=A0=BC=E5=92=8CK?= =?UTF-8?q?=E7=BA=BF=E6=8E=A8=E9=80=81=E7=9A=84=E5=BC=82=E5=B8=B8=E5=A4=84?= =?UTF-8?q?=E7=90=86=E9=80=BB=E8=BE=91=EF=BC=8C=E7=A1=AE=E4=BF=9D=E5=9C=A8?= =?UTF-8?q?=E6=8E=A8=E9=80=81=E5=A4=B1=E8=B4=A5=E6=97=B6=E8=AE=B0=E5=BD=95?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E4=BF=A1=E6=81=AF=E4=BB=A5=E4=BE=BF=E4=BA=8E?= =?UTF-8?q?=E8=B0=83=E8=AF=95=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/maker-engine.ts | 133 ++++++++++++++++++++++---------- src/core/offset-maker-engine.ts | 133 ++++++++++++++++++++++---------- src/core/trend-engine.ts | 131 +++++++++++++++++++++---------- 3 files changed, 275 insertions(+), 122 deletions(-) diff --git a/src/core/maker-engine.ts b/src/core/maker-engine.ts index 34e1fc6..8a33117 100644 --- a/src/core/maker-engine.ts +++ b/src/core/maker-engine.ts @@ -109,53 +109,94 @@ export class MakerEngine { } private bootstrap(): void { - this.exchange.watchAccount((snapshot) => { - this.accountSnapshot = snapshot; - const totalUnrealized = Number(snapshot.totalUnrealizedProfit ?? "0"); - if (Number.isFinite(totalUnrealized)) { - this.accountUnrealized = totalUnrealized; - } - const position = getPosition(snapshot, this.config.symbol); - this.updateSessionVolume(position); - this.emitUpdate(); - }); - - this.exchange.watchOrders((orders) => { - this.syncLocksWithOrders(orders); - this.openOrders = Array.isArray(orders) - ? orders.filter((order) => order.type !== "MARKET" && order.symbol === this.config.symbol) - : []; - const currentIds = new Set(this.openOrders.map((order) => order.orderId)); - for (const id of Array.from(this.pendingCancelOrders)) { - if (!currentIds.has(id)) { - this.pendingCancelOrders.delete(id); + try { + this.exchange.watchAccount((snapshot) => { + try { + this.accountSnapshot = snapshot; + const totalUnrealized = Number(snapshot.totalUnrealizedProfit ?? "0"); + if (Number.isFinite(totalUnrealized)) { + this.accountUnrealized = totalUnrealized; + } + const position = getPosition(snapshot, this.config.symbol); + this.updateSessionVolume(position); + this.emitUpdate(); + } catch (err) { + this.tradeLog.push("error", `账户推送处理异常: ${String(err)}`); } - } - this.initialOrderSnapshotReady = true; - this.emitUpdate(); - }); + }); + } catch (err) { + this.tradeLog.push("error", `订阅账户失败: ${String(err)}`); + } - this.exchange.watchDepth(this.config.symbol, (depth) => { - this.depthSnapshot = depth; - this.emitUpdate(); - }); + try { + this.exchange.watchOrders((orders) => { + try { + this.syncLocksWithOrders(orders); + this.openOrders = Array.isArray(orders) + ? orders.filter((order) => order.type !== "MARKET" && order.symbol === this.config.symbol) + : []; + const currentIds = new Set(this.openOrders.map((order) => order.orderId)); + for (const id of Array.from(this.pendingCancelOrders)) { + if (!currentIds.has(id)) { + this.pendingCancelOrders.delete(id); + } + } + this.initialOrderSnapshotReady = true; + this.emitUpdate(); + } catch (err) { + this.tradeLog.push("error", `订单推送处理异常: ${String(err)}`); + } + }); + } catch (err) { + this.tradeLog.push("error", `订阅订单失败: ${String(err)}`); + } - this.exchange.watchTicker(this.config.symbol, (ticker) => { - this.tickerSnapshot = ticker; - this.emitUpdate(); - }); + try { + this.exchange.watchDepth(this.config.symbol, (depth) => { + try { + this.depthSnapshot = depth; + this.emitUpdate(); + } catch (err) { + this.tradeLog.push("error", `深度推送处理异常: ${String(err)}`); + } + }); + } catch (err) { + this.tradeLog.push("error", `订阅深度失败: ${String(err)}`); + } + + try { + this.exchange.watchTicker(this.config.symbol, (ticker) => { + try { + this.tickerSnapshot = ticker; + this.emitUpdate(); + } catch (err) { + this.tradeLog.push("error", `价格推送处理异常: ${String(err)}`); + } + }); + } catch (err) { + this.tradeLog.push("error", `订阅Ticker失败: ${String(err)}`); + } // Maker strategy does not consume klines, but subscribe to keep parity with other modules - this.exchange.watchKlines(this.config.symbol, "1m", () => { - /* no-op */ - }); + try { + this.exchange.watchKlines(this.config.symbol, "1m", () => { + try { + /* no-op */ + } catch (err) { + this.tradeLog.push("error", `K线推送处理异常: ${String(err)}`); + } + }); + } catch (err) { + this.tradeLog.push("error", `订阅K线失败: ${String(err)}`); + } } - private syncLocksWithOrders(orders: AsterOrder[]): void { + private syncLocksWithOrders(orders: AsterOrder[] | null | undefined): void { + const list = Array.isArray(orders) ? orders : []; Object.keys(this.pending).forEach((type) => { const pendingId = this.pending[type]; if (!pendingId) return; - const match = orders.find((order) => String(order.orderId) === pendingId); + const match = list.find((order) => String(order.orderId) === pendingId); if (!match || (match.status && match.status !== "NEW" && match.status !== "PARTIALLY_FILLED")) { unlockOperating(this.locks, this.timers, this.pending, type); } @@ -407,10 +448,20 @@ export class MakerEngine { } private emitUpdate(): void { - const snapshot = this.buildSnapshot(); - const handlers = this.listeners.get("update"); - if (handlers) { - handlers.forEach((handler) => handler(snapshot)); + try { + const snapshot = this.buildSnapshot(); + const handlers = this.listeners.get("update"); + if (handlers) { + handlers.forEach((handler) => { + try { + handler(snapshot); + } catch (err) { + this.tradeLog.push("error", `更新回调处理异常: ${String(err)}`); + } + }); + } + } catch (err) { + this.tradeLog.push("error", `快照或更新分发异常: ${String(err)}`); } } diff --git a/src/core/offset-maker-engine.ts b/src/core/offset-maker-engine.ts index 0ea7b9c..add9d5c 100644 --- a/src/core/offset-maker-engine.ts +++ b/src/core/offset-maker-engine.ts @@ -108,52 +108,93 @@ export class OffsetMakerEngine { } private bootstrap(): void { - this.exchange.watchAccount((snapshot) => { - this.accountSnapshot = snapshot; - const totalUnrealized = Number(snapshot.totalUnrealizedProfit ?? "0"); - if (Number.isFinite(totalUnrealized)) { - this.accountUnrealized = totalUnrealized; - } - const position = getPosition(snapshot, this.config.symbol); - this.updateSessionVolume(position); - this.emitUpdate(); - }); - - this.exchange.watchOrders((orders) => { - this.syncLocksWithOrders(orders); - this.openOrders = Array.isArray(orders) - ? orders.filter((order) => order.type !== "MARKET" && order.symbol === this.config.symbol) - : []; - const currentIds = new Set(this.openOrders.map((order) => order.orderId)); - for (const id of Array.from(this.pendingCancelOrders)) { - if (!currentIds.has(id)) { - this.pendingCancelOrders.delete(id); + try { + this.exchange.watchAccount((snapshot) => { + try { + this.accountSnapshot = snapshot; + const totalUnrealized = Number(snapshot.totalUnrealizedProfit ?? "0"); + if (Number.isFinite(totalUnrealized)) { + this.accountUnrealized = totalUnrealized; + } + const position = getPosition(snapshot, this.config.symbol); + this.updateSessionVolume(position); + this.emitUpdate(); + } catch (err) { + this.tradeLog.push("error", `账户推送处理异常: ${String(err)}`); } - } - this.initialOrderSnapshotReady = true; - this.emitUpdate(); - }); + }); + } catch (err) { + this.tradeLog.push("error", `订阅账户失败: ${String(err)}`); + } - this.exchange.watchDepth(this.config.symbol, (depth) => { - this.depthSnapshot = depth; - this.emitUpdate(); - }); + try { + this.exchange.watchOrders((orders) => { + try { + this.syncLocksWithOrders(orders); + this.openOrders = Array.isArray(orders) + ? orders.filter((order) => order.type !== "MARKET" && order.symbol === this.config.symbol) + : []; + const currentIds = new Set(this.openOrders.map((order) => order.orderId)); + for (const id of Array.from(this.pendingCancelOrders)) { + if (!currentIds.has(id)) { + this.pendingCancelOrders.delete(id); + } + } + this.initialOrderSnapshotReady = true; + this.emitUpdate(); + } catch (err) { + this.tradeLog.push("error", `订单推送处理异常: ${String(err)}`); + } + }); + } catch (err) { + this.tradeLog.push("error", `订阅订单失败: ${String(err)}`); + } - this.exchange.watchTicker(this.config.symbol, (ticker) => { - this.tickerSnapshot = ticker; - this.emitUpdate(); - }); + try { + this.exchange.watchDepth(this.config.symbol, (depth) => { + try { + this.depthSnapshot = depth; + this.emitUpdate(); + } catch (err) { + this.tradeLog.push("error", `深度推送处理异常: ${String(err)}`); + } + }); + } catch (err) { + this.tradeLog.push("error", `订阅深度失败: ${String(err)}`); + } - this.exchange.watchKlines(this.config.symbol, "1m", () => { - /* no-op */ - }); + try { + this.exchange.watchTicker(this.config.symbol, (ticker) => { + try { + this.tickerSnapshot = ticker; + this.emitUpdate(); + } catch (err) { + this.tradeLog.push("error", `价格推送处理异常: ${String(err)}`); + } + }); + } catch (err) { + this.tradeLog.push("error", `订阅Ticker失败: ${String(err)}`); + } + + try { + this.exchange.watchKlines(this.config.symbol, "1m", () => { + try { + /* no-op */ + } catch (err) { + this.tradeLog.push("error", `K线推送处理异常: ${String(err)}`); + } + }); + } catch (err) { + this.tradeLog.push("error", `订阅K线失败: ${String(err)}`); + } } - private syncLocksWithOrders(orders: AsterOrder[]): void { + private syncLocksWithOrders(orders: AsterOrder[] | null | undefined): void { + const list = Array.isArray(orders) ? orders : []; Object.keys(this.pending).forEach((type) => { const pendingId = this.pending[type]; if (!pendingId) return; - const match = orders.find((order) => String(order.orderId) === pendingId); + const match = list.find((order) => String(order.orderId) === pendingId); if (!match || (match.status && match.status !== "NEW" && match.status !== "PARTIALLY_FILLED")) { unlockOperating(this.locks, this.timers, this.pending, type); } @@ -499,10 +540,20 @@ export class OffsetMakerEngine { } private emitUpdate(): void { - const snapshot = this.buildSnapshot(); - const handlers = this.listeners.get("update"); - if (handlers) { - handlers.forEach((handler) => handler(snapshot)); + try { + const snapshot = this.buildSnapshot(); + const handlers = this.listeners.get("update"); + if (handlers) { + handlers.forEach((handler) => { + try { + handler(snapshot); + } catch (err) { + this.tradeLog.push("error", `更新回调处理异常: ${String(err)}`); + } + }); + } + } catch (err) { + this.tradeLog.push("error", `快照或更新分发异常: ${String(err)}`); } } diff --git a/src/core/trend-engine.ts b/src/core/trend-engine.ts index fa95a41..5104f25 100644 --- a/src/core/trend-engine.ts +++ b/src/core/trend-engine.ts @@ -126,48 +126,89 @@ export class TrendEngine { } private bootstrap(): void { - this.exchange.watchAccount((snapshot) => { - this.accountSnapshot = snapshot; - const position = getPosition(snapshot, this.config.symbol); - this.updateSessionVolume(position); - this.emitUpdate(); - }); - this.exchange.watchOrders((orders) => { - this.synchronizeLocks(orders); - this.openOrders = Array.isArray(orders) - ? orders.filter((order) => order.type !== "MARKET" && order.symbol === this.config.symbol) - : []; - const currentIds = new Set(this.openOrders.map((order) => order.orderId)); - for (const id of Array.from(this.pendingCancelOrders)) { - if (!currentIds.has(id)) { - this.pendingCancelOrders.delete(id); + try { + this.exchange.watchAccount((snapshot) => { + try { + this.accountSnapshot = snapshot; + const position = getPosition(snapshot, this.config.symbol); + this.updateSessionVolume(position); + this.emitUpdate(); + } catch (err) { + this.tradeLog.push("error", `账户推送处理异常: ${String(err)}`); } - } - if (this.openOrders.length === 0 || this.pendingCancelOrders.size === 0) { - this.cancelAllRequested = false; - } - this.ordersSnapshotReady = true; - this.emitUpdate(); - }); - this.exchange.watchDepth(this.config.symbol, (depth) => { - this.depthSnapshot = depth; - this.emitUpdate(); - }); - this.exchange.watchTicker(this.config.symbol, (ticker) => { - this.tickerSnapshot = ticker; - this.emitUpdate(); - }); - this.exchange.watchKlines(this.config.symbol, this.config.klineInterval, (klines) => { - this.klineSnapshot = klines; - this.emitUpdate(); - }); + }); + } catch (err) { + this.tradeLog.push("error", `订阅账户失败: ${String(err)}`); + } + try { + this.exchange.watchOrders((orders) => { + try { + this.synchronizeLocks(orders); + this.openOrders = Array.isArray(orders) + ? orders.filter((order) => order.type !== "MARKET" && order.symbol === this.config.symbol) + : []; + const currentIds = new Set(this.openOrders.map((order) => order.orderId)); + for (const id of Array.from(this.pendingCancelOrders)) { + if (!currentIds.has(id)) { + this.pendingCancelOrders.delete(id); + } + } + if (this.openOrders.length === 0 || this.pendingCancelOrders.size === 0) { + this.cancelAllRequested = false; + } + this.ordersSnapshotReady = true; + this.emitUpdate(); + } catch (err) { + this.tradeLog.push("error", `订单推送处理异常: ${String(err)}`); + } + }); + } catch (err) { + this.tradeLog.push("error", `订阅订单失败: ${String(err)}`); + } + try { + this.exchange.watchDepth(this.config.symbol, (depth) => { + try { + this.depthSnapshot = depth; + this.emitUpdate(); + } catch (err) { + this.tradeLog.push("error", `深度推送处理异常: ${String(err)}`); + } + }); + } catch (err) { + this.tradeLog.push("error", `订阅深度失败: ${String(err)}`); + } + try { + this.exchange.watchTicker(this.config.symbol, (ticker) => { + try { + this.tickerSnapshot = ticker; + this.emitUpdate(); + } catch (err) { + this.tradeLog.push("error", `价格推送处理异常: ${String(err)}`); + } + }); + } catch (err) { + this.tradeLog.push("error", `订阅Ticker失败: ${String(err)}`); + } + try { + this.exchange.watchKlines(this.config.symbol, this.config.klineInterval, (klines) => { + try { + this.klineSnapshot = Array.isArray(klines) ? klines : []; + this.emitUpdate(); + } catch (err) { + this.tradeLog.push("error", `K线推送处理异常: ${String(err)}`); + } + }); + } catch (err) { + this.tradeLog.push("error", `订阅K线失败: ${String(err)}`); + } } - private synchronizeLocks(orders: AsterOrder[]): void { + private synchronizeLocks(orders: AsterOrder[] | null | undefined): void { + const list = Array.isArray(orders) ? orders : []; Object.keys(this.pending).forEach((type) => { const pendingId = this.pending[type]; if (!pendingId) return; - const match = orders.find((order) => String(order.orderId) === pendingId); + const match = list.find((order) => String(order.orderId) === pendingId); if (!match || (match.status && match.status !== "NEW")) { unlockOperating(this.locks, this.timers, this.pending, type); } @@ -532,10 +573,20 @@ export class TrendEngine { } private emitUpdate(): void { - const snapshot = this.buildSnapshot(); - const handlers = this.listeners.get("update"); - if (handlers) { - handlers.forEach((handler) => handler(snapshot)); + try { + const snapshot = this.buildSnapshot(); + const handlers = this.listeners.get("update"); + if (handlers) { + handlers.forEach((handler) => { + try { + handler(snapshot); + } catch (err) { + this.tradeLog.push("error", `更新回调处理异常: ${String(err)}`); + } + }); + } + } catch (err) { + this.tradeLog.push("error", `快照或更新分发异常: ${String(err)}`); } }