Files
dydx-v4-web/src/hooks/Orderbook/useOrderbookValues.ts
T
Jared Vu 0ed1f33808 Canvas Orderbook (#227)
* 🚧 Canvas Orderbook

* 🚧 Add orderbookMap to aid in Canvas redraws

* enable mine column

* Merge main into canvas-orderbook-2

* 🧱 fix: Add orderbook constants file, remove console log, nits

* 💄 style: orderbook canvas text 13.5px -> 12px

* Updated constants and typings

* loadingSpace logic was slowing down ref mount
2024-01-24 23:16:33 -08:00

127 lines
4.0 KiB
TypeScript

import { useMemo } from 'react';
import { shallowEqual, useSelector } from 'react-redux';
import { OrderSide } from '@dydxprotocol/v4-client-js';
import { type PerpetualMarketOrderbookLevel } from '@/constants/abacus';
import { DepthChartSeries, DepthChartDatum } from '@/constants/charts';
import { getCurrentMarketOrderbook } from '@/state/perpetualsSelectors';
import { getSubaccountOpenOrdersBySideAndPrice } from '@/state/accountSelectors';
import { MustBigNumber } from '@/lib/numbers';
export const useCalculateOrderbookData = ({ maxRowsPerSide }: { maxRowsPerSide: number }) => {
const orderbook = useSelector(getCurrentMarketOrderbook, shallowEqual);
const openOrdersBySideAndPrice =
useSelector(getSubaccountOpenOrdersBySideAndPrice, shallowEqual) || {};
return useMemo(() => {
const asks: Array<PerpetualMarketOrderbookLevel | undefined> = (
orderbook?.asks?.toArray() ?? []
)
.map(
(row, idx: number) =>
({
key: `ask-${idx}`,
side: 'ask',
mine: openOrdersBySideAndPrice[OrderSide.SELL]?.[row.price]?.size,
...row,
} as PerpetualMarketOrderbookLevel)
)
.slice(0, maxRowsPerSide);
const bids: Array<PerpetualMarketOrderbookLevel | undefined> = (
orderbook?.bids?.toArray() ?? []
)
.map(
(row, idx: number) =>
({
key: `bid-${idx}`,
side: 'bid',
mine: openOrdersBySideAndPrice[OrderSide.BUY]?.[row.price]?.size,
...row,
} as PerpetualMarketOrderbookLevel)
)
.slice(0, maxRowsPerSide);
// Prevent the bid/ask sides from crossing by using the offsets.
// While the books are crossing...
while (asks[0] && bids[0] && bids[0]!.price >= asks[0].price) {
// Drop the order on the side with the lower offset.
// The offset of the other side is higher and so supercedes.
if (bids[0]!.offset === asks[0].offset) {
// If offsets are the same, give precedence to the larger size. In this case,
// one of the sizes *should* be zero, but we simply check for the larger size.
if (bids[0]!.size > asks[0].size) {
asks.shift();
} else {
bids.pop();
}
} else {
// Offsets are not equal. Give precedence to the larger offset.
if (bids[0]!.offset > asks[0].offset) {
asks.shift();
} else {
bids.pop();
}
}
}
const spread =
asks[0]?.price && bids[0]?.price ? MustBigNumber(asks[0].price).minus(bids[0].price) : null;
const spreadPercent = orderbook?.spreadPercent;
const histogramRange = Math.max(
isNaN(Number(bids[bids.length - 1]?.depth)) ? 0 : Number(bids[bids.length - 1]?.depth),
isNaN(Number(asks[asks.length - 1]?.depth)) ? 0 : Number(asks[asks.length - 1]?.depth)
);
return {
asks,
bids,
spread,
spreadPercent,
histogramRange,
hasOrderbook: !!orderbook,
};
}, [orderbook, openOrdersBySideAndPrice]);
};
export const useOrderbookValuesForDepthChart = () => {
const orderbook = useSelector(getCurrentMarketOrderbook, shallowEqual);
return useMemo(() => {
const bids = (orderbook?.bids?.toArray() ?? [])
.filter(Boolean)
.map((datum) => ({ ...datum, seriesKey: DepthChartSeries.Bids } as DepthChartDatum));
const asks = (orderbook?.asks?.toArray() ?? [])
.filter(Boolean)
.map((datum) => ({ ...datum, seriesKey: DepthChartSeries.Asks } as DepthChartDatum));
const lowestBid = bids[bids.length - 1];
const highestBid = bids[0];
const lowestAsk = asks[0];
const highestAsk = asks[asks.length - 1];
const midMarketPrice = orderbook?.midPrice;
const spread = MustBigNumber(lowestAsk?.price ?? 0).minus(highestBid?.price ?? 0);
const spreadPercent = orderbook?.spreadPercent;
return {
bids,
asks,
lowestBid,
highestBid,
lowestAsk,
highestAsk,
midMarketPrice,
spread,
spreadPercent,
orderbook,
};
}, [orderbook]);
};