diff --git a/src/constants/orderbook.ts b/src/constants/orderbook.ts index 3e8b490ef..169843ee1 100644 --- a/src/constants/orderbook.ts +++ b/src/constants/orderbook.ts @@ -2,7 +2,7 @@ * @description Orderbook display constants */ export const ORDERBOOK_MAX_ROWS_PER_SIDE = 30; -export const ORDERBOOK_ANIMATION_DURATION = 400; +export const ORDERBOOK_ANIMATION_DURATION = 100; /** * @description Orderbook pixel constants diff --git a/src/hooks/Orderbook/useDrawOrderbook.ts b/src/hooks/Orderbook/useDrawOrderbook.ts index 62cc7d057..8aa73f7fe 100644 --- a/src/hooks/Orderbook/useDrawOrderbook.ts +++ b/src/hooks/Orderbook/useDrawOrderbook.ts @@ -40,6 +40,8 @@ enum OrderbookRowAnimationType { NONE, } +export type Rekt = { x1: number; x2: number; y1: number; y2: number }; + export const useDrawOrderbook = ({ data, histogramRange, @@ -96,7 +98,7 @@ export const useDrawOrderbook = ({ gradientMultiplier, histogramAccentColor, histogramSide, - idx, + rekt, }: { barType: 'depth' | 'size'; ctx: CanvasRenderingContext2D; @@ -104,15 +106,9 @@ export const useDrawOrderbook = ({ gradientMultiplier: number; histogramAccentColor: string; histogramSide: 'left' | 'right'; - idx: number; + rekt: Rekt; }) => { - const { x1, x2, y1, y2 } = getRektFromIdx({ - idx, - rowHeight, - canvasWidth, - canvasHeight, - side, - }); + const { x1, x2, y1, y2 } = rekt; // X values const maxHistogramBarWidth = x2 - x1 - (barType === 'size' ? 8 : 2); @@ -162,25 +158,19 @@ export const useDrawOrderbook = ({ const drawText = ({ animationType = OrderbookRowAnimationType.NONE, ctx, - idx, mine, price, size, + rekt, }: { animationType?: OrderbookRowAnimationType; ctx: CanvasRenderingContext2D; - idx: number; mine?: number; price?: number; size?: number; + rekt: Rekt; }) => { - const { y1 } = getRektFromIdx({ - idx, - rowHeight, - canvasWidth, - canvasHeight, - side, - }); + const { y1 } = rekt; const { text: y } = getYForElements({ y: y1, rowHeight }); @@ -189,7 +179,7 @@ export const useDrawOrderbook = ({ switch (animationType) { case OrderbookRowAnimationType.REMOVE: { - textColor = theme.textSecondary; + textColor = theme.textTertiary; break; } @@ -250,6 +240,13 @@ export const useDrawOrderbook = ({ if (!rowToRender) return; const { depth, mine, price, size } = rowToRender; const histogramAccentColor = side === 'bid' ? theme.positiveFaded : theme.negativeFaded; + const rekt = getRektFromIdx({ + idx, + rowHeight, + canvasWidth, + canvasHeight, + side, + }); // Depth Bar if (depth) { @@ -260,7 +257,7 @@ export const useDrawOrderbook = ({ gradientMultiplier: 1.3, histogramAccentColor, histogramSide, - idx, + rekt, }); } @@ -272,17 +269,17 @@ export const useDrawOrderbook = ({ gradientMultiplier: 5, histogramAccentColor, histogramSide, - idx, + rekt, }); // Size, Price, Mine drawText({ animationType, ctx, - idx, mine, price, size, + rekt, }); }; @@ -298,59 +295,24 @@ export const useDrawOrderbook = ({ // Animate row removal and update const mapOfOrderbookPriceLevels = side && currentOrderbookMap?.[side === 'ask' ? 'asks' : 'bids']; - const empty: number[] = []; - const removed: number[] = []; - const updated: number[] = []; prevData.current.forEach((row, idx) => { - if (!row) { - empty.push(idx); - return; - } + if (!row) return; + + let animationType = OrderbookRowAnimationType.NEW; if (mapOfOrderbookPriceLevels?.[row.price] === 0) { - removed.push(idx); - drawOrderbookRow({ - ctx, - idx, - rowToRender: row, - animationType: OrderbookRowAnimationType.REMOVE, - }); - } else if (mapOfOrderbookPriceLevels?.[row.price] === row?.size) { - drawOrderbookRow({ - ctx, - idx, - rowToRender: data[idx], - animationType: OrderbookRowAnimationType.NONE, - }); - } else { - updated.push(idx); - drawOrderbookRow({ - ctx, - idx, - rowToRender: row, - animationType: OrderbookRowAnimationType.NEW, - }); + animationType = OrderbookRowAnimationType.REMOVE; + } else if (mapOfOrderbookPriceLevels?.[row.price] === row.size) { + animationType = OrderbookRowAnimationType.NONE; } + + drawOrderbookRow({ ctx, idx, rowToRender: row, animationType }); }); setTimeout(() => { - [...empty, ...removed, ...updated].forEach((idx) => { - const { x1, y1, x2, y2 } = getRektFromIdx({ - idx, - rowHeight, - canvasWidth, - canvasHeight, - side, - }); - - ctx.clearRect(x1, y1, x2 - x1, y2 - y1); - drawOrderbookRow({ - ctx, - idx, - rowToRender: data[idx], - }); - }); + ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); + data.forEach((row, idx) => drawOrderbookRow({ ctx, idx, rowToRender: row })); }, ORDERBOOK_ANIMATION_DURATION); prevData.current = data; diff --git a/src/hooks/Orderbook/useOrderbookValues.ts b/src/hooks/Orderbook/useOrderbookValues.ts index 804e78ea1..ceecab6af 100644 --- a/src/hooks/Orderbook/useOrderbookValues.ts +++ b/src/hooks/Orderbook/useOrderbookValues.ts @@ -3,10 +3,10 @@ import { useMemo } from 'react'; import { OrderSide } from '@dydxprotocol/v4-client-js'; import { shallowEqual, useSelector } from 'react-redux'; -import { type PerpetualMarketOrderbookLevel } from '@/constants/abacus'; +import { OrderbookLine, type PerpetualMarketOrderbookLevel } from '@/constants/abacus'; import { DepthChartSeries, DepthChartDatum } from '@/constants/charts'; -import { getSubaccountOpenStatusOrdersBySideAndPrice } from '@/state/accountSelectors'; +import { getSubaccountOrderSizeBySideAndPrice } from '@/state/accountSelectors'; import { getCurrentMarketOrderbook } from '@/state/perpetualsSelectors'; import { MustBigNumber } from '@/lib/numbers'; @@ -14,19 +14,19 @@ import { MustBigNumber } from '@/lib/numbers'; export const useCalculateOrderbookData = ({ maxRowsPerSide }: { maxRowsPerSide: number }) => { const orderbook = useSelector(getCurrentMarketOrderbook, shallowEqual); - const openOrdersBySideAndPrice = - useSelector(getSubaccountOpenStatusOrdersBySideAndPrice, shallowEqual) || {}; + const subaccountOrderSizeBySideAndPrice = + useSelector(getSubaccountOrderSizeBySideAndPrice, shallowEqual) || {}; return useMemo(() => { const asks: Array = ( orderbook?.asks?.toArray() ?? [] ) .map( - (row, idx: number) => + (row: OrderbookLine, idx: number) => ({ key: `ask-${idx}`, side: 'ask', - mine: openOrdersBySideAndPrice[OrderSide.SELL]?.[row.price]?.size, + mine: subaccountOrderSizeBySideAndPrice[OrderSide.SELL]?.[row.price], ...row, } as PerpetualMarketOrderbookLevel) ) @@ -36,11 +36,11 @@ export const useCalculateOrderbookData = ({ maxRowsPerSide }: { maxRowsPerSide: orderbook?.bids?.toArray() ?? [] ) .map( - (row, idx: number) => + (row: OrderbookLine, idx: number) => ({ key: `bid-${idx}`, side: 'bid', - mine: openOrdersBySideAndPrice[OrderSide.BUY]?.[row.price]?.size, + mine: subaccountOrderSizeBySideAndPrice[OrderSide.BUY]?.[row.price], ...row, } as PerpetualMarketOrderbookLevel) ) @@ -87,7 +87,7 @@ export const useCalculateOrderbookData = ({ maxRowsPerSide }: { maxRowsPerSide: histogramRange, hasOrderbook: !!orderbook, }; - }, [orderbook, openOrdersBySideAndPrice]); + }, [orderbook, subaccountOrderSizeBySideAndPrice]); }; export const useOrderbookValuesForDepthChart = () => { diff --git a/src/state/accountSelectors.ts b/src/state/accountSelectors.ts index 2cf740344..bb331bff7 100644 --- a/src/state/accountSelectors.ts +++ b/src/state/accountSelectors.ts @@ -142,16 +142,20 @@ export const getSubaccountOpenStatusOrders = createSelector([getSubaccountOrders orders?.filter((order) => order.status === AbacusOrderStatus.open) ); -export const getSubaccountOpenStatusOrdersBySideAndPrice = createSelector( +export const getSubaccountOrderSizeBySideAndPrice = createSelector( [getSubaccountOpenStatusOrders], (openOrders = []) => { - const ordersBySide: Partial>> = {}; - openOrders.forEach((order) => { + const orderSizeBySideAndPrice: Partial>> = {}; + openOrders.forEach((order: SubaccountOrder) => { const side = ORDER_SIDES[order.side.name]; - const byPrice = (ordersBySide[side] ??= {}); - byPrice[order.price] = order; + const byPrice = (orderSizeBySideAndPrice[side] ??= {}); + if (byPrice[order.price]) { + byPrice[order.price] += order.size; + } else { + byPrice[order.price] = order.size; + } }); - return ordersBySide; + return orderSizeBySideAndPrice; } ); diff --git a/src/views/tables/Orderbook.tsx b/src/views/tables/Orderbook.tsx index 4eacba087..2d50718ce 100644 --- a/src/views/tables/Orderbook.tsx +++ b/src/views/tables/Orderbook.tsx @@ -20,7 +20,7 @@ import { type CustomRowConfig, TableRow } from '@/components/Table'; import { WithTooltip } from '@/components/WithTooltip'; import { calculateCanViewAccount } from '@/state/accountCalculators'; -import { getSubaccountOpenStatusOrdersBySideAndPrice } from '@/state/accountSelectors'; +import { getSubaccountOrderSizeBySideAndPrice } from '@/state/accountSelectors'; import { getCurrentMarketAssetData } from '@/state/assetsSelectors'; import { setTradeFormInputs } from '@/state/inputs'; import { getCurrentInput } from '@/state/inputsSelectors'; @@ -50,8 +50,8 @@ type RowData = Pick & { const useCalculateOrderbookData = ({ maxRowsPerSide }: { maxRowsPerSide: number }) => { const orderbook = useSelector(getCurrentMarketOrderbook, shallowEqual); - const openOrdersBySideAndPrice = - useSelector(getSubaccountOpenStatusOrdersBySideAndPrice, shallowEqual) || {}; + const subaccountOrderSizeBySideAndPrice = + useSelector(getSubaccountOrderSizeBySideAndPrice, shallowEqual) || {}; return useMemo(() => { const asks = (orderbook?.asks?.toArray() ?? []) @@ -60,8 +60,7 @@ const useCalculateOrderbookData = ({ maxRowsPerSide }: { maxRowsPerSide: number ({ key: `ask-${idx}`, side: 'ask', - mine: openOrdersBySideAndPrice[OrderSide.SELL]?.[row.price]?.size, - ...row, + mine: subaccountOrderSizeBySideAndPrice[OrderSide.SELL]?.[row.price], } as RowData) ) .slice(0, maxRowsPerSide); @@ -72,7 +71,7 @@ const useCalculateOrderbookData = ({ maxRowsPerSide }: { maxRowsPerSide: number ({ key: `bid-${idx}`, side: 'bid', - mine: openOrdersBySideAndPrice[OrderSide.BUY]?.[row.price]?.size, + mine: subaccountOrderSizeBySideAndPrice[OrderSide.BUY]?.[row.price], ...row, } as RowData) ) @@ -141,7 +140,7 @@ const useCalculateOrderbookData = ({ maxRowsPerSide }: { maxRowsPerSide: number } return { asks, bids, spread, spreadPercent, histogramRange, hasOrderbook: !!orderbook }; - }, [orderbook, openOrdersBySideAndPrice]); + }, [orderbook, subaccountOrderSizeBySideAndPrice]); }; const OrderbookTable = ({