feat: 更新订单处理逻辑,将价格类型改为字符串以避免精度问题,并添加价格格式化函数

This commit is contained in:
discountry
2025-10-03 07:15:21 +08:00
parent 65aaf26879
commit d5c4b04746
7 changed files with 68 additions and 39 deletions
+18 -11
View File
@@ -7,7 +7,7 @@ import type {
AsterOrder,
AsterTicker,
} from "../exchanges/types";
import { roundDownToTick } from "../utils/math";
import { formatPriceToString } from "../utils/math";
import { createTradeLog, type TradeLogEntry } from "../logging/trade-log";
import { isUnknownOrderError, isRateLimitError } from "../utils/errors";
import { getPosition } from "../utils/strategy";
@@ -30,7 +30,7 @@ import { SessionVolumeTracker } from "./common/session-volume";
interface DesiredOrder {
side: "BUY" | "SELL";
price: number;
price: string; // 改为字符串价格
amount: number;
reduceOnly: boolean;
}
@@ -244,10 +244,12 @@ export class MakerEngine {
return;
}
const closeBidPrice = roundDownToTick(topBid, this.config.priceTick);
const closeAskPrice = roundDownToTick(topAsk, this.config.priceTick);
const bidPrice = roundDownToTick(topBid - this.config.bidOffset, this.config.priceTick);
const askPrice = roundDownToTick(topAsk + this.config.askOffset, this.config.priceTick);
// 直接使用orderbook价格,格式化为字符串避免精度问题
const priceDecimals = Math.max(0, Math.floor(Math.log10(1 / this.config.priceTick)));
const closeBidPrice = formatPriceToString(topBid, priceDecimals);
const closeAskPrice = formatPriceToString(topAsk, priceDecimals);
const bidPrice = formatPriceToString(topBid - this.config.bidOffset, priceDecimals);
const askPrice = formatPriceToString(topAsk + this.config.askOffset, priceDecimals);
const position = getPosition(this.accountSnapshot, this.config.symbol);
const absPosition = Math.abs(position.positionAmt);
const desired: DesiredOrder[] = [];
@@ -268,7 +270,7 @@ export class MakerEngine {
this.desiredOrders = desired;
this.sessionVolume.update(position, this.getReferencePrice());
await this.syncOrders(desired);
await this.checkRisk(position, closeBidPrice, closeAskPrice);
await this.checkRisk(position, Number(closeBidPrice), Number(closeAskPrice));
this.emitUpdate();
} catch (error) {
if (isRateLimitError(error)) {
@@ -291,9 +293,10 @@ export class MakerEngine {
if (Math.abs(position.positionAmt) < EPS) return;
const { topBid, topAsk } = getTopPrices(this.depthSnapshot);
if (topBid == null || topAsk == null) return;
const closeBidPrice = roundDownToTick(topBid, this.config.priceTick);
const closeAskPrice = roundDownToTick(topAsk, this.config.priceTick);
await this.checkRisk(position, closeBidPrice, closeAskPrice);
const priceDecimals = Math.max(0, Math.floor(Math.log10(1 / this.config.priceTick)));
const closeBidPrice = formatPriceToString(topBid, priceDecimals);
const closeAskPrice = formatPriceToString(topAsk, priceDecimals);
await this.checkRisk(position, Number(closeBidPrice), Number(closeAskPrice));
await this.flushOrders();
}
@@ -368,13 +371,17 @@ export class MakerEngine {
this.timers,
this.pending,
target.side,
target.price,
target.price, // 已经是字符串价格
target.amount,
(type, detail) => this.tradeLog.push(type, detail),
target.reduceOnly,
{
markPrice: getPosition(this.accountSnapshot, this.config.symbol).markPrice,
maxPct: this.config.maxCloseSlippagePct,
},
{
priceTick: this.config.priceTick,
qtyStep: 0.001, // 默认数量步长
}
);
} catch (error) {
+24 -10
View File
@@ -7,7 +7,7 @@ import type {
AsterOrder,
AsterTicker,
} from "../exchanges/types";
import { roundDownToTick } from "../utils/math";
import { formatPriceToString } from "../utils/math";
import { createTradeLog } from "../logging/trade-log";
import { isUnknownOrderError, isRateLimitError } from "../utils/errors";
import { getPosition } from "../utils/strategy";
@@ -32,7 +32,7 @@ import { SessionVolumeTracker } from "./common/session-volume";
interface DesiredOrder {
side: "BUY" | "SELL";
price: number;
price: string; // 改为字符串价格
amount: number;
reduceOnly: boolean;
}
@@ -236,6 +236,7 @@ export class OffsetMakerEngine {
return;
}
// 确保使用最新的深度数据
const depth = this.depthSnapshot!;
const { topBid, topAsk } = getTopPrices(depth);
if (topBid == null || topAsk == null) {
@@ -257,10 +258,18 @@ export class OffsetMakerEngine {
return;
}
const closeBidPrice = roundDownToTick(topBid!, this.config.priceTick);
const closeAskPrice = roundDownToTick(topAsk!, this.config.priceTick);
const bidPrice = roundDownToTick(topBid! - this.config.bidOffset, this.config.priceTick);
const askPrice = roundDownToTick(topAsk! + this.config.askOffset, this.config.priceTick);
// 在计算挂单价格前,重新获取最新的深度数据以确保价格同步
const latestDepth = this.depthSnapshot!;
const { topBid: latestBid, topAsk: latestAsk } = getTopPrices(latestDepth);
const finalBid = latestBid ?? topBid!;
const finalAsk = latestAsk ?? topAsk!;
// 直接使用orderbook价格,格式化为字符串避免精度问题
const priceDecimals = Math.max(0, Math.floor(Math.log10(1 / this.config.priceTick)));
const closeBidPrice = formatPriceToString(finalBid, priceDecimals);
const closeAskPrice = formatPriceToString(finalAsk, priceDecimals);
const bidPrice = formatPriceToString(finalBid - this.config.bidOffset, priceDecimals);
const askPrice = formatPriceToString(finalAsk + this.config.askOffset, priceDecimals);
const absPosition = Math.abs(position.positionAmt);
const desired: DesiredOrder[] = [];
const canEnter = !this.rateLimit.shouldBlockEntries();
@@ -282,7 +291,7 @@ export class OffsetMakerEngine {
this.desiredOrders = desired;
this.sessionVolume.update(position, this.getReferencePrice());
await this.syncOrders(desired);
await this.checkRisk(position, closeBidPrice, closeAskPrice);
await this.checkRisk(position, Number(closeBidPrice), Number(closeAskPrice));
this.emitUpdate();
} catch (error) {
if (isRateLimitError(error)) {
@@ -307,8 +316,9 @@ export class OffsetMakerEngine {
const absPosition = Math.abs(position.positionAmt);
const side: "BUY" | "SELL" = position.positionAmt > 0 ? "SELL" : "BUY";
const { topBid, topAsk } = getTopPrices(this.depthSnapshot);
const closeBidPrice = topBid != null ? roundDownToTick(topBid, this.config.priceTick) : null;
const closeAskPrice = topAsk != null ? roundDownToTick(topAsk, this.config.priceTick) : null;
const priceDecimals = Math.max(0, Math.floor(Math.log10(1 / this.config.priceTick)));
const closeBidPrice = topBid != null ? formatPriceToString(topBid, priceDecimals) : null;
const closeAskPrice = topAsk != null ? formatPriceToString(topAsk, priceDecimals) : null;
try {
await marketClose(
this.exchange,
@@ -471,13 +481,17 @@ export class OffsetMakerEngine {
this.timers,
this.pending,
target.side,
target.price,
target.price, // 已经是字符串价格
target.amount,
(type, detail) => this.tradeLog.push(type, detail),
target.reduceOnly,
{
markPrice: getPosition(this.accountSnapshot, this.config.symbol).markPrice,
maxPct: this.config.maxCloseSlippagePct,
},
{
priceTick: this.config.priceTick,
qtyStep: 0.001, // 默认数量步长
}
);
} catch (error) {
+6 -6
View File
@@ -27,7 +27,7 @@ import {
} from "../core/order-coordinator";
import type { OrderLockMap, OrderPendingMap, OrderTimerMap } from "../core/order-coordinator";
import { extractMessage, isUnknownOrderError } from "../utils/errors";
import { roundDownToTick } from "../utils/math";
import { formatPriceToString } from "../utils/math";
import { createTradeLog, type TradeLogEntry } from "../logging/trade-log";
import { decryptCopyright } from "../utils/copyright";
import { isRateLimitError } from "../utils/errors";
@@ -557,7 +557,7 @@ export class TrendEngine {
const rawTarget = direction === "long"
? position.entryPrice + steps * stepPx
: position.entryPrice - steps * stepPx;
let targetStop = roundDownToTick(rawTarget, this.config.priceTick);
let targetStop = Number(formatPriceToString(rawTarget, Math.max(0, Math.floor(Math.log10(1 / this.config.priceTick)))));
// 不允许下一次移动超过动态止盈订单的激活价
if (Number.isFinite(trailingActivate)) {
@@ -639,13 +639,13 @@ export class TrendEngine {
}
if (!currentStop) {
await this.tryPlaceStopLoss(stopSide, roundDownToTick(stopPrice, this.config.priceTick), price);
await this.tryPlaceStopLoss(stopSide, Number(formatPriceToString(stopPrice, Math.max(0, Math.floor(Math.log10(1 / this.config.priceTick))))), price);
}
if (!currentTrailing && this.exchange.supportsTrailingStops()) {
await this.tryPlaceTrailingStop(
stopSide,
roundDownToTick(activationPrice, this.config.priceTick),
Number(formatPriceToString(activationPrice, Math.max(0, Math.floor(Math.log10(1 / this.config.priceTick))))),
Math.abs(position.positionAmt)
);
}
@@ -815,7 +815,7 @@ export class TrendEngine {
{ priceTick: this.config.priceTick, qtyStep: this.config.qtyStep }
);
if (order) {
this.tradeLog.push("stop", `移动止损到 ${roundDownToTick(nextStopPrice, this.config.priceTick)}`);
this.tradeLog.push("stop", `移动止损到 ${formatPriceToString(nextStopPrice, Math.max(0, Math.floor(Math.log10(1 / this.config.priceTick))))}`);
}
} catch (err) {
this.tradeLog.push("error", `移动止损失败: ${String(err)}`);
@@ -846,7 +846,7 @@ export class TrendEngine {
{ priceTick: this.config.priceTick, qtyStep: this.config.qtyStep }
);
if (restored) {
this.tradeLog.push("order", `恢复原止损 @ ${roundDownToTick(existingStopPrice, this.config.priceTick)}`);
this.tradeLog.push("order", `恢复原止损 @ ${formatPriceToString(existingStopPrice, Math.max(0, Math.floor(Math.log10(1 / this.config.priceTick))))}`);
}
}
} catch (recoverErr) {