mirror of
https://github.com/discountry/ritmex-bot.git
synced 2026-09-09 08:18:07 +00:00
Refactor price calculation in MakerPointsEngine for improved accuracy
- Replaced direct price calculations with a new method `normalizeDepthTargetPrice` to ensure valid target prices for buy and sell orders. - Updated all instances of price calculations in the MakerPointsEngine to utilize the new normalization method. - Added boundary tests for `getDepthBetweenPrices` to validate behavior when prices are exactly at the target.
This commit is contained in:
@@ -772,17 +772,17 @@ export class MakerPointsEngine {
|
|||||||
const shouldCheckDepth = minDepth > 0;
|
const shouldCheckDepth = minDepth > 0;
|
||||||
|
|
||||||
if (!skipBuy) {
|
if (!skipBuy) {
|
||||||
const price = bid1 * (1 - bps / 10000);
|
const targetPrice = this.normalizeDepthTargetPrice(bid1 * (1 - bps / 10000), priceDecimals);
|
||||||
if (Number.isFinite(price) && price > 0) {
|
if (targetPrice != null) {
|
||||||
if (shouldCheckDepth) {
|
if (shouldCheckDepth) {
|
||||||
const depthQty = getDepthBetweenPrices(depth, "BUY", price);
|
const depthQty = getDepthBetweenPrices(depth, "BUY", targetPrice);
|
||||||
if (depthQty < minDepth) {
|
if (depthQty < minDepth) {
|
||||||
this.logThinDepthSkip("BUY", bps, depthQty, minDepth);
|
this.logThinDepthSkip("BUY", bps, depthQty, minDepth);
|
||||||
} else {
|
} else {
|
||||||
this.resetThinDepthSkip("BUY", bps);
|
this.resetThinDepthSkip("BUY", bps);
|
||||||
desired.push({
|
desired.push({
|
||||||
side: "BUY",
|
side: "BUY",
|
||||||
price: formatPriceToString(price, priceDecimals),
|
price: formatPriceToString(targetPrice, priceDecimals),
|
||||||
amount,
|
amount,
|
||||||
reduceOnly: false,
|
reduceOnly: false,
|
||||||
});
|
});
|
||||||
@@ -790,7 +790,7 @@ export class MakerPointsEngine {
|
|||||||
} else {
|
} else {
|
||||||
desired.push({
|
desired.push({
|
||||||
side: "BUY",
|
side: "BUY",
|
||||||
price: formatPriceToString(price, priceDecimals),
|
price: formatPriceToString(targetPrice, priceDecimals),
|
||||||
amount,
|
amount,
|
||||||
reduceOnly: false,
|
reduceOnly: false,
|
||||||
});
|
});
|
||||||
@@ -798,17 +798,17 @@ export class MakerPointsEngine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!skipSell) {
|
if (!skipSell) {
|
||||||
const price = ask1 * (1 + bps / 10000);
|
const targetPrice = this.normalizeDepthTargetPrice(ask1 * (1 + bps / 10000), priceDecimals);
|
||||||
if (Number.isFinite(price) && price > 0) {
|
if (targetPrice != null) {
|
||||||
if (shouldCheckDepth) {
|
if (shouldCheckDepth) {
|
||||||
const depthQty = getDepthBetweenPrices(depth, "SELL", price);
|
const depthQty = getDepthBetweenPrices(depth, "SELL", targetPrice);
|
||||||
if (depthQty < minDepth) {
|
if (depthQty < minDepth) {
|
||||||
this.logThinDepthSkip("SELL", bps, depthQty, minDepth);
|
this.logThinDepthSkip("SELL", bps, depthQty, minDepth);
|
||||||
} else {
|
} else {
|
||||||
this.resetThinDepthSkip("SELL", bps);
|
this.resetThinDepthSkip("SELL", bps);
|
||||||
desired.push({
|
desired.push({
|
||||||
side: "SELL",
|
side: "SELL",
|
||||||
price: formatPriceToString(price, priceDecimals),
|
price: formatPriceToString(targetPrice, priceDecimals),
|
||||||
amount,
|
amount,
|
||||||
reduceOnly: false,
|
reduceOnly: false,
|
||||||
});
|
});
|
||||||
@@ -816,7 +816,7 @@ export class MakerPointsEngine {
|
|||||||
} else {
|
} else {
|
||||||
desired.push({
|
desired.push({
|
||||||
side: "SELL",
|
side: "SELL",
|
||||||
price: formatPriceToString(price, priceDecimals),
|
price: formatPriceToString(targetPrice, priceDecimals),
|
||||||
amount,
|
amount,
|
||||||
reduceOnly: false,
|
reduceOnly: false,
|
||||||
});
|
});
|
||||||
@@ -839,6 +839,7 @@ export class MakerPointsEngine {
|
|||||||
): boolean {
|
): boolean {
|
||||||
const minDepth = this.config.filterMinDepth;
|
const minDepth = this.config.filterMinDepth;
|
||||||
if (minDepth <= 0) return false;
|
if (minDepth <= 0) return false;
|
||||||
|
const priceDecimals = this.getPriceDecimals();
|
||||||
|
|
||||||
// 获取启用的所有档位
|
// 获取启用的所有档位
|
||||||
const targets = buildBpsTargets({
|
const targets = buildBpsTargets({
|
||||||
@@ -850,11 +851,11 @@ export class MakerPointsEngine {
|
|||||||
let changed = false;
|
let changed = false;
|
||||||
|
|
||||||
for (const bps of targets) {
|
for (const bps of targets) {
|
||||||
const buyPrice = bid1 * (1 - bps / 10000);
|
const buyTargetPrice = this.normalizeDepthTargetPrice(bid1 * (1 - bps / 10000), priceDecimals);
|
||||||
const sellPrice = ask1 * (1 + bps / 10000);
|
const sellTargetPrice = this.normalizeDepthTargetPrice(ask1 * (1 + bps / 10000), priceDecimals);
|
||||||
|
|
||||||
const buyDepthQty = getDepthBetweenPrices(depth, "BUY", buyPrice);
|
const buyDepthQty = getDepthBetweenPrices(depth, "BUY", buyTargetPrice ?? 0);
|
||||||
const sellDepthQty = getDepthBetweenPrices(depth, "SELL", sellPrice);
|
const sellDepthQty = getDepthBetweenPrices(depth, "SELL", sellTargetPrice ?? 0);
|
||||||
const currentBuyOk = buyDepthQty >= minDepth;
|
const currentBuyOk = buyDepthQty >= minDepth;
|
||||||
const currentSellOk = sellDepthQty >= minDepth;
|
const currentSellOk = sellDepthQty >= minDepth;
|
||||||
|
|
||||||
@@ -889,15 +890,16 @@ export class MakerPointsEngine {
|
|||||||
band10To30: this.config.enableBand10To30,
|
band10To30: this.config.enableBand10To30,
|
||||||
band30To100: this.config.enableBand30To100,
|
band30To100: this.config.enableBand30To100,
|
||||||
});
|
});
|
||||||
|
const priceDecimals = this.getPriceDecimals();
|
||||||
|
|
||||||
for (const bps of targets) {
|
for (const bps of targets) {
|
||||||
const lastStatus = this.lastDepthOkStatus[bps];
|
const lastStatus = this.lastDepthOkStatus[bps];
|
||||||
if (!lastStatus) continue;
|
if (!lastStatus) continue;
|
||||||
|
|
||||||
const buyPrice = topBid * (1 - bps / 10000);
|
const buyTargetPrice = this.normalizeDepthTargetPrice(topBid * (1 - bps / 10000), priceDecimals);
|
||||||
const sellPrice = topAsk * (1 + bps / 10000);
|
const sellTargetPrice = this.normalizeDepthTargetPrice(topAsk * (1 + bps / 10000), priceDecimals);
|
||||||
const buyDepthQty = getDepthBetweenPrices(depth, "BUY", buyPrice);
|
const buyDepthQty = getDepthBetweenPrices(depth, "BUY", buyTargetPrice ?? 0);
|
||||||
const sellDepthQty = getDepthBetweenPrices(depth, "SELL", sellPrice);
|
const sellDepthQty = getDepthBetweenPrices(depth, "SELL", sellTargetPrice ?? 0);
|
||||||
const currentBuyOk = buyDepthQty >= minDepth;
|
const currentBuyOk = buyDepthQty >= minDepth;
|
||||||
const currentSellOk = sellDepthQty >= minDepth;
|
const currentSellOk = sellDepthQty >= minDepth;
|
||||||
|
|
||||||
@@ -1344,6 +1346,13 @@ export class MakerPointsEngine {
|
|||||||
return Math.max(0, Math.floor(raw + 1e-9));
|
return Math.max(0, Math.floor(raw + 1e-9));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private normalizeDepthTargetPrice(price: number, priceDecimals: number): number | null {
|
||||||
|
if (!Number.isFinite(price) || price <= 0) return null;
|
||||||
|
const normalized = Number(formatPriceToString(price, priceDecimals));
|
||||||
|
if (!Number.isFinite(normalized) || normalized <= 0) return null;
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
|
||||||
private emitUpdate(): void {
|
private emitUpdate(): void {
|
||||||
try {
|
try {
|
||||||
const snapshot = this.buildSnapshot();
|
const snapshot = this.buildSnapshot();
|
||||||
@@ -1398,12 +1407,13 @@ export class MakerPointsEngine {
|
|||||||
if (!this.depthSnapshot || topBid == null || topAsk == null) {
|
if (!this.depthSnapshot || topBid == null || topAsk == null) {
|
||||||
return bands;
|
return bands;
|
||||||
}
|
}
|
||||||
|
const priceDecimals = this.getPriceDecimals();
|
||||||
|
|
||||||
return bands.map((band) => {
|
return bands.map((band) => {
|
||||||
const buyPrice = topBid * (1 - band.bps / 10000);
|
const buyTargetPrice = this.normalizeDepthTargetPrice(topBid * (1 - band.bps / 10000), priceDecimals);
|
||||||
const sellPrice = topAsk * (1 + band.bps / 10000);
|
const sellTargetPrice = this.normalizeDepthTargetPrice(topAsk * (1 + band.bps / 10000), priceDecimals);
|
||||||
const buyDepth = getDepthBetweenPrices(this.depthSnapshot, "BUY", buyPrice);
|
const buyDepth = getDepthBetweenPrices(this.depthSnapshot, "BUY", buyTargetPrice ?? 0);
|
||||||
const sellDepth = getDepthBetweenPrices(this.depthSnapshot, "SELL", sellPrice);
|
const sellDepth = getDepthBetweenPrices(this.depthSnapshot, "SELL", sellTargetPrice ?? 0);
|
||||||
return { ...band, buyDepth, sellDepth };
|
return { ...band, buyDepth, sellDepth };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { getDepthBetweenPrices } from "../src/utils/price";
|
||||||
|
import type { AsterDepth } from "../src/exchanges/types";
|
||||||
|
|
||||||
|
describe("getDepthBetweenPrices boundary", () => {
|
||||||
|
it("SELL side excludes quantity exactly at target price", () => {
|
||||||
|
const depth: AsterDepth = {
|
||||||
|
lastUpdateId: 1,
|
||||||
|
bids: [],
|
||||||
|
asks: [
|
||||||
|
["69345", "1"],
|
||||||
|
["69349", "2"],
|
||||||
|
["69350", "999"],
|
||||||
|
["69351", "3"],
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const total = getDepthBetweenPrices(depth, "SELL", 69350);
|
||||||
|
expect(total).toBe(3); // 仅 69345 + 69349
|
||||||
|
});
|
||||||
|
|
||||||
|
it("BUY side excludes quantity exactly at target price", () => {
|
||||||
|
const depth: AsterDepth = {
|
||||||
|
lastUpdateId: 1,
|
||||||
|
bids: [
|
||||||
|
["69355", "1"],
|
||||||
|
["69351", "2"],
|
||||||
|
["69350", "999"],
|
||||||
|
["69349", "3"],
|
||||||
|
],
|
||||||
|
asks: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const total = getDepthBetweenPrices(depth, "BUY", 69350);
|
||||||
|
expect(total).toBe(3); // 仅 69355 + 69351
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user