Refactor Maker Points logic by removing dislocation calculations and related UI elements. Update MakerPointsEngine to utilize new price fetching methods and streamline order synchronization. Adjust translations and tests accordingly to reflect these changes.

This commit is contained in:
discountry
2026-01-06 16:18:54 +08:00
parent 7aafc3b69d
commit 33b5407245
5 changed files with 11 additions and 132 deletions
-4
View File
@@ -185,10 +185,6 @@ const translations: Record<string, TranslationEntry> = {
zh: "交易所: {exchange} 交易对: {symbol} 买一价: {bid} 卖一价: {ask} 点差: {spread}",
en: "Exchange: {exchange} | Symbol: {symbol} | Best Bid: {bid} | Best Ask: {ask} | Spread: {spread}",
},
"makerPoints.markLine": {
zh: "标记价: {mark} 偏离: {bps} bps 阻断: {block} bps",
en: "Mark: {mark} | Dislocation: {bps} bps | Block: {block} bps",
},
"makerPoints.quoteLine": {
zh: "挂单模式: {mode} BUY {buy} SELL {sell}",
en: "Quote mode: {mode} | BUY {buy} | SELL {sell}",
+10 -86
View File
@@ -13,7 +13,7 @@ import { isOrderActiveStatus } from "../utils/order-status";
import { getPosition, parseSymbolParts } from "../utils/strategy";
import type { PositionSnapshot } from "../utils/strategy";
import { computePositionPnl } from "../utils/pnl";
import { getTopPrices } from "../utils/price";
import { getMidOrLast, getTopPrices } from "../utils/price";
import {
marketClose,
placeOrder,
@@ -27,7 +27,7 @@ import { StrategyEventEmitter } from "./common/event-emitter";
import { safeSubscribe, type LogHandler } from "./common/subscriptions";
import { SessionVolumeTracker } from "./common/session-volume";
import { BinanceDepthTracker, type BinanceDepthSnapshot } from "./common/binance-depth";
import { buildBpsTargets, computeDislocationBps } from "./maker-points-logic";
import { buildBpsTargets } from "./maker-points-logic";
import { t } from "../i18n";
interface DesiredOrder {
@@ -43,9 +43,6 @@ export interface MakerPointsSnapshot {
topBid: number | null;
topAsk: number | null;
spread: number | null;
markPrice: number | null;
dislocationBps: number | null;
blockedBps: number;
priceDecimals: number;
position: PositionSnapshot;
pnl: number;
@@ -75,7 +72,6 @@ type MakerPointsListener = (snapshot: MakerPointsSnapshot) => void;
const EPS = 1e-5;
const INSUFFICIENT_BALANCE_COOLDOWN_MS = 15_000;
const DISLOCATION_THRESHOLD_BPS = 1;
const STOP_LOSS_COOLDOWN_MS = 10_000;
export class MakerPointsEngine {
@@ -108,9 +104,7 @@ export class MakerPointsEngine {
private accountUnrealized = 0;
private initialOrderSnapshotReady = false;
private initialOrderResetDone = false;
private entryPricePendingLogged = false;
private lastDesiredSummary: string | null = null;
private lastDislocationBlock = 0;
private lastCloseOnly = false;
private lastSkipBuy = false;
private lastSkipSell = false;
@@ -340,35 +334,6 @@ export class MakerPointsEngine {
this.lastCloseOnly = closeOnly;
}
const markPrice = this.getMarkPrice();
const hasMarkPrice = Number.isFinite(markPrice) && (markPrice ?? 0) > 0;
if (!hasMarkPrice && !closeOnly) {
if (!this.entryPricePendingLogged) {
this.tradeLog.push("info", "等待标记价格推送…");
this.entryPricePendingLogged = true;
}
this.emitUpdate();
return;
}
if (hasMarkPrice) {
this.entryPricePendingLogged = false;
}
const resolvedMarkPrice = hasMarkPrice ? Number(markPrice) : 0;
const dislocationBps = hasMarkPrice ? computeDislocationBps(resolvedMarkPrice, topBid, topAsk) : null;
const blockBps =
dislocationBps != null && dislocationBps > DISLOCATION_THRESHOLD_BPS
? Math.floor(dislocationBps + 1e-9)
: 0;
const prevBlockBps = this.lastDislocationBlock;
if (blockBps !== prevBlockBps) {
if (blockBps > 0) {
this.tradeLog.push("warn", `标记价偏离盘口 ${blockBps} bps,撤销该范围挂单`);
} else if (prevBlockBps > 0) {
this.tradeLog.push("info", "标记价偏离已恢复,恢复挂单");
}
this.lastDislocationBlock = blockBps;
}
const binanceSnapshot = this.binanceDepth.getSnapshot();
const rawSkipBuy = Boolean(binanceSnapshot?.skipBuySide);
@@ -388,14 +353,12 @@ export class MakerPointsEngine {
this.lastSkipSell = skipSell;
}
const blockChanged = blockBps !== prevBlockBps;
const closeOnlyChanged = closeOnly !== prevCloseOnly;
const skipChanged = skipBuy !== prevSkipBuy || skipSell !== prevSkipSell;
const repriceNeeded = closeOnly ? true : this.shouldReprice(topBid, topAsk);
const shouldRecompute =
closeOnly ||
repriceNeeded ||
blockChanged ||
closeOnlyChanged ||
skipChanged ||
this.desiredOrders.length === 0;
@@ -406,8 +369,6 @@ export class MakerPointsEngine {
: this.buildDesiredOrders({
bid1: topBid,
ask1: topAsk,
markPrice: resolvedMarkPrice,
blockBps,
skipBuy,
skipSell,
})
@@ -426,7 +387,7 @@ export class MakerPointsEngine {
this.desiredOrders = desired;
this.logDesiredOrders(desired);
this.sessionVolume.update(position, this.getReferencePrice());
await this.syncOrders(desired, resolvedMarkPrice, closeOnly);
await this.syncOrders(desired, closeOnly);
this.emitUpdate();
} catch (error) {
if (isRateLimitError(error)) {
@@ -446,12 +407,10 @@ export class MakerPointsEngine {
private buildDesiredOrders(params: {
bid1: number;
ask1: number;
markPrice: number;
blockBps: number;
skipBuy: boolean;
skipSell: boolean;
}): DesiredOrder[] {
const { bid1, ask1, markPrice, blockBps, skipBuy, skipSell } = params;
const { bid1, ask1, skipBuy, skipSell } = params;
const amount = Number(this.config.perOrderAmount);
if (!Number.isFinite(amount) || amount <= 0) return [];
@@ -469,12 +428,7 @@ export class MakerPointsEngine {
for (const bps of targets) {
if (!skipBuy) {
const price = bid1 * (1 - bps / 10000);
const distanceBps = (markPrice - price) / markPrice * 10000;
if (
Number.isFinite(price) &&
price > 0 &&
(!Number.isFinite(distanceBps) || distanceBps > blockBps)
) {
if (Number.isFinite(price) && price > 0) {
desired.push({
side: "BUY",
price: formatPriceToString(price, priceDecimals),
@@ -485,12 +439,7 @@ export class MakerPointsEngine {
}
if (!skipSell) {
const price = ask1 * (1 + bps / 10000);
const distanceBps = (price - markPrice) / markPrice * 10000;
if (
Number.isFinite(price) &&
price > 0 &&
(!Number.isFinite(distanceBps) || distanceBps > blockBps)
) {
if (Number.isFinite(price) && price > 0) {
desired.push({
side: "SELL",
price: formatPriceToString(price, priceDecimals),
@@ -574,7 +523,7 @@ export class MakerPointsEngine {
}
}
private async syncOrders(targets: DesiredOrder[], markPrice: number, closeOnly: boolean): Promise<void> {
private async syncOrders(targets: DesiredOrder[], closeOnly: boolean): Promise<void> {
const availableOrders = this.openOrders.filter((o) => !this.pendingCancelOrders.has(String(o.orderId)));
const openOrders = availableOrders.filter((order) => isOrderActiveStatus(order.status));
const { toCancel, toPlace } = makeOrderPlan(openOrders, targets);
@@ -626,15 +575,11 @@ export class MakerPointsEngine {
target.amount,
(type, detail) => this.tradeLog.push(type, detail),
target.reduceOnly,
closeOnly
? undefined
: {
markPrice,
maxPct: this.config.maxCloseSlippagePct,
},
undefined,
{
priceTick: this.priceTick,
qtyStep: this.qtyStep,
skipDedupe: true,
}
);
} catch (error) {
@@ -783,12 +728,6 @@ export class MakerPointsEngine {
const position = getPosition(this.accountSnapshot, this.config.symbol);
const { topBid, topAsk } = getTopPrices(this.depthSnapshot);
const spread = topBid != null && topAsk != null ? topAsk - topBid : null;
const markPrice = this.getMarkPrice();
const dislocationBps = computeDislocationBps(markPrice, topBid, topAsk);
const blockBps =
dislocationBps != null && dislocationBps > DISLOCATION_THRESHOLD_BPS
? Math.floor(dislocationBps + 1e-9)
: 0;
const pnl = computePositionPnl(position, topBid, topAsk);
return {
@@ -797,9 +736,6 @@ export class MakerPointsEngine {
topBid,
topAsk,
spread,
markPrice,
dislocationBps,
blockedBps: blockBps,
priceDecimals: this.getPriceDecimals(),
position,
pnl,
@@ -820,19 +756,7 @@ export class MakerPointsEngine {
}
private getReferencePrice(): number | null {
const mark = Number(this.tickerSnapshot?.markPrice);
if (Number.isFinite(mark) && mark > 0) return mark;
const last = Number(this.tickerSnapshot?.lastPrice);
return Number.isFinite(last) && last > 0 ? last : null;
}
private getMarkPrice(): number | null {
const mark = Number(this.tickerSnapshot?.markPrice);
if (Number.isFinite(mark) && mark > 0) return mark;
const positionMark = Number(getPosition(this.accountSnapshot, this.config.symbol).markPrice);
if (Number.isFinite(positionMark) && positionMark > 0) return positionMark;
const last = Number(this.tickerSnapshot?.lastPrice);
return Number.isFinite(last) && last > 0 ? last : null;
return getMidOrLast(this.depthSnapshot, this.tickerSnapshot);
}
private logReadinessBlockers(): void {
+1 -9
View File
@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { buildBpsTargets, computeDislocationBps } from "./maker-points-logic";
import { buildBpsTargets } from "./maker-points-logic";
describe("maker points target builder", () => {
it("builds fixed bps targets per enabled band", () => {
@@ -20,11 +20,3 @@ describe("maker points target builder", () => {
expect(targets).toEqual([9, 99]);
});
});
describe("maker points dislocation", () => {
it("computes max bps dislocation from mark vs bid/ask", () => {
const bps = computeDislocationBps(100, 99.97, 100.02);
expect(bps).not.toBeNull();
expect(Number(bps?.toFixed(2))).toBe(3.0);
});
});
-21
View File
@@ -11,24 +11,3 @@ export function buildBpsTargets(config: MakerPointsBandConfig): number[] {
if (config.band30To100) targets.push(99);
return targets.sort((a, b) => a - b);
}
export function computeDislocationBps(
markPrice: number | null | undefined,
bestBid: number | null | undefined,
bestAsk: number | null | undefined
): number | null {
const mark = Number(markPrice);
if (!Number.isFinite(mark) || mark <= 0) return null;
const distances: number[] = [];
const bid = Number(bestBid);
const ask = Number(bestAsk);
if (Number.isFinite(bid)) {
distances.push(Math.abs(mark - bid) / mark * 10000);
}
if (Number.isFinite(ask)) {
distances.push(Math.abs(ask - mark) / mark * 10000);
}
if (!distances.length) return null;
const max = Math.max(...distances);
return Number.isFinite(max) ? max : null;
}
-12
View File
@@ -79,10 +79,6 @@ export function MakerPointsApp({ onExit }: MakerPointsAppProps) {
const spreadDisplay =
snapshot.spread != null ? `${formatNumber(snapshot.spread, spreadDigits)} USDT` : "-";
const hasPosition = Math.abs(snapshot.position.positionAmt) > 1e-5;
const markDisplay = Number.isFinite(snapshot.markPrice) ? formatNumber(snapshot.markPrice, priceDigits) : "-";
const dislocationDisplay =
snapshot.dislocationBps != null ? formatNumber(snapshot.dislocationBps, 2) : "-";
const blockDisplay = snapshot.blockedBps > 0 ? String(snapshot.blockedBps) : "-";
const sortedOrders = [...snapshot.openOrders].sort((a, b) =>
(Number(b.updateTime ?? 0) - Number(a.updateTime ?? 0)) || Number(b.orderId) - Number(a.orderId)
@@ -153,13 +149,6 @@ export function MakerPointsApp({ onExit }: MakerPointsAppProps) {
spread: spreadDisplay,
})}
</Text>
<Text>
{t("makerPoints.markLine", {
mark: markDisplay,
bps: dislocationDisplay,
block: blockDisplay,
})}
</Text>
<Text color="gray">{t("trend.statusLine", { status: readyStatus })}</Text>
<Text>
{t("makerPoints.quoteLine", {
@@ -247,4 +236,3 @@ export function MakerPointsApp({ onExit }: MakerPointsAppProps) {
</Box>
);
}