mirror of
https://github.com/discountry/ritmex-bot.git
synced 2026-09-11 17:28:08 +00:00
add filter
This commit is contained in:
@@ -199,6 +199,8 @@ export interface MakerPointsConfig {
|
|||||||
minRepriceBps: number;
|
minRepriceBps: number;
|
||||||
/** 是否根据 Binance 盘口深度失衡自动取消单边挂单,默认 true */
|
/** 是否根据 Binance 盘口深度失衡自动取消单边挂单,默认 true */
|
||||||
enableBinanceDepthCancel: boolean;
|
enableBinanceDepthCancel: boolean;
|
||||||
|
/** 0-10 bps 档位最小深度阈值 (BTC),盘口到目标价之间的挂单量低于此值则跳过该档位,默认 1 */
|
||||||
|
band0To10MinDepth: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultMakerPointsAmount = parseNumber(process.env.MAKER_POINTS_ORDER_AMOUNT, parseNumber(process.env.TRADE_AMOUNT, 0.001));
|
const defaultMakerPointsAmount = parseNumber(process.env.MAKER_POINTS_ORDER_AMOUNT, parseNumber(process.env.TRADE_AMOUNT, 0.001));
|
||||||
@@ -224,6 +226,7 @@ export const makerPointsConfig: MakerPointsConfig = {
|
|||||||
band30To100Amount: parseNumber(process.env.MAKER_POINTS_BAND_30_100_AMOUNT, defaultMakerPointsAmount),
|
band30To100Amount: parseNumber(process.env.MAKER_POINTS_BAND_30_100_AMOUNT, defaultMakerPointsAmount),
|
||||||
minRepriceBps: parseNumber(process.env.MAKER_POINTS_MIN_REPRICE_BPS, 3),
|
minRepriceBps: parseNumber(process.env.MAKER_POINTS_MIN_REPRICE_BPS, 3),
|
||||||
enableBinanceDepthCancel: parseBoolean(process.env.MAKER_POINTS_BINANCE_DEPTH_CANCEL, true),
|
enableBinanceDepthCancel: parseBoolean(process.env.MAKER_POINTS_BINANCE_DEPTH_CANCEL, true),
|
||||||
|
band0To10MinDepth: parseNumber(process.env.MAKER_POINTS_BAND_0_10_MIN_DEPTH, 1),
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface BasisArbConfig {
|
export interface BasisArbConfig {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { isOrderActiveStatus } from "../utils/order-status";
|
|||||||
import { getPosition, parseSymbolParts } from "../utils/strategy";
|
import { getPosition, parseSymbolParts } from "../utils/strategy";
|
||||||
import type { PositionSnapshot } from "../utils/strategy";
|
import type { PositionSnapshot } from "../utils/strategy";
|
||||||
import { computePositionPnl } from "../utils/pnl";
|
import { computePositionPnl } from "../utils/pnl";
|
||||||
import { getMidOrLast, getTopPrices } from "../utils/price";
|
import { getDepthBetweenPrices, getMidOrLast, getTopPrices } from "../utils/price";
|
||||||
import {
|
import {
|
||||||
marketClose,
|
marketClose,
|
||||||
placeOrder,
|
placeOrder,
|
||||||
@@ -504,6 +504,7 @@ export class MakerPointsEngine {
|
|||||||
ask1: topAsk,
|
ask1: topAsk,
|
||||||
skipBuy,
|
skipBuy,
|
||||||
skipSell,
|
skipSell,
|
||||||
|
depth,
|
||||||
})
|
})
|
||||||
: this.desiredOrders;
|
: this.desiredOrders;
|
||||||
|
|
||||||
@@ -542,8 +543,9 @@ export class MakerPointsEngine {
|
|||||||
ask1: number;
|
ask1: number;
|
||||||
skipBuy: boolean;
|
skipBuy: boolean;
|
||||||
skipSell: boolean;
|
skipSell: boolean;
|
||||||
|
depth: AsterDepth | null;
|
||||||
}): DesiredOrder[] {
|
}): DesiredOrder[] {
|
||||||
const { bid1, ask1, skipBuy, skipSell } = params;
|
const { bid1, ask1, skipBuy, skipSell, depth } = params;
|
||||||
|
|
||||||
const targets = buildBpsTargets({
|
const targets = buildBpsTargets({
|
||||||
band0To10: this.config.enableBand0To10,
|
band0To10: this.config.enableBand0To10,
|
||||||
@@ -555,6 +557,7 @@ export class MakerPointsEngine {
|
|||||||
|
|
||||||
const priceDecimals = this.getPriceDecimals();
|
const priceDecimals = this.getPriceDecimals();
|
||||||
const desired: DesiredOrder[] = [];
|
const desired: DesiredOrder[] = [];
|
||||||
|
const minDepth = this.config.band0To10MinDepth;
|
||||||
|
|
||||||
const getAmountForBps = (bps: number): number => {
|
const getAmountForBps = (bps: number): number => {
|
||||||
if (bps <= 10) return Number(this.config.band0To10Amount);
|
if (bps <= 10) return Number(this.config.band0To10Amount);
|
||||||
@@ -566,26 +569,63 @@ export class MakerPointsEngine {
|
|||||||
const amount = getAmountForBps(bps);
|
const amount = getAmountForBps(bps);
|
||||||
if (!Number.isFinite(amount) || amount <= 0) continue;
|
if (!Number.isFinite(amount) || amount <= 0) continue;
|
||||||
|
|
||||||
|
// 对于 0-10 bps 档位,检查深度是否足够
|
||||||
|
const isBand0To10 = bps <= 10;
|
||||||
|
|
||||||
if (!skipBuy) {
|
if (!skipBuy) {
|
||||||
const price = bid1 * (1 - bps / 10000);
|
const price = bid1 * (1 - bps / 10000);
|
||||||
if (Number.isFinite(price) && price > 0) {
|
if (Number.isFinite(price) && price > 0) {
|
||||||
desired.push({
|
// 0-10 bps 档位需要检查深度
|
||||||
side: "BUY",
|
if (isBand0To10 && minDepth > 0) {
|
||||||
price: formatPriceToString(price, priceDecimals),
|
const depthQty = getDepthBetweenPrices(depth, "BUY", price);
|
||||||
amount,
|
if (depthQty < minDepth) {
|
||||||
reduceOnly: false,
|
this.logThinDepthSkip("BUY", bps, depthQty, minDepth);
|
||||||
});
|
// 跳过该档位的 BUY 挂单
|
||||||
|
} else {
|
||||||
|
this.resetThinDepthSkip("BUY");
|
||||||
|
desired.push({
|
||||||
|
side: "BUY",
|
||||||
|
price: formatPriceToString(price, priceDecimals),
|
||||||
|
amount,
|
||||||
|
reduceOnly: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
desired.push({
|
||||||
|
side: "BUY",
|
||||||
|
price: formatPriceToString(price, priceDecimals),
|
||||||
|
amount,
|
||||||
|
reduceOnly: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!skipSell) {
|
if (!skipSell) {
|
||||||
const price = ask1 * (1 + bps / 10000);
|
const price = ask1 * (1 + bps / 10000);
|
||||||
if (Number.isFinite(price) && price > 0) {
|
if (Number.isFinite(price) && price > 0) {
|
||||||
desired.push({
|
// 0-10 bps 档位需要检查深度
|
||||||
side: "SELL",
|
if (isBand0To10 && minDepth > 0) {
|
||||||
price: formatPriceToString(price, priceDecimals),
|
const depthQty = getDepthBetweenPrices(depth, "SELL", price);
|
||||||
amount,
|
if (depthQty < minDepth) {
|
||||||
reduceOnly: false,
|
this.logThinDepthSkip("SELL", bps, depthQty, minDepth);
|
||||||
});
|
// 跳过该档位的 SELL 挂单
|
||||||
|
} else {
|
||||||
|
this.resetThinDepthSkip("SELL");
|
||||||
|
desired.push({
|
||||||
|
side: "SELL",
|
||||||
|
price: formatPriceToString(price, priceDecimals),
|
||||||
|
amount,
|
||||||
|
reduceOnly: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
desired.push({
|
||||||
|
side: "SELL",
|
||||||
|
price: formatPriceToString(price, priceDecimals),
|
||||||
|
amount,
|
||||||
|
reduceOnly: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1127,6 +1167,47 @@ export class MakerPointsEngine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private lastThinDepthSkipBuy = false;
|
||||||
|
private lastThinDepthSkipSell = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录因深度不足而跳过挂单的日志
|
||||||
|
* 使用状态跟踪避免重复日志
|
||||||
|
*/
|
||||||
|
private logThinDepthSkip(side: "BUY" | "SELL", bps: number, depthQty: number, minDepth: number): void {
|
||||||
|
const isBuy = side === "BUY";
|
||||||
|
const alreadySkipped = isBuy ? this.lastThinDepthSkipBuy : this.lastThinDepthSkipSell;
|
||||||
|
|
||||||
|
if (!alreadySkipped) {
|
||||||
|
this.tradeLog.push(
|
||||||
|
"info",
|
||||||
|
`跳过 ${side} ${bps}bps 挂单: 深度 ${depthQty.toFixed(4)} BTC < ${minDepth} BTC`
|
||||||
|
);
|
||||||
|
if (isBuy) {
|
||||||
|
this.lastThinDepthSkipBuy = true;
|
||||||
|
} else {
|
||||||
|
this.lastThinDepthSkipSell = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当深度恢复时重置跳过状态,允许下次再次记录
|
||||||
|
*/
|
||||||
|
private resetThinDepthSkip(side: "BUY" | "SELL"): void {
|
||||||
|
if (side === "BUY") {
|
||||||
|
if (this.lastThinDepthSkipBuy) {
|
||||||
|
this.tradeLog.push("info", "BUY 深度恢复,继续挂单");
|
||||||
|
this.lastThinDepthSkipBuy = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.lastThinDepthSkipSell) {
|
||||||
|
this.tradeLog.push("info", "SELL 深度恢复,继续挂单");
|
||||||
|
this.lastThinDepthSkipSell = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private registerInsufficientBalance(error: unknown): void {
|
private registerInsufficientBalance(error: unknown): void {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const detail = extractMessage(error);
|
const detail = extractMessage(error);
|
||||||
|
|||||||
@@ -56,4 +56,58 @@ export function getMidOrLast(depth?: AsterDepth | null, ticker?: AsterTicker | n
|
|||||||
return Number.isFinite(last) ? last : null;
|
return Number.isFinite(last) ? last : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算从盘口一档到目标价格之间的挂单总量
|
||||||
|
* @param depth 深度数据
|
||||||
|
* @param side 挂单方向: BUY 检查 bids, SELL 检查 asks
|
||||||
|
* @param targetPrice 目标挂单价格
|
||||||
|
* @returns 从一档到目标价格之间的挂单总量 (不包含目标价格本身)
|
||||||
|
*/
|
||||||
|
export function getDepthBetweenPrices(
|
||||||
|
depth: AsterDepth | null | undefined,
|
||||||
|
side: "BUY" | "SELL",
|
||||||
|
targetPrice: number
|
||||||
|
): number {
|
||||||
|
if (!depth) return 0;
|
||||||
|
if (!Number.isFinite(targetPrice) || targetPrice <= 0) return 0;
|
||||||
|
|
||||||
|
let total = 0;
|
||||||
|
|
||||||
|
if (side === "BUY") {
|
||||||
|
// BUY 订单挂在 bid 侧,检查从 bid1 到目标价格之间的所有 bids
|
||||||
|
// bids 按价格从高到低排序,目标价格 < bid1
|
||||||
|
const bids = depth.bids ?? [];
|
||||||
|
for (const level of bids) {
|
||||||
|
const price = Number(level[0]);
|
||||||
|
const qty = Number(level[1]);
|
||||||
|
if (!Number.isFinite(price) || !Number.isFinite(qty)) continue;
|
||||||
|
// 只计算价格 > 目标价格的档位 (目标价格以上的挂单)
|
||||||
|
if (price > targetPrice) {
|
||||||
|
total += qty;
|
||||||
|
} else {
|
||||||
|
// bids 是从高到低排序,一旦 price <= targetPrice 就停止
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// SELL 订单挂在 ask 侧,检查从 ask1 到目标价格之间的所有 asks
|
||||||
|
// asks 按价格从低到高排序,目标价格 > ask1
|
||||||
|
const asks = depth.asks ?? [];
|
||||||
|
for (const level of asks) {
|
||||||
|
const price = Number(level[0]);
|
||||||
|
const qty = Number(level[1]);
|
||||||
|
if (!Number.isFinite(price) || !Number.isFinite(qty)) continue;
|
||||||
|
// 只计算价格 < 目标价格的档位 (目标价格以下的挂单)
|
||||||
|
if (price < targetPrice) {
|
||||||
|
total += qty;
|
||||||
|
} else {
|
||||||
|
// asks 是从低到高排序,一旦 price >= targetPrice 就停止
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user