diff --git a/src/i18n/index.ts b/src/i18n/index.ts index bbb46c0..d665369 100644 --- a/src/i18n/index.ts +++ b/src/i18n/index.ts @@ -185,10 +185,6 @@ const translations: Record = { 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}", diff --git a/src/strategy/maker-points-engine.ts b/src/strategy/maker-points-engine.ts index 2d3b7be..70d8ab1 100644 --- a/src/strategy/maker-points-engine.ts +++ b/src/strategy/maker-points-engine.ts @@ -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 { + private async syncOrders(targets: DesiredOrder[], closeOnly: boolean): Promise { 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 { diff --git a/src/strategy/maker-points-logic.test.ts b/src/strategy/maker-points-logic.test.ts index d9bf757..9dc776e 100644 --- a/src/strategy/maker-points-logic.test.ts +++ b/src/strategy/maker-points-logic.test.ts @@ -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); - }); -}); diff --git a/src/strategy/maker-points-logic.ts b/src/strategy/maker-points-logic.ts index e7dcb03..5136eca 100644 --- a/src/strategy/maker-points-logic.ts +++ b/src/strategy/maker-points-logic.ts @@ -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; -} diff --git a/src/ui/MakerPointsApp.tsx b/src/ui/MakerPointsApp.tsx index 01ac2f3..c90dc06 100644 --- a/src/ui/MakerPointsApp.tsx +++ b/src/ui/MakerPointsApp.tsx @@ -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, })} - - {t("makerPoints.markLine", { - mark: markDisplay, - bps: dislocationDisplay, - block: blockDisplay, - })} - {t("trend.statusLine", { status: readyStatus })} {t("makerPoints.quoteLine", { @@ -247,4 +236,3 @@ export function MakerPointsApp({ onExit }: MakerPointsAppProps) { ); } -