Skip to content

Commit

Permalink
Return values from redis without division, perform division in javasc…
Browse files Browse the repository at this point in the history
…ript
  • Loading branch information
adamfraser committed Sep 24, 2024
1 parent 843aea3 commit 5a6ddd4
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@ describe('orderbook-mid-prices-cache', () => {
await deleteAllAsync(client);
});

afterEach(async () => {
await deleteAllAsync(client);
});

describe('setPrice', () => {
it('sets a price for a ticker', async () => {
await setPrice(client, ticker, '50000');
Expand Down Expand Up @@ -102,5 +98,39 @@ describe('orderbook-mid-prices-cache', () => {

jest.useRealTimers();
});

it('returns the correct median price for small numbers with even number of prices', async () => {
await Promise.all([
setPrice(client, ticker, '0.00000000002345'),
setPrice(client, ticker, '0.00000000002346'),
]);

const midPrice1 = await getMedianPrice(client, ticker);
expect(midPrice1).toEqual('0.000000000023455');
});

it('returns the correct median price for small numbers with odd number of prices', async () => {
await Promise.all([
setPrice(client, ticker, '0.00000000001'),
setPrice(client, ticker, '0.00000000002'),
setPrice(client, ticker, '0.00000000003'),
setPrice(client, ticker, '0.00000000004'),
setPrice(client, ticker, '0.00000000005'),
]);

const midPrice1 = await getMedianPrice(client, ticker);
expect(midPrice1).toEqual('0.00000000003');

await deleteAllAsync(client);

await Promise.all([
setPrice(client, ticker, '0.00000847007'),
setPrice(client, ticker, '0.00000847006'),
setPrice(client, ticker, '0.00000847008'),
]);

const midPrice2 = await getMedianPrice(client, ticker);
expect(midPrice2).toEqual('0.00000847007');
});
});
});
30 changes: 25 additions & 5 deletions indexer/packages/redis/src/caches/orderbook-mid-prices-cache.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Big from 'big.js';
import { Callback, RedisClient } from 'redis';

import {
Expand Down Expand Up @@ -69,21 +70,23 @@ export async function setPrice(

/**
* Retrieves the median price for a given ticker from the cache.
* Uses a Lua script to calculate the median price from the sorted set in Redis.
* Uses a Lua script to fetch either the middle element (for odd number of prices)
* or the two middle elements (for even number of prices) from a sorted set in Redis.
* If two middle elements are returned, their average is calculated in JavaScript.
* @param client The Redis client
* @param ticker The ticker symbol
* @returns A promise that resolves with the median price as a string, or null if not found
*/
export async function getMedianPrice(client: RedisClient, ticker: string): Promise<string | null> {
let evalAsync: (
marketCacheKey: string,
) => Promise<string> = (
) => Promise<string[]> = (
marketCacheKey,
) => {
return new Promise((resolve, reject) => {
const callback: Callback<string> = (
const callback: Callback<string[]> = (
err: Error | null,
results: string,
results: string[],
) => {
if (err) {
return reject(err);
Expand All @@ -101,7 +104,24 @@ export async function getMedianPrice(client: RedisClient, ticker: string): Promi
};
evalAsync = evalAsync.bind(client);

return evalAsync(
const prices = await evalAsync(
getOrderbookMidPriceCacheKey(ticker),
);

if (!prices || prices.length === 0) {
return null;
}

if (prices.length === 1) {
return Big(prices[0]).toFixed();
}

if (prices.length === 2) {
const [price1, price2] = prices.map((price) => {
return Big(price);
});
return price1.plus(price2).div(2).toFixed();
}

return null;
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,9 @@ local middle = math.floor(#prices / 2)

-- Calculate median
if #prices % 2 == 0 then
-- If even, return the average of the two middle elements
local median = (tonumber(prices[middle]) + tonumber(prices[middle + 1])) / 2
return tostring(median)
-- If even, return both prices, division will be handled in Javascript
return {prices[middle], prices[middle + 1]}
else
-- If odd, return the middle element
return prices[middle + 1]
return {prices[middle + 1]}
end

0 comments on commit 5a6ddd4

Please sign in to comment.