chore: use previous groupings for calc

This commit is contained in:
Jack Waller
2025-06-11 10:08:58 +10:00
parent a9bdf07cfa
commit acc695052d
3 changed files with 99 additions and 39 deletions

View File

@@ -24,13 +24,13 @@ import {
SubscriberLookup, SubscriberLookup,
addMarketSlotToResponse, addMarketSlotToResponse,
addOracletoResponse, addOracletoResponse,
aggregatePrices,
l2WithBNToStrings, l2WithBNToStrings,
parsePositiveIntArray, parsePositiveIntArray,
publishGroupings,
} from '../utils/utils'; } from '../utils/utils';
import { setHealthStatus, HEALTH_STATUS } from '../core/metrics'; import { setHealthStatus, HEALTH_STATUS } from '../core/metrics';
type wsMarketArgs = { export type wsMarketArgs = {
marketIndex: number; marketIndex: number;
marketType: MarketType; marketType: MarketType;
marketName: string; marketName: string;
@@ -39,12 +39,11 @@ type wsMarketArgs = {
numVammOrders?: number; numVammOrders?: number;
fallbackL2Generators?: L2OrderBookGenerator[]; fallbackL2Generators?: L2OrderBookGenerator[];
updateOnChange?: boolean; updateOnChange?: boolean;
tickSize?: BN tickSize?: BN;
}; };
require('dotenv').config(); require('dotenv').config();
export const GROUPING_OPTIONS = [1, 10, 100, 500, 1000];
const PERP_MAKRET_STALENESS_THRESHOLD = 30 * 60 * 1000; const PERP_MAKRET_STALENESS_THRESHOLD = 30 * 60 * 1000;
const SPOT_MAKRET_STALENESS_THRESHOLD = 60 * 60 * 1000; const SPOT_MAKRET_STALENESS_THRESHOLD = 60 * 60 * 1000;
const STALE_ORACLE_REMOVE_VAMM_THRESHOLD = 160; const STALE_ORACLE_REMOVE_VAMM_THRESHOLD = 160;
@@ -120,7 +119,7 @@ export class DLOBSubscriberIO extends DLOBSubscriber {
includeVamm, includeVamm,
updateOnChange: false, updateOnChange: false,
fallbackL2Generators: [], fallbackL2Generators: [],
tickSize: perpMarket?.amm?.orderTickSize ?? ONE tickSize: perpMarket?.amm?.orderTickSize ?? ONE,
}); });
} }
for (const market of config.spotMarketInfos) { for (const market of config.spotMarketInfos) {
@@ -135,7 +134,8 @@ export class DLOBSubscriberIO extends DLOBSubscriber {
config.spotMarketSubscribers[market.marketIndex].phoenix, config.spotMarketSubscribers[market.marketIndex].phoenix,
config.spotMarketSubscribers[market.marketIndex].openbook, config.spotMarketSubscribers[market.marketIndex].openbook,
].filter((a) => !!a), ].filter((a) => !!a),
tickSize: config.spotMarketSubscribers[market.marketIndex].tickSize ?? ONE tickSize:
config.spotMarketSubscribers[market.marketIndex].tickSize ?? ONE,
}); });
} }
} }
@@ -400,29 +400,14 @@ export class DLOBSubscriberIO extends DLOBSubscriber {
l2Formatted_depth100 l2Formatted_depth100
); );
GROUPING_OPTIONS.forEach(group => { publishGroupings(
const pricePrecision = BigNum.from(group).mul(marketArgs.tickSize).toNum() l2Formatted,
const aggregatedBids = aggregatePrices(l2Formatted.bids, 'bid', pricePrecision) marketArgs,
.sort((a, b) => b[0] - a[0]) this.redisClient,
.slice(0, 20) clientPrefix,
marketType,
const aggregatedAsks = aggregatePrices(l2Formatted.asks, 'ask', pricePrecision) this.indicativeQuotesRedisClient
.sort((a, b) => a[0] - b[0])
.slice(0, 20)
const l2Formatted_grouped20 = Object.assign({}, l2Formatted, {
bids: aggregatedBids,
asks: aggregatedAsks,
});
this.redisClient.publish(
`${clientPrefix}orderbook_${marketType}_${marketArgs.marketIndex}_grouped_${group}${
this.indicativeQuotesRedisClient ? '_indicative' : ''
}`,
l2Formatted_grouped20
); );
})
if (!this.indicativeQuotesRedisClient) { if (!this.indicativeQuotesRedisClient) {
const bids = this.dlob const bids = this.dlob

View File

@@ -1,8 +1,8 @@
import { import {
BN, BN,
BigNum,
DriftClient, DriftClient,
DriftEnv, DriftEnv,
L2Level,
L2OrderBook, L2OrderBook,
L3OrderBook, L3OrderBook,
MarketType, MarketType,
@@ -21,6 +21,16 @@ import { logger } from './logger';
import { NextFunction, Request, Response } from 'express'; import { NextFunction, Request, Response } from 'express';
import FEATURE_FLAGS from './featureFlags'; import FEATURE_FLAGS from './featureFlags';
import { Connection } from '@solana/web3.js'; import { Connection } from '@solana/web3.js';
import { wsMarketArgs } from 'src/dlob-subscriber/DLOBSubscriberIO';
export const GROUPING_OPTIONS = [1, 10, 100, 500, 1000];
export const GROUPING_DEPENDENCIES = {
1: null,
10: 1,
100: 10,
500: 100,
1000: 100,
};
export const l2WithBNToStrings = (l2: L2OrderBook): any => { export const l2WithBNToStrings = (l2: L2OrderBook): any => {
for (const key of Object.keys(l2)) { for (const key of Object.keys(l2)) {
@@ -181,7 +191,7 @@ export function aggregatePrices(entries, side, pricePrecision) {
const price = parseFloat(entry.price); const price = parseFloat(entry.price);
const data = { const data = {
size: parseFloat(entry.size), size: parseFloat(entry.size),
sources: entry.sources || {} sources: entry.sources || {},
}; };
let bucketPrice, displayPrice; let bucketPrice, displayPrice;
@@ -207,19 +217,85 @@ export function aggregatePrices(entries, side, pricePrecision) {
bucketData.size += data.size; bucketData.size += data.size;
if (data.sources) { if (data.sources) {
Object.entries(data.sources).forEach(([sourceKey, sourceSize]: [string, string]) => { Object.entries(data.sources).forEach(
([sourceKey, sourceSize]: [string, string]) => {
if (!bucketData.sources[sourceKey]) { if (!bucketData.sources[sourceKey]) {
bucketData.sources[sourceKey] = 0; bucketData.sources[sourceKey] = 0;
} }
bucketData.sources[sourceKey] += parseFloat(sourceSize); bucketData.sources[sourceKey] += parseFloat(sourceSize);
});
} }
);
}
}); });
return Array.from(result.values()); return Array.from(result.values());
} }
export function publishGroupings(
l2Formatted,
marketArgs: wsMarketArgs,
redisClient: RedisClient,
clientPrefix: string,
marketType: string,
indicativeQuotesRedisClient: RedisClient
) {
const groupingResults = new Map();
GROUPING_OPTIONS.forEach((group) => {
const pricePrecision = BigNum.from(group).mul(marketArgs.tickSize).toNum();
const dependency = GROUPING_DEPENDENCIES[group];
let fullAggregatedBids, fullAggregatedAsks;
if (dependency && groupingResults.has(dependency)) {
const previousResults = groupingResults.get(dependency);
fullAggregatedBids = aggregatePrices(
previousResults.bids,
'bid',
pricePrecision
).sort((a, b) => b[0] - a[0]);
fullAggregatedAsks = aggregatePrices(
previousResults.asks,
'ask',
pricePrecision
).sort((a, b) => a[0] - b[0]);
} else {
fullAggregatedBids = aggregatePrices(
l2Formatted.bids,
'bid',
pricePrecision
).sort((a, b) => b[0] - a[0]);
fullAggregatedAsks = aggregatePrices(
l2Formatted.asks,
'ask',
pricePrecision
).sort((a, b) => a[0] - b[0]);
}
groupingResults.set(group, {
bids: fullAggregatedBids,
asks: fullAggregatedAsks,
});
const aggregatedBids = fullAggregatedBids.slice(0, 20);
const aggregatedAsks = fullAggregatedAsks.slice(0, 20);
const l2Formatted_grouped20 = Object.assign({}, l2Formatted, {
bids: aggregatedBids,
asks: aggregatedAsks,
});
redisClient.publish(
`${clientPrefix}orderbook_${marketType}_${
marketArgs.marketIndex
}_grouped_${group}${indicativeQuotesRedisClient ? '_indicative' : ''}`,
l2Formatted_grouped20
);
});
}
/** /**
* Takes in a req.query like: `{ * Takes in a req.query like: `{
* marketName: 'SOL-PERP,BTC-PERP,ETH-PERP', * marketName: 'SOL-PERP,BTC-PERP,ETH-PERP',

View File

@@ -3,11 +3,10 @@ import express from 'express';
import * as http from 'http'; import * as http from 'http';
import compression from 'compression'; import compression from 'compression';
import { WebSocket, WebSocketServer } from 'ws'; import { WebSocket, WebSocketServer } from 'ws';
import { sleep, selectMostRecentBySlot } from './utils/utils'; import { sleep, selectMostRecentBySlot, GROUPING_OPTIONS } from './utils/utils';
import { register, Gauge, Counter } from 'prom-client'; import { register, Gauge, Counter } from 'prom-client';
import { DriftEnv, PerpMarkets, SpotMarkets } from '@drift-labs/sdk'; import { DriftEnv, PerpMarkets, SpotMarkets } from '@drift-labs/sdk';
import { RedisClient, RedisClientPrefix } from '@drift/common/clients'; import { RedisClient, RedisClientPrefix } from '@drift/common/clients';
import { GROUPING_OPTIONS } from './dlob-subscriber/DLOBSubscriberIO';
// Set up env constants // Set up env constants
require('dotenv').config(); require('dotenv').config();