mirror of
https://github.com/discountry/ritmex-bot.git
synced 2026-09-11 09:18:08 +00:00
更新 MakerEngine、OffsetMakerEngine 和 TrendEngine,优化账户、订单、深度、价格和K线推送的异常处理逻辑,确保在推送失败时记录错误信息以便于调试。
This commit is contained in:
+92
-41
@@ -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)}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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)}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user