mirror of
https://github.com/discountry/ritmex-bot.git
synced 2026-09-09 16:28:06 +00:00
添加全局错误处理机制,优化运行时错误日志记录,提升应用稳定性和错误追踪能力。
This commit is contained in:
@@ -7,6 +7,7 @@ import type {
|
||||
TickerListener,
|
||||
} from "./adapter";
|
||||
import type { AsterOrder, CreateOrderParams, AsterDepth, AsterTicker, AsterKline } from "./types";
|
||||
import { extractMessage } from "../utils/errors";
|
||||
import { AsterGateway } from "./aster/client";
|
||||
|
||||
export interface AsterCredentials {
|
||||
@@ -20,71 +21,127 @@ export class AsterExchangeAdapter implements ExchangeAdapter {
|
||||
private readonly gateway: AsterGateway;
|
||||
private readonly symbol: string;
|
||||
private initPromise: Promise<void> | null = null;
|
||||
private lastInitErrorAt = 0;
|
||||
private readonly initContexts = new Set<string>();
|
||||
private retryTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
private retryDelayMs = 3000;
|
||||
|
||||
constructor(credentials: AsterCredentials = {}) {
|
||||
this.gateway = new AsterGateway({ apiKey: credentials.apiKey, apiSecret: credentials.apiSecret });
|
||||
this.symbol = (credentials.symbol ?? process.env.TRADE_SYMBOL ?? "BTCUSDT").toUpperCase();
|
||||
}
|
||||
|
||||
private ensureInitialized(): Promise<void> {
|
||||
private safeInvoke<T extends (...args: any[]) => void>(context: string, cb: T): T {
|
||||
const wrapped = ((...args: any[]) => {
|
||||
try {
|
||||
cb(...args);
|
||||
} catch (error) {
|
||||
console.error(`[AsterExchangeAdapter] ${context} handler failed: ${extractMessage(error)}`);
|
||||
}
|
||||
}) as T;
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
private ensureInitialized(context?: string): Promise<void> {
|
||||
if (!this.initPromise) {
|
||||
this.initPromise = this.gateway.ensureInitialized(this.symbol);
|
||||
this.initContexts.clear();
|
||||
this.initPromise = this.gateway.ensureInitialized(this.symbol).then((value) => {
|
||||
this.clearRetry();
|
||||
return value;
|
||||
}).catch((error) => {
|
||||
this.handleInitError("initialize", error);
|
||||
this.initPromise = null;
|
||||
this.scheduleRetry();
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
if (context && !this.initContexts.has(context)) {
|
||||
this.initContexts.add(context);
|
||||
this.initPromise.catch((error) => {
|
||||
this.handleInitError(context, error);
|
||||
this.scheduleRetry();
|
||||
});
|
||||
}
|
||||
return this.initPromise;
|
||||
}
|
||||
|
||||
private scheduleRetry(): void {
|
||||
if (this.retryTimer) return;
|
||||
this.retryTimer = setTimeout(() => {
|
||||
this.retryTimer = null;
|
||||
if (this.initPromise) return;
|
||||
this.retryDelayMs = Math.min(this.retryDelayMs * 2, 60_000);
|
||||
void this.ensureInitialized("retry");
|
||||
}, this.retryDelayMs);
|
||||
}
|
||||
|
||||
private clearRetry(): void {
|
||||
if (this.retryTimer) {
|
||||
clearTimeout(this.retryTimer);
|
||||
this.retryTimer = null;
|
||||
}
|
||||
this.retryDelayMs = 3000;
|
||||
}
|
||||
|
||||
private handleInitError(context: string, error: unknown): void {
|
||||
const now = Date.now();
|
||||
if (now - this.lastInitErrorAt < 5000) return;
|
||||
this.lastInitErrorAt = now;
|
||||
console.error(`[AsterExchangeAdapter] ${context} failed`, error);
|
||||
}
|
||||
|
||||
watchAccount(cb: AccountListener): void {
|
||||
void this.ensureInitialized();
|
||||
this.gateway.onAccount((snapshot) => {
|
||||
void this.ensureInitialized("watchAccount");
|
||||
this.gateway.onAccount(this.safeInvoke("watchAccount", (snapshot) => {
|
||||
cb(snapshot);
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
watchOrders(cb: OrderListener): void {
|
||||
void this.ensureInitialized();
|
||||
this.gateway.onOrders((orders) => {
|
||||
void this.ensureInitialized("watchOrders");
|
||||
this.gateway.onOrders(this.safeInvoke("watchOrders", (orders) => {
|
||||
cb(orders);
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
watchDepth(symbol: string, cb: DepthListener): void {
|
||||
void this.ensureInitialized();
|
||||
this.gateway.onDepth(symbol, (depth: AsterDepth) => {
|
||||
void this.ensureInitialized("watchDepth");
|
||||
this.gateway.onDepth(symbol, this.safeInvoke("watchDepth", (depth: AsterDepth) => {
|
||||
cb(depth);
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
watchTicker(symbol: string, cb: TickerListener): void {
|
||||
void this.ensureInitialized();
|
||||
this.gateway.onTicker(symbol, (ticker: AsterTicker) => {
|
||||
void this.ensureInitialized("watchTicker");
|
||||
this.gateway.onTicker(symbol, this.safeInvoke("watchTicker", (ticker: AsterTicker) => {
|
||||
cb(ticker);
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
watchKlines(symbol: string, interval: string, cb: KlineListener): void {
|
||||
void this.ensureInitialized();
|
||||
this.gateway.onKlines(symbol, interval, (klines: AsterKline[]) => {
|
||||
void this.ensureInitialized("watchKlines");
|
||||
this.gateway.onKlines(symbol, interval, this.safeInvoke("watchKlines", (klines: AsterKline[]) => {
|
||||
cb(klines);
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
async createOrder(params: CreateOrderParams): Promise<AsterOrder> {
|
||||
await this.ensureInitialized();
|
||||
await this.ensureInitialized("createOrder");
|
||||
return this.gateway.createOrder(params);
|
||||
}
|
||||
|
||||
async cancelOrder(params: { symbol: string; orderId: number | string }): Promise<void> {
|
||||
await this.ensureInitialized();
|
||||
await this.ensureInitialized("cancelOrder");
|
||||
await this.gateway.cancelOrder({ symbol: params.symbol, orderId: Number(params.orderId) });
|
||||
}
|
||||
|
||||
async cancelOrders(params: { symbol: string; orderIdList: Array<number | string> }): Promise<void> {
|
||||
await this.ensureInitialized();
|
||||
await this.ensureInitialized("cancelOrders");
|
||||
await this.gateway.cancelOrders({ symbol: params.symbol, orderIdList: params.orderIdList });
|
||||
}
|
||||
|
||||
async cancelAllOrders(params: { symbol: string }): Promise<void> {
|
||||
await this.ensureInitialized();
|
||||
await this.ensureInitialized("cancelAllOrders");
|
||||
await this.gateway.cancelAllOrders(params);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user