Enhance entry price logic in Maker and Liquidity Maker strategies

- Added `entryDepthLevel` configuration option to `MakerConfig` and `LiquidityMakerConfig` for specifying order entry levels.
- Implemented `getPricesAtLevel` utility function to retrieve bid and ask prices at specified depth levels.
- Updated `MakerEngine`, `LiquidityMakerEngine`, and `OffsetMakerEngine` to utilize the new entry level logic for determining opening prices based on market depth.
- Improved price handling to ensure more accurate order placements in varying market conditions.
This commit is contained in:
discountry
2026-01-20 01:03:38 +08:00
parent 168d8cbb08
commit 76704b6bdd
5 changed files with 79 additions and 9 deletions
+6
View File
@@ -157,6 +157,8 @@ export interface MakerConfig {
maxLogEntries: number;
maxCloseSlippagePct: number;
priceTick: number;
/** 开仓挂单档位:1=买1/卖1,2=买2/卖2,以此类推。仅影响无仓位时的开仓挂单,平仓逻辑不受影响。默认1 */
entryDepthLevel: number;
}
export const makerConfig: MakerConfig = {
@@ -172,6 +174,7 @@ export const makerConfig: MakerConfig = {
0.05
),
priceTick: parseNumber(process.env.MAKER_PRICE_TICK ?? process.env.PRICE_TICK, 0.1),
entryDepthLevel: Math.max(1, Math.floor(parseNumber(process.env.MAKER_ENTRY_DEPTH_LEVEL, 1))),
};
export interface MakerPointsConfig {
@@ -347,6 +350,8 @@ export interface LiquidityMakerConfig {
closeTickOffset: number;
/** 偏移判断阈值倍数,当一侧深度超出另一侧此倍数时取消薄端订单,默认2 */
depthImbalanceRatio: number;
/** 开仓挂单档位:1=买1/卖1,2=买2/卖2,以此类推。仅影响无仓位时的开仓挂单,平仓逻辑不受影响。默认1 */
entryDepthLevel: number;
}
export const liquidityMakerConfig: LiquidityMakerConfig = {
@@ -364,6 +369,7 @@ export const liquidityMakerConfig: LiquidityMakerConfig = {
priceTick: parseNumber(process.env.LIQUIDITY_MAKER_PRICE_TICK ?? process.env.MAKER_PRICE_TICK ?? process.env.PRICE_TICK, 0.1),
closeTickOffset: Math.max(1, Math.floor(parseNumber(process.env.LIQUIDITY_MAKER_CLOSE_TICK_OFFSET, 1))),
depthImbalanceRatio: Math.max(1.1, parseNumber(process.env.LIQUIDITY_MAKER_DEPTH_IMBALANCE_RATIO, 2)),
entryDepthLevel: Math.max(1, Math.floor(parseNumber(process.env.MAKER_ENTRY_DEPTH_LEVEL, 1))),
};
export function isBasisStrategyEnabled(): boolean {
+11 -3
View File
@@ -14,7 +14,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, getMidOrLast } from "../utils/price";
import { getTopPrices, getPricesAtLevel, getMidOrLast } from "../utils/price";
import { shouldStopLoss } from "../utils/risk";
import {
marketClose,
@@ -430,10 +430,18 @@ export class LiquidityMakerEngine {
// 直接使用orderbook价格,格式化为字符串避免精度问题
const priceDecimals = this.getPriceDecimals();
// 平仓价格始终使用买1/卖1
const closeBidPrice = formatPriceToString(finalBid, priceDecimals);
const closeAskPrice = formatPriceToString(finalAsk, priceDecimals);
const rawBidPrice = finalBid - this.config.bidOffset;
const rawAskPrice = finalAsk + this.config.askOffset;
// 开仓价格根据 entryDepthLevel 使用指定档位
const entryLevel = this.config.entryDepthLevel ?? 1;
const { bidAtLevel: entryBid, askAtLevel: entryAsk } = getPricesAtLevel(latestDepth, entryLevel);
const entryBidBase = entryBid ?? finalBid;
const entryAskBase = entryAsk ?? finalAsk;
const rawBidPrice = entryBidBase - this.config.bidOffset;
const rawAskPrice = entryAskBase + this.config.askOffset;
const safeBid = this.ensureMakerPrice("BUY", rawBidPrice, finalBid, finalAsk);
const safeAsk = this.ensureMakerPrice("SELL", rawAskPrice, finalBid, finalAsk);
const bidPrice = safeBid != null ? formatPriceToString(safeBid, priceDecimals) : null;
+11 -3
View File
@@ -14,7 +14,7 @@ import { isOrderActiveStatus } from "../utils/order-status";
import { getPosition } from "../utils/strategy";
import type { PositionSnapshot } from "../utils/strategy";
import { computePositionPnl } from "../utils/pnl";
import { getTopPrices, getMidOrLast } from "../utils/price";
import { getTopPrices, getPricesAtLevel, getMidOrLast } from "../utils/price";
import { shouldStopLoss } from "../utils/risk";
import {
marketClose,
@@ -305,10 +305,18 @@ export class MakerEngine {
// 直接使用orderbook价格,格式化为字符串避免精度问题
const priceDecimals = this.getPriceDecimals();
// 平仓价格始终使用买1/卖1
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);
// 开仓价格根据 entryDepthLevel 使用指定档位
const entryLevel = this.config.entryDepthLevel ?? 1;
const { bidAtLevel: entryBid, askAtLevel: entryAsk } = getPricesAtLevel(depth, entryLevel);
const entryBidBase = entryBid ?? topBid;
const entryAskBase = entryAsk ?? topAsk;
const bidPrice = formatPriceToString(entryBidBase - this.config.bidOffset, priceDecimals);
const askPrice = formatPriceToString(entryAskBase + this.config.askOffset, priceDecimals);
const position = getPosition(this.accountSnapshot, this.config.symbol);
const absPosition = Math.abs(position.positionAmt);
const desired: DesiredOrder[] = [];
+11 -3
View File
@@ -15,7 +15,7 @@ import { getPosition, parseSymbolParts } from "../utils/strategy";
import type { PositionSnapshot } from "../utils/strategy";
import { computeDepthStats } from "../utils/depth";
import { computePositionPnl } from "../utils/pnl";
import { getTopPrices, getMidOrLast } from "../utils/price";
import { getTopPrices, getPricesAtLevel, getMidOrLast } from "../utils/price";
import { shouldStopLoss } from "../utils/risk";
import {
marketClose,
@@ -356,10 +356,18 @@ export class OffsetMakerEngine {
// 直接使用orderbook价格,格式化为字符串避免精度问题
const priceDecimals = this.getPriceDecimals();
// 平仓价格始终使用买1/卖1
const closeBidPrice = formatPriceToString(finalBid, priceDecimals);
const closeAskPrice = formatPriceToString(finalAsk, priceDecimals);
const rawBidPrice = finalBid - this.config.bidOffset;
const rawAskPrice = finalAsk + this.config.askOffset;
// 开仓价格根据 entryDepthLevel 使用指定档位
const entryLevel = this.config.entryDepthLevel ?? 1;
const { bidAtLevel: entryBid, askAtLevel: entryAsk } = getPricesAtLevel(latestDepth, entryLevel);
const entryBidBase = entryBid ?? finalBid;
const entryAskBase = entryAsk ?? finalAsk;
const rawBidPrice = entryBidBase - this.config.bidOffset;
const rawAskPrice = entryAskBase + this.config.askOffset;
const safeBid = this.ensureMakerPrice("BUY", rawBidPrice, finalBid, finalAsk);
const safeAsk = this.ensureMakerPrice("SELL", rawAskPrice, finalBid, finalAsk);
const bidPrice = safeBid != null ? formatPriceToString(safeBid, priceDecimals) : null;
+40
View File
@@ -9,6 +9,46 @@ export function getTopPrices(depth?: AsterDepth | null): { topBid: number | null
};
}
/**
*
* @param depth
* @param level 1=1/12=2/2
* @returns 退
*/
export function getPricesAtLevel(
depth?: AsterDepth | null,
level: number = 1
): { bidAtLevel: number | null; askAtLevel: number | null } {
const index = Math.max(0, level - 1);
// 尝试获取指定档位,如果不存在则回退到最近的有效档位
const bids = depth?.bids ?? [];
const asks = depth?.asks ?? [];
let bidAtLevel: number | null = null;
let askAtLevel: number | null = null;
// 从指定档位向前查找第一个有效的买价
for (let i = Math.min(index, bids.length - 1); i >= 0; i--) {
const bid = Number(bids[i]?.[0]);
if (Number.isFinite(bid)) {
bidAtLevel = bid;
break;
}
}
// 从指定档位向前查找第一个有效的卖价
for (let i = Math.min(index, asks.length - 1); i >= 0; i--) {
const ask = Number(asks[i]?.[0]);
if (Number.isFinite(ask)) {
askAtLevel = ask;
break;
}
}
return { bidAtLevel, askAtLevel };
}
export function getMidOrLast(depth?: AsterDepth | null, ticker?: AsterTicker | null): number | null {
const { topBid, topAsk } = getTopPrices(depth);
if (topBid != null && topAsk != null) return (topBid + topAsk) / 2;