mirror of
https://github.com/discountry/ritmex-bot.git
synced 2026-09-11 17:28:08 +00:00
feat: 更新网格引擎,添加绝对持仓量跟踪逻辑,优化锚定价格选择和订单消失分类处理
This commit is contained in:
+43
-12
@@ -99,6 +99,7 @@ export class GridEngine {
|
|||||||
private sidesLocked = false;
|
private sidesLocked = false;
|
||||||
private startupCleaned = false;
|
private startupCleaned = false;
|
||||||
private initialCloseHandled = false;
|
private initialCloseHandled = false;
|
||||||
|
private lastAbsPositionAmt = 0;
|
||||||
|
|
||||||
private accountSnapshot: AsterAccountSnapshot | null = null;
|
private accountSnapshot: AsterAccountSnapshot | null = null;
|
||||||
private depthSnapshot: AsterDepth | null = null;
|
private depthSnapshot: AsterDepth | null = null;
|
||||||
@@ -206,6 +207,7 @@ export class GridEngine {
|
|||||||
(snapshot) => {
|
(snapshot) => {
|
||||||
this.accountSnapshot = snapshot;
|
this.accountSnapshot = snapshot;
|
||||||
this.position = getPosition(snapshot, this.config.symbol);
|
this.position = getPosition(snapshot, this.config.symbol);
|
||||||
|
this.lastAbsPositionAmt = Math.abs(this.position.positionAmt);
|
||||||
if (!this.feedArrived.account) {
|
if (!this.feedArrived.account) {
|
||||||
this.feedArrived.account = true;
|
this.feedArrived.account = true;
|
||||||
log("info", "账户快照已同步");
|
log("info", "账户快照已同步");
|
||||||
@@ -342,12 +344,12 @@ export class GridEngine {
|
|||||||
private tryLockSidesOnce(): void {
|
private tryLockSidesOnce(): void {
|
||||||
if (this.sidesLocked) return;
|
if (this.sidesLocked) return;
|
||||||
if (!this.feedStatus.ticker && !this.feedStatus.depth) return;
|
if (!this.feedStatus.ticker && !this.feedStatus.depth) return;
|
||||||
const reference = this.getReferencePrice();
|
const anchor = this.chooseAnchoringPrice();
|
||||||
if (!Number.isFinite(reference) || reference == null) return;
|
if (!Number.isFinite(anchor) || anchor == null) return;
|
||||||
const price = this.clampReferencePrice(Number(reference));
|
const price = this.clampReferencePrice(Number(anchor));
|
||||||
this.buildLevelMeta(price);
|
this.buildLevelMeta(price);
|
||||||
this.sidesLocked = true;
|
this.sidesLocked = true;
|
||||||
this.log("info", "已根据现价一次性划分买卖档位");
|
this.log("info", "已根据锚定价一次性划分买卖档位");
|
||||||
}
|
}
|
||||||
|
|
||||||
private clampReferencePrice(price: number): number {
|
private clampReferencePrice(price: number): number {
|
||||||
@@ -473,10 +475,16 @@ export class GridEngine {
|
|||||||
// For reduce-only disappearance, check mapping
|
// For reduce-only disappearance, check mapping
|
||||||
for (const [source, roKey] of this.reduceOnlyKeyBySourceLevel.entries()) {
|
for (const [source, roKey] of this.reduceOnlyKeyBySourceLevel.entries()) {
|
||||||
if (roKey === prevKey) {
|
if (roKey === prevKey) {
|
||||||
// close filled for this source
|
// Determine if disappearance was a fill or a manual cancel via position change
|
||||||
this.pendingLongLevels.delete(source);
|
const absNow = Math.abs(this.position.positionAmt);
|
||||||
this.pendingShortLevels.delete(source);
|
if (absNow + EPSILON < this.lastAbsPositionAmt) {
|
||||||
this.reduceOnlyKeyBySourceLevel.delete(source);
|
// treated as filled: clear pending mapping
|
||||||
|
this.pendingLongLevels.delete(source);
|
||||||
|
this.pendingShortLevels.delete(source);
|
||||||
|
this.reduceOnlyKeyBySourceLevel.delete(source);
|
||||||
|
} else {
|
||||||
|
// treated as canceled: keep pending so we re-arm close order
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -486,12 +494,18 @@ export class GridEngine {
|
|||||||
if (meta.side === "BUY") this.pendingLongLevels.add(meta.level);
|
if (meta.side === "BUY") this.pendingLongLevels.add(meta.level);
|
||||||
else this.pendingShortLevels.add(meta.level);
|
else this.pendingShortLevels.add(meta.level);
|
||||||
} else {
|
} else {
|
||||||
// reduce-only filled: clear exposure for mapped source level
|
// reduce-only disappeared: decide filled vs canceled by observing position delta
|
||||||
for (const [source, roKey] of this.reduceOnlyKeyBySourceLevel.entries()) {
|
for (const [source, roKey] of this.reduceOnlyKeyBySourceLevel.entries()) {
|
||||||
if (roKey === prevKey) {
|
if (roKey === prevKey) {
|
||||||
this.pendingLongLevels.delete(source);
|
const absNow = Math.abs(this.position.positionAmt);
|
||||||
this.pendingShortLevels.delete(source);
|
if (absNow + EPSILON < this.lastAbsPositionAmt) {
|
||||||
this.reduceOnlyKeyBySourceLevel.delete(source);
|
// filled: clear pending mapping
|
||||||
|
this.pendingLongLevels.delete(source);
|
||||||
|
this.pendingShortLevels.delete(source);
|
||||||
|
this.reduceOnlyKeyBySourceLevel.delete(source);
|
||||||
|
} else {
|
||||||
|
// canceled: keep pending mapping so desired will re-place close order
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -580,6 +594,8 @@ export class GridEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.lastUpdated = this.now();
|
this.lastUpdated = this.now();
|
||||||
|
// Update last observed absolute position amount for next disappearance classification
|
||||||
|
this.lastAbsPositionAmt = Math.abs(this.position.positionAmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
private findSourceForCloseTarget(targetLevel: number, side: "BUY" | "SELL"): number {
|
private findSourceForCloseTarget(targetLevel: number, side: "BUY" | "SELL"): number {
|
||||||
@@ -750,6 +766,21 @@ export class GridEngine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private chooseAnchoringPrice(): number | null {
|
||||||
|
const reference = this.getReferencePrice();
|
||||||
|
if (!Number.isFinite(reference) || reference == null) return null;
|
||||||
|
const ref = Number(reference);
|
||||||
|
const qty = this.position.positionAmt;
|
||||||
|
const entry = this.position.entryPrice;
|
||||||
|
const hasEntry = Number.isFinite(entry) && Math.abs(entry) > EPSILON;
|
||||||
|
if (!hasEntry || Math.abs(qty) <= EPSILON) return ref;
|
||||||
|
// If long and market below cost, anchor at entry to avoid shorting below cost
|
||||||
|
if (qty > 0 && ref < Number(entry) - EPSILON) return Number(entry);
|
||||||
|
// If short and market above cost, anchor at entry to avoid longing above cost
|
||||||
|
if (qty < 0 && ref > Number(entry) + EPSILON) return Number(entry);
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
|
||||||
// exposure summation removed in simplified flow
|
// exposure summation removed in simplified flow
|
||||||
|
|
||||||
private async cancelAllExistingOrdersOnStartup(): Promise<void> {
|
private async cancelAllExistingOrdersOnStartup(): Promise<void> {
|
||||||
|
|||||||
Reference in New Issue
Block a user