send indicative quotes to mainnet (#374)
* Bumping drift-common to ab538e0c1efbf64a3dfecf2af16e5a75aab81400 * Bumping drift-common to cf7f6ec63febe0d3b661f187e4e26e729797c4d0 * Bumping drift-common to 149f5efe5444d89b53b5370dcea4a3115fec8c5f * Bumping drift-common to c879f8a85bfcbaef1f1c6d9c6bf4a05e8970fa0a * Bumping drift-common to d089c9ed7e7400a0f3ea98a0184a4e3bf273fcf5 * dlob add fetch indicative quotes (#372) * dlob add fetch indicative quotes * indicative ws stream * prettify * allow for oracle orders * Bumping drift-common to 56331c89a2285a04cc86a639e0fceeda03366f43 * add console log for debugging indicative * add indicative in l2 queries * add more console logging for debugging * bump common and remove logging * Bumping drift-common to 43104f473f43111f8cc34733a43a85b5fef7f778 * Bumping drift-common to 6d75612fa1fab858daa90cdb91f0c8c9edbc8242 * Bumping drift-common to 3a42e9665c04a27bcf92e7faa50c168690312880 * Bumping drift-common to 20351d45f2644fb7194ffce441d3d2cb8d6c6b4b * Bumping drift-common to 04216dfc03aa659db24fc24ba42aa8a4e9a8bb9a * Bumping drift-common to ad20bbe9d3a262c547f66ec464306fabac539fd8 * Bumping drift-common to 5539ad5f3849b19e1b7106f783a16a182fc9a411 * Bumping drift-common to 8b7e118f1aed9d1d7a786cd165f0ceef2a07297c * Bumping drift-common to 52b7948cebb0ed6bc1f8278b0f549366a1be3ba0 * Bumping drift-common to 26a603c2d78f7f662cb872075bdff1fd6b75ec43 * Bumping drift-common to 683bfc5cefe5097a257511507f22831f185f1549 * Bumping drift-common to b66eaade382a58b563fdc5ee6c03bc98e5bb2195 * Bumping drift-common to 9a48bea13265723c5d17377e68aaf9984b24c722 * Bumping drift-common to a147ab129f6eb59d77889ac2b7a915ca910d83dd * Bumping drift-common to 65891abfcd5f59c726aecb211ae77ca38394bdf1 * Bumping drift-common to 540cdbbc9b322990d288af8f4f3183f2c96087b7 * Bumping drift-common to 978b8a03b59d0dc24d1269f319ff6a7ff711775a * Bumping drift-common to af74d01953db3683bca585c2111910cbbe171a72 * Bumping drift-common to cd684e22082438f0cb784ec234fd9e5d3cd63bb2 --------- Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
@@ -1,11 +1,17 @@
|
||||
import {
|
||||
BN,
|
||||
DLOBSubscriber,
|
||||
DLOBSubscriptionConfig,
|
||||
DriftEnv,
|
||||
L2OrderBookGenerator,
|
||||
MarketType,
|
||||
Order,
|
||||
OrderStatus,
|
||||
OrderTriggerCondition,
|
||||
OrderType,
|
||||
PerpOperation,
|
||||
PositionDirection,
|
||||
ZERO,
|
||||
isOperationPaused,
|
||||
isVariant,
|
||||
} from '@drift-labs/sdk';
|
||||
@@ -37,6 +43,8 @@ const PERP_MAKRET_STALENESS_THRESHOLD = 30 * 60 * 1000;
|
||||
const SPOT_MAKRET_STALENESS_THRESHOLD = 60 * 60 * 1000;
|
||||
const STALE_ORACLE_REMOVE_VAMM_THRESHOLD = 160;
|
||||
|
||||
const INDICATIVE_QUOTES_PUBKEY = 'inDNdu3ML4vG5LNExqcwuCQtLcCU8KfK5YM2qYV3JJz';
|
||||
|
||||
const PERP_MARKETS_TO_SKIP_SLOT_CHECK =
|
||||
process.env.PERP_MARKETS_TO_SKIP_SLOT_CHECK !== undefined
|
||||
? parsePositiveIntArray(process.env.PERP_MARKETS_TO_SKIP_SLOT_CHECK)
|
||||
@@ -55,6 +63,7 @@ export class DLOBSubscriberIO extends DLOBSubscriber {
|
||||
public marketArgs: wsMarketArgs[] = [];
|
||||
public lastSeenL2Formatted: Map<MarketType, Map<number, any>>;
|
||||
redisClient: RedisClient;
|
||||
indicativeQuotesRedisClient?: RedisClient;
|
||||
public killSwitchSlotDiffThreshold: number;
|
||||
public lastMarketSlotMap: Map<
|
||||
MarketType,
|
||||
@@ -65,6 +74,7 @@ export class DLOBSubscriberIO extends DLOBSubscriber {
|
||||
config: DLOBSubscriptionConfig & {
|
||||
env: DriftEnv;
|
||||
redisClient: RedisClient;
|
||||
indicativeQuotesRedisClient?: RedisClient;
|
||||
perpMarketInfos: wsMarketInfo[];
|
||||
spotMarketInfos: wsMarketInfo[];
|
||||
spotMarketSubscribers: SubscriberLookup;
|
||||
@@ -73,6 +83,8 @@ export class DLOBSubscriberIO extends DLOBSubscriber {
|
||||
) {
|
||||
super(config);
|
||||
this.redisClient = config.redisClient;
|
||||
this.indicativeQuotesRedisClient = config.indicativeQuotesRedisClient;
|
||||
|
||||
this.killSwitchSlotDiffThreshold =
|
||||
config.killSwitchSlotDiffThreshold || 200;
|
||||
|
||||
@@ -124,6 +136,105 @@ export class DLOBSubscriberIO extends DLOBSubscriber {
|
||||
await super.updateDLOB();
|
||||
for (const marketArgs of this.marketArgs) {
|
||||
try {
|
||||
if (this.indicativeQuotesRedisClient) {
|
||||
const marketType = isVariant(marketArgs.marketType, 'perp')
|
||||
? 'perp'
|
||||
: 'spot';
|
||||
|
||||
const mms = await this.indicativeQuotesRedisClient.smembers(
|
||||
`market_mms_${marketType}_${marketArgs.marketIndex}`
|
||||
);
|
||||
const mmQuotes = await Promise.all(
|
||||
mms.map((mm) => {
|
||||
return this.indicativeQuotesRedisClient.get(
|
||||
`mm_quotes_${marketType}_${marketArgs.marketIndex}_${mm}`
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
const nowMinus1000Ms = Date.now() - 1000;
|
||||
mmQuotes.forEach((quote) => {
|
||||
if (Number(quote['ts']) > nowMinus1000Ms) {
|
||||
const indicativeBaseOrder: Order = {
|
||||
status: OrderStatus.OPEN,
|
||||
orderType: OrderType.LIMIT,
|
||||
orderId: 0,
|
||||
slot: new BN(this.slotSource.getSlot()),
|
||||
marketIndex: marketArgs.marketIndex,
|
||||
marketType: marketArgs.marketType,
|
||||
baseAssetAmount: ZERO,
|
||||
immediateOrCancel: false,
|
||||
direction: PositionDirection.LONG,
|
||||
oraclePriceOffset: 0,
|
||||
maxTs: new BN(quote['ts'] + 1000),
|
||||
reduceOnly: false,
|
||||
triggerCondition: OrderTriggerCondition.ABOVE,
|
||||
price: ZERO,
|
||||
userOrderId: 0,
|
||||
postOnly: true,
|
||||
auctionDuration: 0,
|
||||
auctionStartPrice: ZERO,
|
||||
auctionEndPrice: ZERO,
|
||||
// Rest are not necessary and set for type conforming
|
||||
existingPositionDirection: PositionDirection.LONG,
|
||||
triggerPrice: ZERO,
|
||||
baseAssetAmountFilled: ZERO,
|
||||
quoteAssetAmountFilled: ZERO,
|
||||
quoteAssetAmount: ZERO,
|
||||
bitFlags: 0,
|
||||
postedSlotTail: 0,
|
||||
};
|
||||
|
||||
if (quote['bid_size'] && quote['bid_price']) {
|
||||
// Sanity check bid price and size
|
||||
|
||||
const indicativeBid: Order = Object.assign(
|
||||
{},
|
||||
indicativeBaseOrder,
|
||||
{
|
||||
oraclePriceOffset: quote['is_oracle_offset']
|
||||
? quote['bid_price']
|
||||
: 0,
|
||||
price: quote['is_oracle_offset']
|
||||
? 0
|
||||
: new BN(quote['bid_price']),
|
||||
baseAssetAmount: new BN(quote['bid_size']),
|
||||
direction: PositionDirection.LONG,
|
||||
}
|
||||
);
|
||||
this.dlob.insertOrder(
|
||||
indicativeBid,
|
||||
INDICATIVE_QUOTES_PUBKEY,
|
||||
this.slotSource.getSlot(),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
if (quote['ask_size'] && quote['ask_price']) {
|
||||
const indicativeAsk: Order = Object.assign(
|
||||
{},
|
||||
indicativeBaseOrder,
|
||||
{
|
||||
oraclePriceOffset: quote['is_oracle_offset']
|
||||
? quote['ask_price']
|
||||
: 0,
|
||||
price: quote['is_oracle_offset']
|
||||
? 0
|
||||
: new BN(quote['ask_price']),
|
||||
baseAssetAmount: new BN(quote['ask_size']),
|
||||
direction: PositionDirection.SHORT,
|
||||
}
|
||||
);
|
||||
this.dlob.insertOrder(
|
||||
indicativeAsk,
|
||||
INDICATIVE_QUOTES_PUBKEY,
|
||||
this.slotSource.getSlot(),
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
this.getL2AndSendMsg(marketArgs);
|
||||
this.getL3AndSendMsg(marketArgs);
|
||||
} catch (error) {
|
||||
@@ -162,6 +273,7 @@ export class DLOBSubscriberIO extends DLOBSubscriber {
|
||||
logger.info('Oracle is stale, removing vamm orders');
|
||||
includeVamm = false;
|
||||
}
|
||||
|
||||
const l2 = this.getL2({ ...l2FuncArgs, includeVamm });
|
||||
const slot = l2.slot;
|
||||
const lastMarketSlotAndTime = this.lastMarketSlotMap
|
||||
@@ -264,44 +376,43 @@ export class DLOBSubscriberIO extends DLOBSubscriber {
|
||||
|
||||
this.redisClient.publish(
|
||||
`${clientPrefix}orderbook_${marketType}_${marketArgs.marketIndex}${
|
||||
this.protectedMakerView ? '_pmm' : ''
|
||||
this.indicativeQuotesRedisClient ? '_indicative' : ''
|
||||
}`,
|
||||
l2Formatted
|
||||
);
|
||||
this.redisClient.set(
|
||||
`last_update_orderbook_${marketType}_${marketArgs.marketIndex}${
|
||||
this.protectedMakerView ? '_pmm' : ''
|
||||
this.indicativeQuotesRedisClient ? '_indicative' : ''
|
||||
}`,
|
||||
l2Formatted_depth100
|
||||
);
|
||||
const oraclePriceData =
|
||||
marketType === 'spot'
|
||||
? this.driftClient.getOracleDataForSpotMarket(marketArgs.marketIndex)
|
||||
: this.driftClient.getOracleDataForPerpMarket(marketArgs.marketIndex);
|
||||
const bids = this.dlob
|
||||
.getBestMakers({
|
||||
marketIndex: marketArgs.marketIndex,
|
||||
marketType: marketArgs.marketType,
|
||||
direction: PositionDirection.LONG,
|
||||
slot: slot,
|
||||
oraclePriceData,
|
||||
numMakers: 4,
|
||||
})
|
||||
.map((x) => x.toString());
|
||||
const asks = this.dlob
|
||||
.getBestMakers({
|
||||
marketIndex: marketArgs.marketIndex,
|
||||
marketType: marketArgs.marketType,
|
||||
direction: PositionDirection.SHORT,
|
||||
slot,
|
||||
oraclePriceData,
|
||||
numMakers: 4,
|
||||
})
|
||||
.map((x) => x.toString());
|
||||
this.redisClient.set(
|
||||
`last_update_orderbook_best_makers_${marketType}_${marketArgs.marketIndex}`,
|
||||
{ bids, asks, slot }
|
||||
);
|
||||
|
||||
if (!this.indicativeQuotesRedisClient) {
|
||||
const bids = this.dlob
|
||||
.getBestMakers({
|
||||
marketIndex: marketArgs.marketIndex,
|
||||
marketType: marketArgs.marketType,
|
||||
direction: PositionDirection.LONG,
|
||||
slot: slot,
|
||||
oraclePriceData: oracleData,
|
||||
numMakers: 4,
|
||||
})
|
||||
.map((x) => x.toString());
|
||||
const asks = this.dlob
|
||||
.getBestMakers({
|
||||
marketIndex: marketArgs.marketIndex,
|
||||
marketType: marketArgs.marketType,
|
||||
direction: PositionDirection.SHORT,
|
||||
slot,
|
||||
oraclePriceData: oracleData,
|
||||
numMakers: 4,
|
||||
})
|
||||
.map((x) => x.toString());
|
||||
this.redisClient.set(
|
||||
`last_update_orderbook_best_makers_${marketType}_${marketArgs.marketIndex}`,
|
||||
{ bids, asks, slot }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
getL3AndSendMsg(marketArgs: wsMarketArgs): void {
|
||||
@@ -355,7 +466,7 @@ export class DLOBSubscriberIO extends DLOBSubscriber {
|
||||
|
||||
this.redisClient.set(
|
||||
`last_update_orderbook_l3_${marketType}_${marketArgs.marketIndex}${
|
||||
this.protectedMakerView ? '_pmm' : ''
|
||||
this.indicativeQuotesRedisClient ? '_indicative' : ''
|
||||
}`,
|
||||
l3
|
||||
);
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
import { DLOB, OrderSubscriber, UserAccount, UserMap } from '@drift-labs/sdk';
|
||||
import {
|
||||
DLOB,
|
||||
OrderSubscriber,
|
||||
ProtectMakerParamsMap,
|
||||
UserAccount,
|
||||
UserMap,
|
||||
} from '@drift-labs/sdk';
|
||||
import { PublicKey } from '@solana/web3.js';
|
||||
|
||||
export type DLOBProvider = {
|
||||
subscribe(): Promise<void>;
|
||||
getDLOB(slot: number, protectedMakerView: boolean): Promise<DLOB>;
|
||||
getDLOB(
|
||||
slot: number,
|
||||
protectedMakerParamsMap?: ProtectMakerParamsMap
|
||||
): Promise<DLOB>;
|
||||
getUniqueAuthorities(): PublicKey[];
|
||||
getUserAccounts(): Generator<{
|
||||
userAccount: UserAccount;
|
||||
@@ -20,8 +29,11 @@ export function getDLOBProviderFromUserMap(userMap: UserMap): DLOBProvider {
|
||||
subscribe: async () => {
|
||||
await userMap.subscribe();
|
||||
},
|
||||
getDLOB: async (slot: number, protectedMakerView: boolean) => {
|
||||
return await userMap.getDLOB(slot, protectedMakerView);
|
||||
getDLOB: async (
|
||||
slot: number,
|
||||
protectedMakerParamsMap?: ProtectMakerParamsMap
|
||||
) => {
|
||||
return await userMap.getDLOB(slot, protectedMakerParamsMap);
|
||||
},
|
||||
getUniqueAuthorities: () => {
|
||||
return userMap.getUniqueAuthorities();
|
||||
@@ -56,8 +68,11 @@ export function getDLOBProviderFromOrderSubscriber(
|
||||
subscribe: async () => {
|
||||
await orderSubscriber.subscribe();
|
||||
},
|
||||
getDLOB: async (slot: number, protectedMakerView: boolean) => {
|
||||
return await orderSubscriber.getDLOB(slot, protectedMakerView);
|
||||
getDLOB: async (
|
||||
slot: number,
|
||||
protectedMakerParamsMap?: ProtectMakerParamsMap
|
||||
) => {
|
||||
return await orderSubscriber.getDLOB(slot, protectedMakerParamsMap);
|
||||
},
|
||||
getUniqueAuthorities: () => {
|
||||
const authorities = new Set<string>();
|
||||
|
||||
26
src/index.ts
26
src/index.ts
@@ -521,7 +521,7 @@ const main = async (): Promise<void> => {
|
||||
|
||||
app.get('/l2', async (req, res, next) => {
|
||||
try {
|
||||
const { marketName, marketIndex, marketType, depth, includePmm } =
|
||||
const { marketName, marketIndex, marketType, depth, includeIndicative } =
|
||||
req.query;
|
||||
|
||||
const { normedMarketType, normedMarketIndex, error } = validateDlobQuery(
|
||||
@@ -537,14 +537,15 @@ const main = async (): Promise<void> => {
|
||||
}
|
||||
|
||||
const isSpot = isVariant(normedMarketType, 'spot');
|
||||
const includePmmStr = (includePmm as string)?.toLowerCase() === 'true';
|
||||
const includeIndicativeStr =
|
||||
(includeIndicative as string)?.toLowerCase() === 'true';
|
||||
const adjustedDepth = depth ?? '100';
|
||||
|
||||
let l2Formatted: any;
|
||||
const redisL2 = await fetchFromRedis(
|
||||
`last_update_orderbook_${
|
||||
isSpot ? 'spot' : 'perp'
|
||||
}_${normedMarketIndex}${includePmmStr ? '_pmm' : ''}`,
|
||||
}_${normedMarketIndex}${includeIndicativeStr ? '_indicative' : ''}`,
|
||||
selectMostRecentBySlot
|
||||
);
|
||||
const depthToUse = Math.min(parseInt(adjustedDepth as string) ?? 1, 100);
|
||||
@@ -606,7 +607,7 @@ const main = async (): Promise<void> => {
|
||||
includePhoenix,
|
||||
includeOpenbook,
|
||||
includeOracle,
|
||||
includePmm,
|
||||
includeIndicative,
|
||||
} = req.query;
|
||||
|
||||
const normedParams = normalizeBatchQueryParams({
|
||||
@@ -618,7 +619,7 @@ const main = async (): Promise<void> => {
|
||||
includePhoenix: includePhoenix as string | undefined,
|
||||
includeOpenbook: includeOpenbook as string | undefined,
|
||||
includeOracle: includeOracle as string | undefined,
|
||||
includePmm: includePmm as string | undefined,
|
||||
includeIndicative: includeIndicative as string | undefined,
|
||||
});
|
||||
|
||||
if (normedParams === undefined) {
|
||||
@@ -649,14 +650,17 @@ const main = async (): Promise<void> => {
|
||||
}
|
||||
|
||||
const isSpot = isVariant(normedMarketType, 'spot');
|
||||
const normedIncludePmm = normedParam['includePmm'] == 'true';
|
||||
const normedIncludeIndicative =
|
||||
normedParam['includeIndicative'] == 'true';
|
||||
|
||||
const adjustedDepth = normedParam['depth'] ?? '100';
|
||||
let l2Formatted: any;
|
||||
const redisL2 = await fetchFromRedis(
|
||||
`last_update_orderbook_${
|
||||
isSpot ? 'spot' : 'perp'
|
||||
}_${normedMarketIndex}${normedIncludePmm ? '_pmm' : ''}`,
|
||||
}_${normedMarketIndex}${
|
||||
normedIncludeIndicative ? '_indicative' : ''
|
||||
}`,
|
||||
selectMostRecentBySlot
|
||||
);
|
||||
const depth = Math.min(parseInt(adjustedDepth as string) ?? 1, 100);
|
||||
@@ -726,7 +730,8 @@ const main = async (): Promise<void> => {
|
||||
|
||||
app.get('/l3', async (req, res, next) => {
|
||||
try {
|
||||
const { marketName, marketIndex, marketType, includePmm } = req.query;
|
||||
const { marketName, marketIndex, marketType, includeIndicative } =
|
||||
req.query;
|
||||
|
||||
const { normedMarketType, normedMarketIndex, error } = validateDlobQuery(
|
||||
driftClient,
|
||||
@@ -741,11 +746,12 @@ const main = async (): Promise<void> => {
|
||||
}
|
||||
|
||||
const marketTypeStr = getVariant(normedMarketType);
|
||||
const normedIncludePmm = (includePmm as string)?.toLowerCase() === 'true';
|
||||
const normedIncludeIndicative =
|
||||
(includeIndicative as string)?.toLowerCase() === 'true';
|
||||
|
||||
const redisL3 = await fetchFromRedis(
|
||||
`last_update_orderbook_l3_${marketTypeStr}_${normedMarketIndex}${
|
||||
normedIncludePmm ? '_pmm' : ''
|
||||
normedIncludeIndicative ? '_indicative' : ''
|
||||
}`,
|
||||
selectMostRecentBySlot
|
||||
);
|
||||
|
||||
@@ -313,6 +313,9 @@ const main = async () => {
|
||||
});
|
||||
await redisClient.connect();
|
||||
|
||||
const indicativeRedisClient = new RedisClient({});
|
||||
await indicativeRedisClient.connect();
|
||||
|
||||
const connection = new Connection(endpoint, {
|
||||
wsEndpoint: wsEndpoint,
|
||||
commitment: stateCommitment,
|
||||
@@ -506,7 +509,7 @@ const main = async () => {
|
||||
});
|
||||
await dlobSubscriber.subscribe();
|
||||
|
||||
const dlobSubscriberPmm = new DLOBSubscriberIO({
|
||||
const dlobSubscriberIndicative = new DLOBSubscriberIO({
|
||||
driftClient,
|
||||
env: driftEnv,
|
||||
dlobSource: dlobProvider,
|
||||
@@ -518,8 +521,9 @@ const main = async () => {
|
||||
spotMarketInfos,
|
||||
killSwitchSlotDiffThreshold: KILLSWITCH_SLOT_DIFF_THRESHOLD,
|
||||
protectedMakerView: true,
|
||||
indicativeQuotesRedisClient: indicativeRedisClient,
|
||||
});
|
||||
await dlobSubscriberPmm.subscribe();
|
||||
await dlobSubscriberIndicative.subscribe();
|
||||
|
||||
if (useWebsocket && !FEATURE_FLAGS.DISABLE_GPA_REFRESH) {
|
||||
const recursiveFetch = (delay = WS_FALLBACK_FETCH_INTERVAL) => {
|
||||
@@ -595,7 +599,6 @@ const main = async () => {
|
||||
|
||||
const handleDebug = async (req: Request, res: Response) => {
|
||||
const marketIndex = +req.query.marketIndex;
|
||||
const protectedMakerView = req.query.includePmm === 'true';
|
||||
let marketType: MarketType = MarketType.PERP;
|
||||
let oraclePriceData: OraclePriceData;
|
||||
if (req.query.marketType === 'spot') {
|
||||
@@ -606,7 +609,7 @@ const main = async () => {
|
||||
}
|
||||
try {
|
||||
const slot = slotSource.getSlot();
|
||||
const dlob = await dlobProvider.getDLOB(slot, protectedMakerView);
|
||||
const dlob = await dlobProvider.getDLOB(slot);
|
||||
const l2 = dlob.getL2({
|
||||
marketIndex,
|
||||
marketType,
|
||||
@@ -629,7 +632,6 @@ const main = async () => {
|
||||
},
|
||||
l2: l2WithBNToStrings(l2),
|
||||
l3,
|
||||
includePmm: protectedMakerView,
|
||||
};
|
||||
|
||||
res.json(state);
|
||||
|
||||
@@ -95,6 +95,8 @@ const getRedisChannelFromMessage = (message: any): string => {
|
||||
return `trades_${marketType}_${marketIndex}`;
|
||||
case 'orderbook':
|
||||
return `orderbook_${marketType}_${marketIndex}`;
|
||||
case 'orderbook_indicative':
|
||||
return `orderbook_${marketType}_${marketIndex}_indicative`;
|
||||
case 'priorityfees':
|
||||
return `priorityFees_${marketType}_${marketIndex}`;
|
||||
case undefined:
|
||||
|
||||
Reference in New Issue
Block a user