diff --git a/src/publishers/tests/tradeMetricsProcessor.test.ts b/src/publishers/tests/tradeMetricsProcessor.test.ts index d34326b..cf17d6b 100644 --- a/src/publishers/tests/tradeMetricsProcessor.test.ts +++ b/src/publishers/tests/tradeMetricsProcessor.test.ts @@ -286,4 +286,82 @@ describe('tradeMetricsProcessor', () => { }, ]); }); + + it('does not emit maker_not_competitive for a fill maker without indicative quotes', async () => { + const metrics = createMetricSinks(); + const quoteState = new Map([ + ['market_mms_perp_0', ['quoted-maker']], + [ + 'mm_quotes_v2_perp_0_quoted-maker', + { + ts: 1710000000000, + quotes: [{ bid_price: 99900000, bid_size: 1000000000 }], + }, + ], + ]); + + const { processFillEvent } = createTradeMetricsProcessor({ + redisClientPrefix: 'dlob:', + indicativeQuoteMaxAgeMs: 1000, + indicativeQuotesCacheTtlMs: 250, + spotMarketPrecisionResolver: () => undefined, + publisherRedisClient: { + publish: async () => 1, + }, + indicativeQuotesRedisClient: { + smembers: async (key) => quoteState.get(key) ?? [], + get: async (key) => quoteState.get(key), + }, + metrics, + }); + + const fillEvent: FillEvent = { + ts: 1710000000000, + marketIndex: 0, + marketType: 'perp', + filler: 'mock-filler', + takerFee: 0, + makerFee: 0, + quoteAssetAmountSurplus: 0, + baseAssetAmountFilled: 1, + quoteAssetAmountFilled: 100, + taker: 'mock-taker', + takerOrderId: 1, + takerOrderDirection: 'short', + takerOrderBaseAssetAmount: 1, + takerOrderCumulativeBaseAssetAmountFilled: 1, + takerOrderCumulativeQuoteAssetAmountFilled: 100, + maker: 'external-maker', + makerOrderId: 2, + makerOrderDirection: 'long', + makerOrderBaseAssetAmount: 1, + makerOrderCumulativeBaseAssetAmountFilled: 1, + makerOrderCumulativeQuoteAssetAmountFilled: 100, + oraclePrice: 100, + txSig: 'mock-2', + slot: 2, + fillRecordId: 2, + action: 'fill', + actionExplanation: 'none', + referrerReward: 0, + bitFlags: 0, + }; + + await processFillEvent(fillEvent); + + expect(metrics.indicativeQuoteEvaluationCount.calls).not.toEqual( + expect.arrayContaining([ + { + value: 1, + attributes: { + maker: 'external-maker', + market_index: 0, + market_type: 'perp', + side: 'long', + result: 'maker_not_competitive', + }, + }, + ]) + ); + }); }); diff --git a/src/publishers/tradeMetricsProcessor.ts b/src/publishers/tradeMetricsProcessor.ts index ed7e026..6e99421 100644 --- a/src/publishers/tradeMetricsProcessor.ts +++ b/src/publishers/tradeMetricsProcessor.ts @@ -298,8 +298,15 @@ export const createTradeMetricsProcessor = ({ }); } + const fillMakerEvaluation = fillEvent.maker + ? marketQuoteEvaluations.find( + (evaluation) => evaluation.maker === fillEvent.maker + ) + : undefined; if ( fillEvent.maker && + fillMakerEvaluation && + fillMakerEvaluation.totalQuoteValueOnBook > 0 && !marketQuotes.find((quote) => quote.maker === fillEvent.maker) ) { metrics.indicativeQuoteEvaluationCount.add(1, {