mirror of
https://github.com/discountry/ritmex-bot.git
synced 2026-09-11 17:28:08 +00:00
更新 MakerEngine 和 TrendEngine,添加待撤销订单管理逻辑;更新 MakerApp,调整累计成交量显示位置
This commit is contained in:
@@ -14,8 +14,10 @@
|
|||||||
- Windows:在开始菜单搜索 “PowerShell” 或 “Windows Terminal” 并打开。
|
- Windows:在开始菜单搜索 “PowerShell” 或 “Windows Terminal” 并打开。
|
||||||
- 使用 `cd` 切换到项目目录,例如:
|
- 使用 `cd` 切换到项目目录,例如:
|
||||||
```bash
|
```bash
|
||||||
cd ~/Desktop/ritmex-bot # macOS / Linux
|
# macOS / Linux
|
||||||
cd C:\Users\用户名\Desktop\ritmex-bot # Windows
|
cd ~/Desktop/ritmex-bot
|
||||||
|
# Windows
|
||||||
|
cd C:\Users\用户名\Desktop\ritmex-bot
|
||||||
```
|
```
|
||||||
3. **安装 [Bun](https://bun.com) ≥ 1.2**
|
3. **安装 [Bun](https://bun.com) ≥ 1.2**
|
||||||
- macOS / Linux:
|
- macOS / Linux:
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ export class MakerEngine {
|
|||||||
private readonly locks: OrderLockMap = {};
|
private readonly locks: OrderLockMap = {};
|
||||||
private readonly timers: OrderTimerMap = {};
|
private readonly timers: OrderTimerMap = {};
|
||||||
private readonly pending: OrderPendingMap = {};
|
private readonly pending: OrderPendingMap = {};
|
||||||
|
private readonly pendingCancelOrders = new Set<number>();
|
||||||
|
|
||||||
private readonly tradeLog: ReturnType<typeof createTradeLog>;
|
private readonly tradeLog: ReturnType<typeof createTradeLog>;
|
||||||
private readonly listeners = new Map<MakerEvent, Set<MakerListener>>();
|
private readonly listeners = new Map<MakerEvent, Set<MakerListener>>();
|
||||||
@@ -121,6 +122,12 @@ export class MakerEngine {
|
|||||||
this.openOrders = Array.isArray(orders)
|
this.openOrders = Array.isArray(orders)
|
||||||
? orders.filter((order) => order.type !== "MARKET" && order.symbol === this.config.symbol)
|
? 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.emitUpdate();
|
this.emitUpdate();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -214,6 +221,9 @@ export class MakerEngine {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const reduceOnly = order.reduceOnly === true;
|
const reduceOnly = order.reduceOnly === true;
|
||||||
|
if (this.pendingCancelOrders.has(order.orderId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const matchedIndex = targets.findIndex((target, index) => {
|
const matchedIndex = targets.findIndex((target, index) => {
|
||||||
if (!unmatched.has(index)) return false;
|
if (!unmatched.has(index)) return false;
|
||||||
if (target.side !== order.side) return false;
|
if (target.side !== order.side) return false;
|
||||||
@@ -228,14 +238,17 @@ export class MakerEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const order of toCancel) {
|
for (const order of toCancel) {
|
||||||
|
this.pendingCancelOrders.add(order.orderId);
|
||||||
try {
|
try {
|
||||||
await this.exchange.cancelOrder({ symbol: this.config.symbol, orderId: order.orderId });
|
await this.exchange.cancelOrder({ symbol: this.config.symbol, orderId: order.orderId });
|
||||||
this.tradeLog.push("order", `撤销不匹配订单 ${order.side} @ ${order.price} reduceOnly=${order.reduceOnly}`);
|
this.tradeLog.push("order", `撤销不匹配订单 ${order.side} @ ${order.price} reduceOnly=${order.reduceOnly}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (isUnknownOrderError(error)) {
|
if (isUnknownOrderError(error)) {
|
||||||
this.tradeLog.push("order", "撤销时发现订单已被成交/取消,忽略");
|
this.tradeLog.push("order", "撤销时发现订单已被成交/取消,忽略");
|
||||||
|
this.pendingCancelOrders.delete(order.orderId);
|
||||||
} else {
|
} else {
|
||||||
this.tradeLog.push("error", `撤销订单失败: ${String(error)}`);
|
this.tradeLog.push("error", `撤销订单失败: ${String(error)}`);
|
||||||
|
this.pendingCancelOrders.delete(order.orderId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -303,13 +316,17 @@ export class MakerEngine {
|
|||||||
private async flushOrders(): Promise<void> {
|
private async flushOrders(): Promise<void> {
|
||||||
if (!this.openOrders.length) return;
|
if (!this.openOrders.length) return;
|
||||||
for (const order of this.openOrders) {
|
for (const order of this.openOrders) {
|
||||||
|
if (this.pendingCancelOrders.has(order.orderId)) continue;
|
||||||
|
this.pendingCancelOrders.add(order.orderId);
|
||||||
try {
|
try {
|
||||||
await this.exchange.cancelOrder({ symbol: this.config.symbol, orderId: order.orderId });
|
await this.exchange.cancelOrder({ symbol: this.config.symbol, orderId: order.orderId });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (isUnknownOrderError(error)) {
|
if (isUnknownOrderError(error)) {
|
||||||
this.tradeLog.push("order", "订单已不存在,撤销跳过");
|
this.tradeLog.push("order", "订单已不存在,撤销跳过");
|
||||||
|
this.pendingCancelOrders.delete(order.orderId);
|
||||||
} else {
|
} else {
|
||||||
this.tradeLog.push("error", `撤销订单失败: ${String(error)}`);
|
this.tradeLog.push("error", `撤销订单失败: ${String(error)}`);
|
||||||
|
this.pendingCancelOrders.delete(order.orderId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,6 +78,8 @@ export class TrendEngine {
|
|||||||
private sessionQuoteVolume = 0;
|
private sessionQuoteVolume = 0;
|
||||||
private prevPositionAmt = 0;
|
private prevPositionAmt = 0;
|
||||||
private initializedPosition = false;
|
private initializedPosition = false;
|
||||||
|
private cancelAllRequested = false;
|
||||||
|
private readonly pendingCancelOrders = new Set<number>();
|
||||||
|
|
||||||
private readonly listeners = new Map<TrendEngineEvent, Set<TrendEngineListener>>();
|
private readonly listeners = new Map<TrendEngineEvent, Set<TrendEngineListener>>();
|
||||||
|
|
||||||
@@ -131,6 +133,15 @@ export class TrendEngine {
|
|||||||
this.openOrders = Array.isArray(orders)
|
this.openOrders = Array.isArray(orders)
|
||||||
? orders.filter((order) => order.type !== "MARKET" && order.symbol === this.config.symbol)
|
? 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) {
|
||||||
|
this.cancelAllRequested = false;
|
||||||
|
}
|
||||||
this.emitUpdate();
|
this.emitUpdate();
|
||||||
});
|
});
|
||||||
this.exchange.watchDepth(this.config.symbol, (depth) => {
|
this.exchange.watchDepth(this.config.symbol, (depth) => {
|
||||||
@@ -210,14 +221,17 @@ export class TrendEngine {
|
|||||||
this.lastPrice = currentPrice;
|
this.lastPrice = currentPrice;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.openOrders.length > 0) {
|
if (this.openOrders.length > 0 && !this.cancelAllRequested) {
|
||||||
try {
|
try {
|
||||||
await this.exchange.cancelAllOrders({ symbol: this.config.symbol });
|
await this.exchange.cancelAllOrders({ symbol: this.config.symbol });
|
||||||
|
this.cancelAllRequested = true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (isUnknownOrderError(err)) {
|
if (isUnknownOrderError(err)) {
|
||||||
this.tradeLog.push("order", "撤单时部分订单已不存在,忽略");
|
this.tradeLog.push("order", "撤单时部分订单已不存在,忽略");
|
||||||
|
this.cancelAllRequested = true;
|
||||||
} else {
|
} else {
|
||||||
this.tradeLog.push("error", `撤销挂单失败: ${String(err)}`);
|
this.tradeLog.push("error", `撤销挂单失败: ${String(err)}`);
|
||||||
|
this.cancelAllRequested = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -315,6 +329,7 @@ export class TrendEngine {
|
|||||||
const orderIdList = this.openOrders.map((order) => order.orderId);
|
const orderIdList = this.openOrders.map((order) => order.orderId);
|
||||||
try {
|
try {
|
||||||
await this.exchange.cancelOrders({ symbol: this.config.symbol, orderIdList });
|
await this.exchange.cancelOrders({ symbol: this.config.symbol, orderIdList });
|
||||||
|
orderIdList.forEach((id) => this.pendingCancelOrders.add(id));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (isUnknownOrderError(err)) {
|
if (isUnknownOrderError(err)) {
|
||||||
this.tradeLog.push("order", "止损前撤单发现订单已不存在");
|
this.tradeLog.push("order", "止损前撤单发现订单已不存在");
|
||||||
|
|||||||
+3
-3
@@ -137,9 +137,6 @@ export function MakerApp({ onExit }: MakerAppProps) {
|
|||||||
<Text>
|
<Text>
|
||||||
浮动盈亏: {formatNumber(snapshot.pnl, 4)} USDT | 账户未实现盈亏: {formatNumber(snapshot.accountUnrealized, 4)} USDT
|
浮动盈亏: {formatNumber(snapshot.pnl, 4)} USDT | 账户未实现盈亏: {formatNumber(snapshot.accountUnrealized, 4)} USDT
|
||||||
</Text>
|
</Text>
|
||||||
<Text>
|
|
||||||
累计成交量: {formatNumber(snapshot.sessionVolume, 2)} USDT
|
|
||||||
</Text>
|
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<Text color="gray">当前无持仓</Text>
|
<Text color="gray">当前无持仓</Text>
|
||||||
@@ -152,6 +149,9 @@ export function MakerApp({ onExit }: MakerAppProps) {
|
|||||||
) : (
|
) : (
|
||||||
<Text color="gray">暂无目标挂单</Text>
|
<Text color="gray">暂无目标挂单</Text>
|
||||||
)}
|
)}
|
||||||
|
<Text>
|
||||||
|
累计成交量: {formatNumber(snapshot.sessionVolume, 2)} USDT
|
||||||
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user