add direction aware adjustments

This commit is contained in:
wphan
2025-10-22 23:50:24 -07:00
parent 838fa6951c
commit a314b5f1f8

View File

@@ -36,6 +36,7 @@ import { AuctionParamArgs } from './types';
import { COMMON_MATH, ENUM_UTILS } from '@drift/common';
import { TakerFillVsOracleBpsRedisResult } from '../athena/repositories/fillQualityAnalytics';
const MAX_FILL_QUALITY_AGE_MS = 10 * 60 * 1000; // 10 minutes
export const GROUPING_OPTIONS = [1, 10, 100, 500, 1000];
export const GROUPING_DEPENDENCIES = {
1: null,
@@ -979,6 +980,12 @@ export const mapToMarketOrderParams = async (
adjustedMarkPrice: null as string | null,
isCrossed: false,
fillQualityBps: null as number | null,
fillQualityDataStale: false,
priceReferenceUsed: null as string | null,
priceReferenceDistance: null as string | null,
reason: null as string | null,
markVsOracle: null as string | null,
isMarkFavorableForDirection: null as boolean | null,
appliedV2Adjustment: false,
};
@@ -1031,6 +1038,17 @@ export const mapToMarketOrderParams = async (
debugInfo.isCrossed = isCrossed;
if (isCrossed) {
// Check data staleness - ignore if older than 10 minutes
const dataAge = Date.now() - (fillQualityInfo.updatedAtTs || 0);
if (dataAge > MAX_FILL_QUALITY_AGE_MS) {
logger.warn(
`Version 2: Fill quality data is stale (${Math.round(
dataAge / 1000
)}s old), skipping adjustment for market ${params.marketIndex}`
);
debugInfo.fillQualityDataStale = true;
} else {
// Get fill quality metric based on direction
const fillQualityBpsStr =
direction === PositionDirection.LONG
@@ -1050,29 +1068,79 @@ export const mapToMarketOrderParams = async (
if (!isNaN(fillQualityBps)) {
const adjustment = oraclePrice
.muln(fillQualityBps)
.divn(10000 * 100); // adjustmentFactor is in bps
const adjustedOraclePrice = oraclePrice.add(adjustment);
.divn(10000 * 100);
const fillQualityAdjustedPrice = oraclePrice.add(adjustment);
// Update ALL relevant prices in estimatedPrices to maintain consistency
// This ensures auction parameters calculated from any price reference (oracle, mark, best, worst)
// will reflect the fill quality adjustment
estimatedPrices.oraclePrice = adjustedOraclePrice;
// Compare fill quality adjusted price vs mark price to determine which is better for takers
// We want to use the price that gets takers closer to where makers have been filling
// AND consider whether the mark price is favorable for the taker's direction
const markPrice = estimatedPrices.markPrice;
const originalOraclePrice = oraclePrice;
// Determine if mark price favors this direction
const markVsOracle = markPrice.sub(originalOraclePrice);
const isMarkFavorableForDirection =
direction === PositionDirection.LONG
? markVsOracle.lt(ZERO) // Mark below oracle is good for longs
: markVsOracle.gt(ZERO); // Mark above oracle is good for shorts
// Calculate distances from each price to the fill quality adjusted price
// The fill quality adjusted price represents where makers have been filling
const distanceFromMark = markPrice
.sub(fillQualityAdjustedPrice)
.abs();
const distanceFromOracle = originalOraclePrice
.sub(fillQualityAdjustedPrice)
.abs();
// Decision logic: prefer fill quality adjusted price when:
// 1. It's closer to the target, OR
// 2. Mark price is unfavorable for this direction
if (
distanceFromOracle.lt(distanceFromMark) ||
!isMarkFavorableForDirection
) {
// Use fill quality adjusted price
estimatedPrices.markPrice = fillQualityAdjustedPrice;
debugInfo.priceReferenceUsed = 'oracleAdjusted';
debugInfo.priceReferenceDistance =
distanceFromOracle.toString();
debugInfo.reason = isMarkFavorableForDirection
? 'closerToTarget'
: 'markUnfavorable';
} else {
// Use mark price
debugInfo.priceReferenceUsed = 'mark';
debugInfo.priceReferenceDistance =
distanceFromMark.toString();
debugInfo.reason = 'markFavorableAndCloser';
}
// Store additional debug info
debugInfo.markVsOracle = markVsOracle.toString();
debugInfo.isMarkFavorableForDirection =
isMarkFavorableForDirection;
// Always update oracle price with fill quality adjustment for consistency
estimatedPrices.oraclePrice = fillQualityAdjustedPrice;
// Update other prices to maintain consistency
estimatedPrices.bestPrice =
estimatedPrices.bestPrice.add(adjustment);
estimatedPrices.entryPrice =
estimatedPrices.entryPrice.add(adjustment);
estimatedPrices.worstPrice =
estimatedPrices.worstPrice.add(adjustment);
estimatedPrices.markPrice =
estimatedPrices.markPrice.add(adjustment);
debugInfo.adjustedOraclePrice = adjustedOraclePrice.toString();
debugInfo.adjustedOraclePrice =
fillQualityAdjustedPrice.toString();
debugInfo.adjustedMarkPrice =
estimatedPrices.markPrice.toString();
debugInfo.appliedV2Adjustment = true;
}
}
}
}
} catch (error) {
logger.warn(
'Version 2: Failed to apply fill quality adjustment, using standard oracle:',
@@ -1160,11 +1228,19 @@ export const mapToMarketOrderParams = async (
adjustedOraclePrice: debugInfo.adjustedOraclePrice,
adjustedMarkPrice: debugInfo.adjustedMarkPrice,
appliedAdjustment: debugInfo.appliedV2Adjustment,
fillQualityDataStale: debugInfo.fillQualityDataStale,
priceReferenceUsed: debugInfo.priceReferenceUsed,
priceReferenceDistance: debugInfo.priceReferenceDistance,
reason: debugInfo.reason,
markVsOracle: debugInfo.markVsOracle,
isMarkFavorableForDirection:
debugInfo.isMarkFavorableForDirection,
fillQualityData: fillQualityInfo
? {
takerBuyBpsAll: fillQualityInfo.takerBuyBpsFromOracle?.all,
takerSellBpsAll:
fillQualityInfo.takerSellBpsFromOracle?.all,
updatedAtTs: fillQualityInfo.updatedAtTs,
}
: null,
}