refactor: enhance order handling and tracking in GridEngine

- Introduced a clientOrderId system for better order identification.
- Updated order creation logic to ensure unique clientOrderIds.
- Improved order cancellation methods to maintain accurate current orders.
- Added tests for new clientOrderId functionality and level state tracking.
- Ensured that desired orders have an intent field set for clarity.
- Enhanced snapshot functionality to include level states for grid lines.
This commit is contained in:
discountry
2026-04-06 20:05:03 +08:00
parent d925fd93ee
commit 345ad11ba3
10 changed files with 1067 additions and 550 deletions
+2
View File
@@ -134,6 +134,7 @@ type PlaceOrderOptions = {
skipDedupe?: boolean;
slPrice?: number;
tpPrice?: number;
clientOrderId?: string;
};
export async function placeOrder(
@@ -180,6 +181,7 @@ export async function placeOrder(
closePosition,
slPrice: opts?.slPrice,
tpPrice: opts?.tpPrice,
clientOrderId: opts?.clientOrderId,
});
pendings[type] = String(order.orderId);
log("order", `挂限价单: ${side} @ ${priceNum} 数量 ${quantity} reduceOnly=${reduceOnly}${opts?.slPrice ? ` sl=${opts.slPrice}` : ""}`);
+1
View File
@@ -815,6 +815,7 @@ export class AsterRestClient {
if (params.activationPrice !== undefined) payload.activationPrice = params.activationPrice;
if (params.callbackRate !== undefined) payload.callbackRate = params.callbackRate;
if (params.quantity !== undefined) payload.quantity = Math.abs(params.quantity);
if (params.clientOrderId !== undefined) payload.newClientOrderId = params.clientOrderId;
// Aster rejects reduceOnly/closePosition for certain order types (e.g. STOP/TRAILING).
// Keep the behavior exchange-specific by stripping them here for Aster.
+3
View File
@@ -487,6 +487,9 @@ export class BinanceGateway {
if (params.callbackRate != null) {
extra.callbackRate = params.callbackRate;
}
if (params.clientOrderId != null) {
extra.newClientOrderId = params.clientOrderId;
}
if (market.kind === "perp") {
if (params.reduceOnly != null) {
+2 -2
View File
@@ -130,10 +130,10 @@ export class DryRunExchangeAdapter implements ExchangeAdapter {
function createSyntheticOrder(params: CreateOrderParams, counter: number): Order {
const now = Date.now();
const orderId = `dry-run-${now}-${counter}`;
const orderId = params.clientOrderId ?? `dry-run-${now}-${counter}`;
return {
orderId,
clientOrderId: orderId,
clientOrderId: params.clientOrderId ?? orderId,
symbol: params.symbol,
side: params.side,
type: params.type,
+3
View File
@@ -45,6 +45,9 @@ function applyCommonFields(params: CreateOrderParams, intent: BaseOrderIntent):
if (intent.closePosition !== undefined) {
params.closePosition = toStringBoolean(intent.closePosition);
}
if (intent.clientOrderId !== undefined) {
params.clientOrderId = intent.clientOrderId;
}
return params;
}
+1
View File
@@ -9,6 +9,7 @@ export interface BaseOrderIntent {
reduceOnly?: boolean;
closePosition?: boolean;
timeInForce?: TimeInForce | "GTX";
clientOrderId?: string;
}
export interface LimitOrderIntent extends BaseOrderIntent {
+1
View File
@@ -27,6 +27,7 @@ export interface CreateOrderParams {
triggerType?: "UNSPECIFIED" | "TAKE_PROFIT" | "STOP_LOSS";
slPrice?: number;
tpPrice?: number;
clientOrderId?: string;
}
export interface AccountPosition {
+15 -2
View File
@@ -5,6 +5,19 @@ import type { GridDirection } from "../../config";
const DATA_DIR = process.env.GRID_DATA_DIR?.trim() || path.resolve("data");
const GRID_FILE = path.resolve(DATA_DIR, "grid-record.json");
/** State of a single grid level */
export type LevelState = "idle" | "filled" | "exit_placed";
export interface StoredLevelInfo {
state: LevelState;
/** The grid level index where ENTRY was filled */
sourceLevel: number;
/** The grid level index where EXIT is targeted (closeTarget) */
targetLevel: number | null;
/** The orderId of the EXIT order on exchange (if exit_placed) */
exitOrderId?: string;
}
export interface StoredGridState {
symbol: string;
lowerPrice: number;
@@ -13,8 +26,8 @@ export interface StoredGridState {
orderSize: number;
maxPositionSize: number;
direction: GridDirection;
longExposure: Record<string, number>;
shortExposure: Record<string, number>;
/** Per-level state: key is level index string */
levels: Record<string, StoredLevelInfo>;
updatedAt: number;
}
File diff suppressed because it is too large Load Diff