Skip to content

Commit

Permalink
Adam/add candles hloc (#2047)
Browse files Browse the repository at this point in the history
  • Loading branch information
adamfraser authored Aug 7, 2024
1 parent 84eed16 commit 73b04dc
Show file tree
Hide file tree
Showing 14 changed files with 598 additions and 24 deletions.
2 changes: 2 additions & 0 deletions indexer/packages/postgres/__tests__/helpers/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,8 @@ export const defaultCandle: CandleCreateObject = {
usdVolume: '2200000',
trades: 300,
startingOpenInterest: '200000',
orderbookMidPriceOpen: '11500',
orderbookMidPriceClose: '12500',
};

export const defaultCandleId: string = CandleTable.uuid(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,11 @@ describe('CandleTable', () => {
const updatedCandle: CandleUpdateObject = {
id: defaultCandleId,
open: '100',
orderbookMidPriceClose: '200',
orderbookMidPriceOpen: '300',
};

await CandleTable.update({
id: defaultCandleId,
open: '100',
});
await CandleTable.update(updatedCandle);

const candle: CandleFromDatabase | undefined = await CandleTable.findById(
defaultCandleId,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import * as Knex from 'knex';

export async function up(knex: Knex): Promise<void> {
return knex
.schema
.alterTable('candles', (table) => {
table.decimal('orderbookMidPriceOpen', null).nullable();
table.decimal('orderbookMidPriceClose', null).nullable();
});
}

export async function down(knex: Knex): Promise<void> {
return knex
.schema
.alterTable('candles', (table) => {
table.dropColumn('orderbookMidPriceOpen');
table.dropColumn('orderbookMidPriceClose');
});
}
6 changes: 6 additions & 0 deletions indexer/packages/postgres/src/models/candle-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ export default class CandleModel extends Model {
usdVolume: { type: 'string', pattern: NonNegativeNumericPattern },
trades: { type: 'integer' },
startingOpenInterest: { type: 'string', pattern: NonNegativeNumericPattern },
orderbookMidPriceOpen: { type: ['string', 'null'], pattern: NonNegativeNumericPattern },
orderbookMidPriceClose: { type: ['string', 'null'], pattern: NonNegativeNumericPattern },
},
};
}
Expand Down Expand Up @@ -77,4 +79,8 @@ export default class CandleModel extends Model {
trades!: number;

startingOpenInterest!: string;

orderbookMidPriceOpen?: string;

orderbookMidPriceClose?: string;
}
4 changes: 4 additions & 0 deletions indexer/packages/postgres/src/types/candle-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ export interface CandleCreateObject {
usdVolume: string;
trades: number;
startingOpenInterest: string;
orderbookMidPriceOpen: string | undefined;
orderbookMidPriceClose: string | undefined;
}

export interface CandleUpdateObject {
Expand All @@ -24,6 +26,8 @@ export interface CandleUpdateObject {
usdVolume?: string;
trades?: number;
startingOpenInterest?: string;
orderbookMidPriceOpen?: string;
orderbookMidPriceClose?: string;
}

export enum CandleResolution {
Expand Down
2 changes: 2 additions & 0 deletions indexer/packages/postgres/src/types/db-model-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,8 @@ export interface CandleFromDatabase extends IdBasedModelFromDatabase {
usdVolume: string;
trades: number;
startingOpenInterest: string;
orderbookMidPriceOpen?: string | null;
orderbookMidPriceClose?: string | null;
}

export interface PnlTicksFromDatabase extends IdBasedModelFromDatabase {
Expand Down
133 changes: 133 additions & 0 deletions indexer/packages/redis/__tests__/caches/orderbook-levels-cache.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
deleteZeroPriceLevel,
getLastUpdatedKey,
deleteStalePriceLevel,
getOrderBookMidPrice,
} from '../../src/caches/orderbook-levels-cache';
import { OrderSide } from '@dydxprotocol-indexer/postgres';
import { OrderbookLevels, PriceLevel } from '../../src/types';
Expand Down Expand Up @@ -685,4 +686,136 @@ describe('orderbookLevelsCache', () => {
expect(size).toEqual('10');
});
});

describe('getMidPrice', () => {
beforeEach(() => {
jest.restoreAllMocks();
jest.restoreAllMocks();
});
afterEach(() => {
jest.restoreAllMocks();
jest.restoreAllMocks();
});

it('returns the correct mid price', async () => {
await Promise.all([
updatePriceLevel({
ticker,
side: OrderSide.BUY,
humanPrice: '45200',
sizeDeltaInQuantums: '2000',
client,
}),
updatePriceLevel({
ticker,
side: OrderSide.BUY,
humanPrice: '45100',
sizeDeltaInQuantums: '2000',
client,
}),
updatePriceLevel({
ticker,
side: OrderSide.BUY,
humanPrice: '45300',
sizeDeltaInQuantums: '2000',
client,
}),
updatePriceLevel({
ticker,
side: OrderSide.SELL,
humanPrice: '45500',
sizeDeltaInQuantums: '2000',
client,
}),
updatePriceLevel({
ticker,
side: OrderSide.SELL,
humanPrice: '45400',
sizeDeltaInQuantums: '2000',
client,
}),
updatePriceLevel({
ticker,
side: OrderSide.SELL,
humanPrice: '45600',
sizeDeltaInQuantums: '2000',
client,
}),
]);

const midPrice = await getOrderBookMidPrice(ticker, client);
expect(midPrice).toEqual('45350');
});
});

it('returns the correct mid price for very small numbers', async () => {
await Promise.all([
updatePriceLevel({
ticker,
side: OrderSide.SELL,
humanPrice: '0.000000002346',
sizeDeltaInQuantums: '2000',
client,
}),
updatePriceLevel({
ticker,
side: OrderSide.BUY,
humanPrice: '0.000000002344',
sizeDeltaInQuantums: '2000',
client,
}),
]);

const midPrice = await getOrderBookMidPrice(ticker, client);
expect(midPrice).toEqual('0.000000002345');
});

it('returns the approprite amount of decimal precision', async () => {
await Promise.all([
updatePriceLevel({
ticker,
side: OrderSide.SELL,
humanPrice: '1.02',
sizeDeltaInQuantums: '2000',
client,
}),
updatePriceLevel({
ticker,
side: OrderSide.BUY,
humanPrice: '1.01',
sizeDeltaInQuantums: '2000',
client,
}),
]);

const midPrice = await getOrderBookMidPrice(ticker, client);
expect(midPrice).toEqual('1.015');
});

it('returns undefined if there are no bids or asks', async () => {
await updatePriceLevel({
ticker,
side: OrderSide.SELL,
humanPrice: '45400',
sizeDeltaInQuantums: '2000',
client,
});

const midPrice = await getOrderBookMidPrice(ticker, client);
expect(midPrice).toBeUndefined();
});

it('returns undefined if humanPrice is NaN', async () => {
await updatePriceLevel({
ticker,
side: OrderSide.SELL,
humanPrice: 'nan',
sizeDeltaInQuantums: '2000',
client,
});

const midPrice = await getOrderBookMidPrice(ticker, client);

expect(midPrice).toBeUndefined();
});
});
24 changes: 24 additions & 0 deletions indexer/packages/redis/src/caches/orderbook-levels-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -529,3 +529,27 @@ function convertToPriceLevels(
};
});
}

