feat: 更新网格引擎,添加现有减仓订单恢复逻辑,优化订单曝光管理和状态同步功能

This commit is contained in:
discountry
2025-10-07 04:41:32 +08:00
parent c6f279c51b
commit 66e1b3e6f5
2 changed files with 252 additions and 6 deletions
+61 -4
View File
@@ -22,6 +22,7 @@ class StubAdapter implements ExchangeAdapter {
public createdOrders: CreateOrderParams[] = [];
public marketOrders: CreateOrderParams[] = [];
public cancelAllCount = 0;
public cancelledOrders: Array<number | string> = [];
supportsTrailingStops(): boolean {
return false;
@@ -91,12 +92,12 @@ class StubAdapter implements ExchangeAdapter {
return order;
}
async cancelOrder(): Promise<void> {
// no-op
async cancelOrder(params: { symbol: string; orderId: number | string }): Promise<void> {
this.cancelledOrders.push(params.orderId);
}
async cancelOrders(): Promise<void> {
// no-op
async cancelOrders(params: { symbol: string; orderIdList: Array<number | string> }): Promise<void> {
this.cancelledOrders.push(...params.orderIdList);
}
async cancelAllOrders(): Promise<void> {
@@ -289,6 +290,62 @@ describe("GridEngine", () => {
engine.stop();
});
it("restores exposures from existing reduce-only orders on restart", async () => {
const adapter = new StubAdapter();
const engine = new GridEngine(baseConfig, adapter, { now: () => 0 });
adapter.emitAccount(createAccountSnapshot(baseConfig.symbol, baseConfig.orderSize * 2));
const reduceOrder: AsterOrder = {
orderId: "existing-reduce",
clientOrderId: "existing-reduce",
symbol: baseConfig.symbol,
side: "SELL",
type: "LIMIT",
status: "NEW",
price: baseConfig.upperPrice.toFixed(1),
origQty: (baseConfig.orderSize * 2).toString(),
executedQty: "0",
stopPrice: "0",
time: Date.now(),
updateTime: Date.now(),
reduceOnly: true,
closePosition: false,
};
adapter.emitOrders([reduceOrder]);
adapter.emitTicker({
symbol: baseConfig.symbol,
lastPrice: "150",
openPrice: "150",
highPrice: "150",
lowPrice: "150",
volume: "0",
quoteVolume: "0",
});
await (engine as any).syncGrid(150);
const longExposure: Map<number, number> = (engine as any).longExposure;
const buyIndices: number[] = (engine as any).buyLevelIndices;
const totalExposure = [...longExposure.values()].reduce((acc, qty) => acc + qty, 0);
expect(totalExposure).toBeCloseTo(baseConfig.orderSize * 2, 6);
expect(longExposure.get(buyIndices.slice(-1)[0]!)).toBeCloseTo(baseConfig.orderSize, 6);
expect(longExposure.get(buyIndices[0]!)).toBeCloseTo(baseConfig.orderSize, 6);
const snapshot = engine.getSnapshot();
const reduceDesired = snapshot.desiredOrders.find(
(order) => order.reduceOnly && order.side === "SELL"
);
expect(reduceDesired).toBeTruthy();
expect(reduceDesired!.amount).toBeCloseTo(baseConfig.orderSize * 2, 6);
expect(Number(reduceDesired!.price)).toBeCloseTo(baseConfig.upperPrice, 6);
expect(adapter.cancelledOrders).toHaveLength(0);
engine.stop();
});
it("halts the grid and closes positions when stop loss triggers", async () => {
const adapter = new StubAdapter();
const engine = new GridEngine(baseConfig, adapter, { now: () => 0 });