add auction anchoring in v2
This commit is contained in:
@@ -34,6 +34,7 @@ import { wsMarketArgs } from 'src/dlob-subscriber/DLOBSubscriberIO';
|
||||
import { DEFAULT_AUCTION_PARAMS, MID_MAJOR_MARKETS } from './constants';
|
||||
import { AuctionParamArgs } from './types';
|
||||
import { COMMON_MATH, ENUM_UTILS } from '@drift/common';
|
||||
import { TakerFillVsOracleBpsRedisResult } from '../athena/repositories/fillQualityAnalytics';
|
||||
|
||||
export const GROUPING_OPTIONS = [1, 10, 100, 500, 1000];
|
||||
export const GROUPING_DEPENDENCIES = {
|
||||
@@ -312,10 +313,17 @@ export function publishGroupings(
|
||||
asks: aggregatedAsks,
|
||||
});
|
||||
|
||||
|
||||
if(['SOL-PERP', 'BTC-PERP', 'ETH-PERP'].includes(l2Formatted_grouped20.marketName) && aggregatedBids.length !== 20 || aggregatedAsks.length !== 20) {
|
||||
logger.error(`Error aggregating dlob levels: group=${group}, bids=${fullAggregatedBids.length}, asks=${fullAggregatedAsks.length}`)
|
||||
logger.error(`Response: ${JSON.stringify(l2Formatted_grouped20)}`)
|
||||
if (
|
||||
(['SOL-PERP', 'BTC-PERP', 'ETH-PERP'].includes(
|
||||
l2Formatted_grouped20.marketName
|
||||
) &&
|
||||
aggregatedBids.length !== 20) ||
|
||||
aggregatedAsks.length !== 20
|
||||
) {
|
||||
logger.error(
|
||||
`Error aggregating dlob levels: group=${group}, bids=${fullAggregatedBids.length}, asks=${fullAggregatedAsks.length}`
|
||||
);
|
||||
logger.error(`Response: ${JSON.stringify(l2Formatted_grouped20)}`);
|
||||
}
|
||||
|
||||
redisClient.publish(
|
||||
@@ -911,6 +919,8 @@ export const getEstimatedPrices = async (
|
||||
* @param driftClient - DriftClient instance (optional, for price calculation)
|
||||
* @param fetchFromRedis - Redis fetch function (optional, for price calculation)
|
||||
* @param selectMostRecentBySlot - Slot selection function (optional, for price calculation)
|
||||
* @param fillQualityInfo - Fill quality analytics data (version 2 only)
|
||||
* @param apiVersion - API version (1 or 2)
|
||||
* @returns Object formatted for deriveMarketOrderParams function or error response
|
||||
*/
|
||||
export const mapToMarketOrderParams = async (
|
||||
@@ -920,7 +930,9 @@ export const mapToMarketOrderParams = async (
|
||||
key: string,
|
||||
selectionCriteria: (responses: any) => any
|
||||
) => Promise<any>,
|
||||
selectMostRecentBySlot?: (responses: any[]) => any
|
||||
selectMostRecentBySlot?: (responses: any[]) => any,
|
||||
fillQualityInfo?: TakerFillVsOracleBpsRedisResult,
|
||||
apiVersion: number = 1
|
||||
): Promise<{
|
||||
success: boolean;
|
||||
data?: {
|
||||
@@ -960,6 +972,15 @@ export const mapToMarketOrderParams = async (
|
||||
let estimatedPrices;
|
||||
let processedSlippageTolerance = params.slippageTolerance;
|
||||
|
||||
// Track debug info for logging
|
||||
let debugInfo = {
|
||||
originalOraclePrice: null as string | null,
|
||||
adjustedOraclePrice: null as string | null,
|
||||
isCrossed: false,
|
||||
fillQualityBps: null as number | null,
|
||||
appliedV2Adjustment: false,
|
||||
};
|
||||
|
||||
if (driftClient && fetchFromRedis && selectMostRecentBySlot) {
|
||||
// Get L2 orderbook data using the utility function
|
||||
const redisL2 = await fetchL2FromRedis(
|
||||
@@ -980,6 +1001,73 @@ export const mapToMarketOrderParams = async (
|
||||
redisL2
|
||||
);
|
||||
|
||||
// Store original oracle for debugging
|
||||
debugInfo.originalOraclePrice = estimatedPrices.oraclePrice.toString();
|
||||
|
||||
// VERSION 2: Adjust oracle price based on fill quality when orderbook is crossed
|
||||
if (apiVersion === 2 && fillQualityInfo && redisL2) {
|
||||
try {
|
||||
// Convert raw L2 to formatted L2 for cross detection
|
||||
const l2Formatted = convertRawL2ToBN(redisL2);
|
||||
|
||||
const isSpot = isVariant(marketType, 'spot');
|
||||
const oracleData = isSpot
|
||||
? driftClient.getOracleDataForSpotMarket(params.marketIndex)
|
||||
: driftClient.getOracleDataForPerpMarket(params.marketIndex);
|
||||
const oraclePrice = oracleData.price ?? ZERO;
|
||||
|
||||
// Detect if orderbook is crossed
|
||||
const spreadInfo = COMMON_MATH.calculateSpreadBidAskMark(
|
||||
l2Formatted,
|
||||
oraclePrice
|
||||
);
|
||||
|
||||
const isCrossed =
|
||||
spreadInfo.bestBidPrice &&
|
||||
spreadInfo.bestAskPrice &&
|
||||
spreadInfo.bestBidPrice.gte(spreadInfo.bestAskPrice);
|
||||
|
||||
debugInfo.isCrossed = isCrossed;
|
||||
|
||||
if (isCrossed) {
|
||||
// Get fill quality metric based on direction
|
||||
const fillQualityBpsStr =
|
||||
direction === PositionDirection.LONG
|
||||
? fillQualityInfo.takerBuyBpsFromOracle?.all
|
||||
: fillQualityInfo.takerSellBpsFromOracle?.all;
|
||||
|
||||
if (
|
||||
fillQualityBpsStr &&
|
||||
fillQualityBpsStr !== 'null' &&
|
||||
fillQualityBpsStr !== null
|
||||
) {
|
||||
const fillQualityBps = Math.round(
|
||||
parseFloat(fillQualityBpsStr) * 100
|
||||
); // mul 100 to maintain 2 sigfigs (1.23 bps = 0.000123)
|
||||
debugInfo.fillQualityBps = fillQualityBps;
|
||||
|
||||
if (!isNaN(fillQualityBps)) {
|
||||
const adjustment = oraclePrice
|
||||
.muln(fillQualityBps)
|
||||
.divn(10000 * 100); // adjustmentFactor is in bps
|
||||
const adjustedOraclePrice = oraclePrice.add(adjustment);
|
||||
|
||||
// Update the oracle price in estimatedPrices
|
||||
estimatedPrices.oraclePrice = adjustedOraclePrice;
|
||||
debugInfo.adjustedOraclePrice = adjustedOraclePrice.toString();
|
||||
debugInfo.appliedV2Adjustment = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.warn(
|
||||
'Version 2: Failed to apply fill quality adjustment, using standard oracle:',
|
||||
error
|
||||
);
|
||||
// Fall through to use unadjusted oracle
|
||||
}
|
||||
}
|
||||
|
||||
// Handle dynamic slippage tolerance calculation if needed
|
||||
if (params.slippageTolerance === undefined) {
|
||||
// Convert raw L2 to formatted L2 for slippage calculation
|
||||
@@ -1028,6 +1116,62 @@ export const mapToMarketOrderParams = async (
|
||||
baseAmount = amount.mul(BASE_PRECISION).div(estimatedPrices.entryPrice);
|
||||
}
|
||||
|
||||
// Comprehensive debug logging
|
||||
logger.info(
|
||||
JSON.stringify({
|
||||
event: 'auction_params_calculated',
|
||||
requestParams: {
|
||||
marketIndex: params.marketIndex,
|
||||
marketType: params.marketType,
|
||||
direction: direction === PositionDirection.LONG ? 'long' : 'short',
|
||||
amount: params.amount,
|
||||
assetType: params.assetType,
|
||||
slippageTolerance: params.slippageTolerance,
|
||||
apiVersion,
|
||||
},
|
||||
priceDiscovery: {
|
||||
originalOraclePrice: debugInfo.originalOraclePrice,
|
||||
finalOraclePrice: estimatedPrices.oraclePrice.toString(),
|
||||
bestPrice: estimatedPrices.bestPrice.toString(),
|
||||
entryPrice: estimatedPrices.entryPrice.toString(),
|
||||
worstPrice: estimatedPrices.worstPrice.toString(),
|
||||
markPrice: estimatedPrices.markPrice.toString(),
|
||||
priceImpactBps: estimatedPrices.priceImpact.toString(),
|
||||
},
|
||||
v2CrossDetection:
|
||||
apiVersion === 2
|
||||
? {
|
||||
isCrossed: debugInfo.isCrossed,
|
||||
fillQualityBps: debugInfo.fillQualityBps,
|
||||
adjustedOraclePrice: debugInfo.adjustedOraclePrice,
|
||||
appliedAdjustment: debugInfo.appliedV2Adjustment,
|
||||
fillQualityData: fillQualityInfo
|
||||
? {
|
||||
takerBuyBpsAll: fillQualityInfo.takerBuyBpsFromOracle?.all,
|
||||
takerSellBpsAll:
|
||||
fillQualityInfo.takerSellBpsFromOracle?.all,
|
||||
}
|
||||
: null,
|
||||
}
|
||||
: undefined,
|
||||
auctionConfig: {
|
||||
duration: params.auctionDuration,
|
||||
startPriceOffset: params.auctionStartPriceOffset,
|
||||
startPriceOffsetFrom: params.auctionStartPriceOffsetFrom,
|
||||
endPriceOffset: params.auctionEndPriceOffset,
|
||||
endPriceOffsetFrom: params.auctionEndPriceOffsetFrom,
|
||||
slippageTolerance: processedSlippageTolerance,
|
||||
isOracleOrder: params.isOracleOrder,
|
||||
reduceOnly: params.reduceOnly ?? false,
|
||||
allowInfSlippage: params.allowInfSlippage ?? false,
|
||||
},
|
||||
calculatedValues: {
|
||||
baseAmount: baseAmount.toString(),
|
||||
slippageToleranceFinal: processedSlippageTolerance,
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
|
||||
Reference in New Issue
Block a user