更新 MakerEngine、OffsetMakerEngine 和 TrendEngine,优化账户、订单、深度、价格和K线推送的异常处理逻辑,确保在推送失败时记录错误信息以便于调试。

This commit is contained in:
discountry
2025-09-24 04:03:15 +08:00
parent dc6f9ee72e
commit 6f05ee0da3
3 changed files with 275 additions and 122 deletions
+92 -41
View File
@@ -109,53 +109,94 @@ export class MakerEngine {
} }
private bootstrap(): void { private bootstrap(): void {
this.exchange.watchAccount((snapshot) => { try {
this.accountSnapshot = snapshot; this.exchange.watchAccount((snapshot) => {
const totalUnrealized = Number(snapshot.totalUnrealizedProfit ?? "0"); try {
if (Number.isFinite(totalUnrealized)) { this.accountSnapshot = snapshot;
this.accountUnrealized = totalUnrealized; const totalUnrealized = Number(snapshot.totalUnrealizedProfit ?? "0");
} if (Number.isFinite(totalUnrealized)) {
const position = getPosition(snapshot, this.config.symbol); this.accountUnrealized = totalUnrealized;
this.updateSessionVolume(position); }
this.emitUpdate(); const position = getPosition(snapshot, this.config.symbol);
}); this.updateSessionVolume(position);
this.emitUpdate();
this.exchange.watchOrders((orders) => { } catch (err) {
this.syncLocksWithOrders(orders); this.tradeLog.push("error", `账户推送处理异常: ${String(err)}`);
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; } catch (err) {
this.emitUpdate(); this.tradeLog.push("error", `订阅账户失败: ${String(err)}`);
}); }
this.exchange.watchDepth(this.config.symbol, (depth) => { try {
this.depthSnapshot = depth; this.exchange.watchOrders((orders) => {
this.emitUpdate(); 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) => { try {
this.tickerSnapshot = ticker; this.exchange.watchDepth(this.config.symbol, (depth) => {
this.emitUpdate(); 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 // Maker strategy does not consume klines, but subscribe to keep parity with other modules
this.exchange.watchKlines(this.config.symbol, "1m", () => { try {
/* no-op */ 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) => { Object.keys(this.pending).forEach((type) => {
const pendingId = this.pending[type]; const pendingId = this.pending[type];
if (!pendingId) return; 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")) { if (!match || (match.status && match.status !== "NEW" && match.status !== "PARTIALLY_FILLED")) {
unlockOperating(this.locks, this.timers, this.pending, type); unlockOperating(this.locks, this.timers, this.pending, type);
} }
@@ -407,10 +448,20 @@ export class MakerEngine {
} }
private emitUpdate(): void { private emitUpdate(): void {
const snapshot = this.buildSnapshot(); try {
const handlers = this.listeners.get("update"); const snapshot = this.buildSnapshot();
if (handlers) { const handlers = this.listeners.get("update");
handlers.forEach((handler) => handler(snapshot)); if (handlers) {
handlers.forEach((handler) => {
try {
handler(snapshot);
} catch (err) {
this.tradeLog.push("error", `更新回调处理异常: ${String(err)}`);
}
});
}
} catch (err) {
this.tradeLog.push("error", `快照或更新分发异常: ${String(err)}`);
} }
} }
+92 -41
View File
@@ -108,52 +108,93 @@ export class OffsetMakerEngine {
} }
private bootstrap(): void { private bootstrap(): void {
this.exchange.watchAccount((snapshot) => { try {
this.accountSnapshot = snapshot; this.exchange.watchAccount((snapshot) => {
const totalUnrealized = Number(snapshot.totalUnrealizedProfit ?? "0"); try {
if (Number.isFinite(totalUnrealized)) { this.accountSnapshot = snapshot;
this.accountUnrealized = totalUnrealized; const totalUnrealized = Number(snapshot.totalUnrealizedProfit ?? "0");
} if (Number.isFinite(totalUnrealized)) {
const position = getPosition(snapshot, this.config.symbol); this.accountUnrealized = totalUnrealized;
this.updateSessionVolume(position); }
this.emitUpdate(); const position = getPosition(snapshot, this.config.symbol);
}); this.updateSessionVolume(position);
this.emitUpdate();
this.exchange.watchOrders((orders) => { } catch (err) {
this.syncLocksWithOrders(orders); this.tradeLog.push("error", `账户推送处理异常: ${String(err)}`);
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; } catch (err) {
this.emitUpdate(); this.tradeLog.push("error", `订阅账户失败: ${String(err)}`);
}); }
this.exchange.watchDepth(this.config.symbol, (depth) => { try {
this.depthSnapshot = depth; this.exchange.watchOrders((orders) => {
this.emitUpdate(); 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) => { try {
this.tickerSnapshot = ticker; this.exchange.watchDepth(this.config.symbol, (depth) => {
this.emitUpdate(); 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", () => { try {
/* no-op */ 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) => { Object.keys(this.pending).forEach((type) => {
const pendingId = this.pending[type]; const pendingId = this.pending[type];
if (!pendingId) return; 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")) { if (!match || (match.status && match.status !== "NEW" && match.status !== "PARTIALLY_FILLED")) {
unlockOperating(this.locks, this.timers, this.pending, type); unlockOperating(this.locks, this.timers, this.pending, type);
} }
@@ -499,10 +540,20 @@ export class OffsetMakerEngine {
} }
private emitUpdate(): void { private emitUpdate(): void {
const snapshot = this.buildSnapshot(); try {
const handlers = this.listeners.get("update"); const snapshot = this.buildSnapshot();
if (handlers) { const handlers = this.listeners.get("update");
handlers.forEach((handler) => handler(snapshot)); if (handlers) {
handlers.forEach((handler) => {
try {
handler(snapshot);
} catch (err) {
this.tradeLog.push("error", `更新回调处理异常: ${String(err)}`);
}
});
}
} catch (err) {
this.tradeLog.push("error", `快照或更新分发异常: ${String(err)}`);
} }
} }
+91 -40
View File
@@ -126,48 +126,89 @@ export class TrendEngine {
} }
private bootstrap(): void { private bootstrap(): void {
this.exchange.watchAccount((snapshot) => { try {
this.accountSnapshot = snapshot; this.exchange.watchAccount((snapshot) => {
const position = getPosition(snapshot, this.config.symbol); try {
this.updateSessionVolume(position); this.accountSnapshot = snapshot;
this.emitUpdate(); const position = getPosition(snapshot, this.config.symbol);
}); this.updateSessionVolume(position);
this.exchange.watchOrders((orders) => { this.emitUpdate();
this.synchronizeLocks(orders); } catch (err) {
this.openOrders = Array.isArray(orders) this.tradeLog.push("error", `账户推送处理异常: ${String(err)}`);
? 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) { } catch (err) {
this.cancelAllRequested = false; this.tradeLog.push("error", `订阅账户失败: ${String(err)}`);
} }
this.ordersSnapshotReady = true; try {
this.emitUpdate(); this.exchange.watchOrders((orders) => {
}); try {
this.exchange.watchDepth(this.config.symbol, (depth) => { this.synchronizeLocks(orders);
this.depthSnapshot = depth; this.openOrders = Array.isArray(orders)
this.emitUpdate(); ? orders.filter((order) => order.type !== "MARKET" && order.symbol === this.config.symbol)
}); : [];
this.exchange.watchTicker(this.config.symbol, (ticker) => { const currentIds = new Set(this.openOrders.map((order) => order.orderId));
this.tickerSnapshot = ticker; for (const id of Array.from(this.pendingCancelOrders)) {
this.emitUpdate(); if (!currentIds.has(id)) {
}); this.pendingCancelOrders.delete(id);
this.exchange.watchKlines(this.config.symbol, this.config.klineInterval, (klines) => { }
this.klineSnapshot = klines; }
this.emitUpdate(); 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) => { Object.keys(this.pending).forEach((type) => {
const pendingId = this.pending[type]; const pendingId = this.pending[type];
if (!pendingId) return; 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")) { if (!match || (match.status && match.status !== "NEW")) {
unlockOperating(this.locks, this.timers, this.pending, type); unlockOperating(this.locks, this.timers, this.pending, type);
} }
@@ -532,10 +573,20 @@ export class TrendEngine {
} }
private emitUpdate(): void { private emitUpdate(): void {
const snapshot = this.buildSnapshot(); try {
const handlers = this.listeners.get("update"); const snapshot = this.buildSnapshot();
if (handlers) { const handlers = this.listeners.get("update");
handlers.forEach((handler) => handler(snapshot)); if (handlers) {
handlers.forEach((handler) => {
try {
handler(snapshot);
} catch (err) {
this.tradeLog.push("error", `更新回调处理异常: ${String(err)}`);
}
});
}
} catch (err) {
this.tradeLog.push("error", `快照或更新分发异常: ${String(err)}`);
} }
} }