Skip to content

Commit

Permalink
[IND-450]: Prevent orders transitioning from canceled to best effort …
Browse files Browse the repository at this point in the history
…canceled (#753)

* [IND-450]: Prevent orders transitioning from canceled to best effort canceled

* fix liquidations

* nits

* lint
  • Loading branch information
vincentwschau committed Nov 10, 2023
1 parent 0cd5cd1 commit ff97fca
Show file tree
Hide file tree
Showing 15 changed files with 509 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ import {
CANCELED_ORDER_WINDOW_SIZE,
isOrderCanceled,
addCanceledOrderId,
removeOrderFromCache,
removeOrderFromCaches,
getOrderCanceledStatus,
addBestEffortCanceledOrderId,
} from '../../src/caches/canceled-orders-cache';
import { CanceledOrderStatus } from '../../src';

describe('cancelledOrdersCache', () => {
const openOrderId1: string = 'order1';
Expand Down Expand Up @@ -40,13 +43,13 @@ describe('cancelledOrdersCache', () => {
expect(isCanceled1).toEqual(true);
expect(isCanceled2).toEqual(true);

let numRemoved: number = await removeOrderFromCache(openOrderId1, client);
expect(numRemoved).toEqual(1);
await removeOrderFromCaches(openOrderId1, client);
isCanceled1 = await isOrderCanceled(openOrderId1, client);
expect(isCanceled1).toEqual(false);

numRemoved = await removeOrderFromCache(openOrderId3, client);
expect(numRemoved).toEqual(0);
await removeOrderFromCaches(openOrderId3, client);
const isCanceled3: boolean = await isOrderCanceled(openOrderId1, client);
expect(isCanceled3).toEqual(false);
});

it('removes cancelled orders outside of window size', async () => {
Expand All @@ -61,4 +64,22 @@ describe('cancelledOrdersCache', () => {
expect(isCanceled3).toEqual(true);
});

describe('getOrderCanceledStatus', () => {
it('correctly returns CANCELED', async () => {
await addCanceledOrderId(openOrderId1, 10, client);
const status: CanceledOrderStatus = await getOrderCanceledStatus(openOrderId1, client);
expect(status).toEqual(CanceledOrderStatus.CANCELED);
});

it('correctly returns BEST_EFFORT_CANCELED', async () => {
await addBestEffortCanceledOrderId(openOrderId1, 10, client);
const status: CanceledOrderStatus = await getOrderCanceledStatus(openOrderId1, client);
expect(status).toEqual(CanceledOrderStatus.BEST_EFFORT_CANCELED);
});

it('correctly returns NOT_CANCELED', async () => {
const status: CanceledOrderStatus = await getOrderCanceledStatus(openOrderId1, client);
expect(status).toEqual(CanceledOrderStatus.NOT_CANCELED);
});
});
});
76 changes: 70 additions & 6 deletions indexer/packages/redis/src/caches/canceled-orders-cache.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { Callback, RedisClient } from 'redis';

import { zRemAsync, zScoreAsync } from '../helpers/redis';
import { CanceledOrderStatus } from '../types';
import { addCanceledOrderIdScript } from './scripts';
// Cache of cancelled orders
export const CANCELED_ORDERS_CACHE_KEY: string = 'v4/cancelled_orders';
export const BEST_EFFORT_CANCELED_ORDERS_CACHE_KEY: string = 'v4/best_effort_cancelled_orders';
// 10 seconds in milliseconds
export const CANCELED_ORDER_WINDOW_SIZE: number = 30 * 1000;

Expand All @@ -24,16 +26,62 @@ export async function isOrderCanceled(
orderId: string,
client: RedisClient,
): Promise<boolean> {
const score: string | null = await
zScoreAsync({ hash: CANCELED_ORDERS_CACHE_KEY, key: orderId }, client);
return score !== null;
const [
canceledScore,
bestEffortCanceledScore,
]: (string | null)[] = await Promise.all([
zScoreAsync({ hash: CANCELED_ORDERS_CACHE_KEY, key: orderId }, client),
zScoreAsync({ hash: BEST_EFFORT_CANCELED_ORDERS_CACHE_KEY, key: orderId }, client),
]);
return canceledScore !== null || bestEffortCanceledScore !== null;
}

export async function removeOrderFromCache(
export async function getOrderCanceledStatus(
orderId: string,
client: RedisClient,
): Promise<CanceledOrderStatus> {
const [
canceledScore,
bestEffortCanceledScore,
]: (string | null)[] = await Promise.all([
zScoreAsync({ hash: CANCELED_ORDERS_CACHE_KEY, key: orderId }, client),
zScoreAsync({ hash: BEST_EFFORT_CANCELED_ORDERS_CACHE_KEY, key: orderId }, client),
]);

if (canceledScore !== null) {
return CanceledOrderStatus.CANCELED;
}

if (bestEffortCanceledScore !== null) {
return CanceledOrderStatus.BEST_EFFORT_CANCELED;
}

return CanceledOrderStatus.NOT_CANCELED;
}

export async function removeOrderFromCaches(
orderId: string,
client: RedisClient,
): Promise<void> {
await Promise.all([
zRemAsync({ hash: CANCELED_ORDERS_CACHE_KEY, key: orderId }, client),
zRemAsync({ hash: BEST_EFFORT_CANCELED_ORDERS_CACHE_KEY, key: orderId }, client),
]);
}

/**
* addCanceledOrderId adds the order id to the best effort canceled orders cache.
*
* @param orderId
* @param timestamp
* @param client
*/
export async function addBestEffortCanceledOrderId(
orderId: string,
timestamp: number,
client: RedisClient,
): Promise<number> {
return zRemAsync({ hash: CANCELED_ORDERS_CACHE_KEY, key: orderId }, client);
return addOrderIdtoCache(orderId, timestamp, client, BEST_EFFORT_CANCELED_ORDERS_CACHE_KEY);
}

/**
Expand All @@ -47,6 +95,22 @@ export async function addCanceledOrderId(
orderId: string,
timestamp: number,
client: RedisClient,
): Promise<number> {
return addOrderIdtoCache(orderId, timestamp, client, CANCELED_ORDERS_CACHE_KEY);
}

/**
* addCanceledOrderId adds the order id to the cacheKey's cache.
*
* @param orderId
* @param timestamp
* @param client
*/
export async function addOrderIdtoCache(
orderId: string,
timestamp: number,
client: RedisClient,
cacheKey: string,
): Promise<number> {
const numKeys: number = 2;
let evalAsync: (
Expand All @@ -69,7 +133,7 @@ export async function addCanceledOrderId(
client.evalsha(
addCanceledOrderIdScript.hash,
numKeys,
CANCELED_ORDERS_CACHE_KEY,
cacheKey,
CANCELED_ORDER_WINDOW_SIZE,
canceledOrderId,
currentTimestampMs,
Expand Down
6 changes: 6 additions & 0 deletions indexer/packages/redis/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ export type LuaScript = {
readonly hash: string;
};

export enum CanceledOrderStatus {
CANCELED = 'CANCELED',
BEST_EFFORT_CANCELED = 'BEST_EFFORT_CANCELED',
NOT_CANCELED = 'NOT_CANCELED',
}

/* ------- PNL Creation TYPES ------- */
export type PnlTickForSubaccounts = {
// Stores a PnlTicksCreateObject for the most recent pnl tick for each subaccount.
Expand Down
Loading

0 comments on commit ff97fca

Please sign in to comment.