export async function getOrderBookMidPrice(
ticker: string,
client: RedisClient,
): Promise<string | undefined> {
const levels = await getOrderBookLevels(ticker, client, {
removeZeros: true,
sortSides: true,
uncrossBook: true,
limitPerSide: 1,
});

if (levels.bids.length === 0 || levels.asks.length === 0) {
return undefined;
}

const bestAsk = Big(levels.asks[0].humanPrice);
const bestBid = Big(levels.bids[0].humanPrice);

if (bestAsk === undefined || bestBid === undefined) {
return undefined;
}
return bestBid.plus(bestAsk).div(2).toFixed();
}
8 changes: 8 additions & 0 deletions indexer/services/comlink/public/api-documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,8 @@ fetch(`${baseURL}/candles/perpetualMarkets/{ticker}?resolution=1MIN`,
"usdVolume": "string",
"trades": 0.1,
"startingOpenInterest": "string",
"orderbookMidPriceOpen": "string",
"orderbookMidPriceClose": "string",
"id": "string"
}
]
Expand Down Expand Up @@ -3655,6 +3657,8 @@ This operation does not require authentication
"usdVolume": "string",
"trades": 0.1,
"startingOpenInterest": "string",
"orderbookMidPriceOpen": "string",
"orderbookMidPriceClose": "string",
"id": "string"
}

Expand All @@ -3675,6 +3679,8 @@ This operation does not require authentication
|usdVolume|string|true|none|none|
|trades|number(double)|true|none|none|
|startingOpenInterest|string|true|none|none|
|orderbookMidPriceOpen|string¦null|false|none|none|
|orderbookMidPriceClose|string¦null|false|none|none|
|id|string|true|none|none|

## CandleResponse
Expand All @@ -3699,6 +3705,8 @@ This operation does not require authentication
"usdVolume": "string",
"trades": 0.1,
"startingOpenInterest": "string",
"orderbookMidPriceOpen": "string",
"orderbookMidPriceClose": "string",
"id": "string"
}
]
Expand Down
8 changes: 8 additions & 0 deletions indexer/services/comlink/public/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,14 @@
"startingOpenInterest": {
"type": "string"
},
"orderbookMidPriceOpen": {
"type": "string",
"nullable": true
},
"orderbookMidPriceClose": {
"type": "string",
"nullable": true
},
"id": {
"type": "string"
}
Expand Down
18 changes: 18 additions & 0 deletions indexer/services/ender/__tests__/helpers/redis-helpers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { OrderSide } from '@dydxprotocol-indexer/postgres';
import {
NextFundingCache,
OrderbookLevelsCache,
StateFilledQuantumsCache,
} from '@dydxprotocol-indexer/redis';
import Big from 'big.js';
Expand Down Expand Up @@ -29,3 +31,19 @@ export async function expectStateFilledQuantums(
expect(stateFilledQuantums).toBeDefined();
expect(stateFilledQuantums).toEqual(quantums);
}

export async function updatePriceLevel(
ticker: string,
price: string,
side: OrderSide,
): Promise<void> {
const quantums: string = '30';

await OrderbookLevelsCache.updatePriceLevel({
ticker,
side,
humanPrice: price,
sizeDeltaInQuantums: quantums,
client: redisClient,
});
}
Loading

0 comments on commit 73b04dc

Please sign in to comment.