mirror of
https://github.com/discountry/ritmex-bot.git
synced 2026-09-11 17:28:08 +00:00
Merge branch 'main' into release/grvt
This commit is contained in:
@@ -87,6 +87,13 @@ export class TrendEngine {
|
|||||||
private cancelAllRequested = false;
|
private cancelAllRequested = false;
|
||||||
private readonly pendingCancelOrders = new Set<string>();
|
private readonly pendingCancelOrders = new Set<string>();
|
||||||
private readonly rateLimit: RateLimitController;
|
private readonly rateLimit: RateLimitController;
|
||||||
|
private lastAccountPosition: PositionSnapshot = {
|
||||||
|
positionAmt: 0,
|
||||||
|
entryPrice: 0,
|
||||||
|
unrealizedProfit: 0,
|
||||||
|
markPrice: null,
|
||||||
|
};
|
||||||
|
private pendingRealized: { pnl: number; timestamp: number } | null = null;
|
||||||
|
|
||||||
// 控制入场频率:同一分钟内最多入场一次
|
// 控制入场频率:同一分钟内最多入场一次
|
||||||
private lastEntryMinute: number | null = null;
|
private lastEntryMinute: number | null = null;
|
||||||
@@ -151,6 +158,7 @@ export class TrendEngine {
|
|||||||
this.accountSnapshot = snapshot;
|
this.accountSnapshot = snapshot;
|
||||||
const position = getPosition(snapshot, this.config.symbol);
|
const position = getPosition(snapshot, this.config.symbol);
|
||||||
this.updateSessionVolume(position);
|
this.updateSessionVolume(position);
|
||||||
|
this.trackPositionLifecycle(position, this.getReferencePrice());
|
||||||
this.emitUpdate();
|
this.emitUpdate();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.tradeLog.push("error", `账户推送处理异常: ${extractMessage(err)}`);
|
this.tradeLog.push("error", `账户推送处理异常: ${extractMessage(err)}`);
|
||||||
@@ -280,12 +288,12 @@ export class TrendEngine {
|
|||||||
} else {
|
} else {
|
||||||
const result = await this.handlePositionManagement(position, price);
|
const result = await this.handlePositionManagement(position, price);
|
||||||
if (result.closed) {
|
if (result.closed) {
|
||||||
this.totalTrades += 1;
|
this.pendingRealized = { pnl: result.pnl, timestamp: Date.now() };
|
||||||
this.totalProfit += result.pnl;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updateSessionVolume(position);
|
this.updateSessionVolume(position);
|
||||||
|
this.trackPositionLifecycle(position, price);
|
||||||
this.lastSma30 = sma30;
|
this.lastSma30 = sma30;
|
||||||
this.lastPrice = price;
|
this.lastPrice = price;
|
||||||
this.emitUpdate();
|
this.emitUpdate();
|
||||||
@@ -317,8 +325,7 @@ export class TrendEngine {
|
|||||||
if (!Number.isFinite(price) || price == null) return;
|
if (!Number.isFinite(price) || price == null) return;
|
||||||
const result = await this.handlePositionManagement(position, Number(price));
|
const result = await this.handlePositionManagement(position, Number(price));
|
||||||
if (result.closed) {
|
if (result.closed) {
|
||||||
this.totalTrades += 1;
|
this.pendingRealized = { pnl: result.pnl, timestamp: Date.now() };
|
||||||
this.totalProfit += result.pnl;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -883,4 +890,46 @@ export class TrendEngine {
|
|||||||
return getMidOrLast(this.depthSnapshot, this.tickerSnapshot) ?? (this.lastPrice != null && Number.isFinite(this.lastPrice) ? this.lastPrice : null);
|
return getMidOrLast(this.depthSnapshot, this.tickerSnapshot) ?? (this.lastPrice != null && Number.isFinite(this.lastPrice) ? this.lastPrice : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private trackPositionLifecycle(position: PositionSnapshot, referencePrice: number | null): void {
|
||||||
|
const prev = this.lastAccountPosition;
|
||||||
|
const prevExposure = Math.abs(prev.positionAmt) > 1e-5;
|
||||||
|
const currentExposure = Math.abs(position.positionAmt) > 1e-5;
|
||||||
|
const signChanged =
|
||||||
|
prevExposure && currentExposure && Math.sign(prev.positionAmt) !== Math.sign(position.positionAmt);
|
||||||
|
|
||||||
|
if (prevExposure && (!currentExposure || signChanged)) {
|
||||||
|
let realized: number | null = this.pendingRealized?.pnl ?? null;
|
||||||
|
if (!Number.isFinite(realized)) {
|
||||||
|
realized = this.estimateRealizedPnl(prev, referencePrice);
|
||||||
|
}
|
||||||
|
if (Number.isFinite(realized)) {
|
||||||
|
this.totalTrades += 1;
|
||||||
|
this.totalProfit += realized ?? 0;
|
||||||
|
}
|
||||||
|
this.pendingRealized = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!prevExposure && currentExposure) {
|
||||||
|
this.pendingRealized = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.lastAccountPosition = {
|
||||||
|
positionAmt: position.positionAmt,
|
||||||
|
entryPrice: position.entryPrice,
|
||||||
|
unrealizedProfit: position.unrealizedProfit,
|
||||||
|
markPrice: position.markPrice,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private estimateRealizedPnl(position: PositionSnapshot, referencePrice: number | null): number {
|
||||||
|
const fallbackPrice =
|
||||||
|
referencePrice ??
|
||||||
|
this.getReferencePrice() ??
|
||||||
|
(this.lastPrice != null && Number.isFinite(this.lastPrice) ? this.lastPrice : position.entryPrice);
|
||||||
|
if (!Number.isFinite(fallbackPrice)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return computePositionPnl(position, fallbackPrice, fallbackPrice);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user