diff --git a/components/trade/Orderbook.tsx b/components/trade/Orderbook.tsx index f45e4a77..8f84097b 100644 --- a/components/trade/Orderbook.tsx +++ b/components/trade/Orderbook.tsx @@ -469,6 +469,9 @@ const Orderbook = () => { size={orderbookData?.asks[index].size} side="sell" sizePercent={orderbookData?.asks[index].sizePercent} + averagePrice={orderbookData?.asks[index].averagePrice} + cumulativeValue={orderbookData?.asks[index].cumulativeValue} + cumulativeSize={orderbookData?.asks[index].cumulativeSize} cumulativeSizePercent={ orderbookData?.asks[index].cumulativeSizePercent } @@ -510,6 +513,9 @@ const Orderbook = () => { size={orderbookData?.bids[index].size} side="buy" sizePercent={orderbookData?.bids[index].sizePercent} + averagePrice={orderbookData?.bids[index].averagePrice} + cumulativeValue={orderbookData?.bids[index].cumulativeValue} + cumulativeSize={orderbookData?.bids[index].cumulativeSize} cumulativeSizePercent={ orderbookData?.bids[index].cumulativeSizePercent } @@ -531,6 +537,9 @@ const OrderbookRow = ({ // invert, hasOpenOrder, minOrderSize, + averagePrice, + cumulativeValue, + cumulativeSize, cumulativeSizePercent, tickSize, grouping, @@ -539,6 +548,9 @@ const OrderbookRow = ({ price: number size: number sizePercent: number + averagePrice: number + cumulativeValue: number + cumulativeSize: number cumulativeSizePercent: number hasOpenOrder: boolean // invert: boolean @@ -616,12 +628,35 @@ const OrderbookRow = ({ [minOrderSize], ) + const handleMouseOver = useCallback(() => { + const { set } = mangoStore.getState() + if (averagePrice && cumulativeSize && cumulativeValue) { + set((state) => { + state.orderbookTooltip = { + averagePrice, + cumulativeSize, + cumulativeValue, + side, + } + }) + } + }, [averagePrice, cumulativeSize, cumulativeValue]) + + const handleMouseLeave = useCallback(() => { + const { set } = mangoStore.getState() + set((state) => { + state.orderbookTooltip = undefined + }) + }, []) + if (!minOrderSize) return null return (
<>
diff --git a/components/trade/OrderbookTooltip.tsx b/components/trade/OrderbookTooltip.tsx new file mode 100644 index 00000000..8fefe4e4 --- /dev/null +++ b/components/trade/OrderbookTooltip.tsx @@ -0,0 +1,50 @@ +import { PerpMarket } from '@blockworks-foundation/mango-v4' +import mangoStore from '@store/mangoStore' +import useSelectedMarket from 'hooks/useSelectedMarket' +import { useMemo } from 'react' +import { useTranslation } from 'react-i18next' +import { formatNumericValue, getDecimalCount } from 'utils/numbers' + +const OrderbookTooltip = () => { + const { t } = useTranslation(['common', 'trade']) + const orderbookTooltip = mangoStore((s) => s.orderbookTooltip) + const { serumOrPerpMarket, baseSymbol, quoteSymbol } = useSelectedMarket() + + const [minOrderDecimals, tickDecimals] = useMemo(() => { + if (!serumOrPerpMarket) return [0, 0] + return [ + getDecimalCount(serumOrPerpMarket.minOrderSize), + getDecimalCount(serumOrPerpMarket.tickSize), + ] + }, [serumOrPerpMarket]) + + if (!orderbookTooltip) return null + + const { averagePrice, cumulativeSize, cumulativeValue, side } = + orderbookTooltip + const isBid = side === 'buy' + const isPerp = serumOrPerpMarket instanceof PerpMarket + return ( +
+

+ {t(side)} + {` ${formatNumericValue(cumulativeSize, minOrderDecimals)} ${ + isPerp ? '' : baseSymbol + } ${t('trade:for')} ${isPerp ? '$' : ''}${formatNumericValue( + cumulativeValue, + tickDecimals, + )} ${isPerp ? '' : quoteSymbol} ${t('trade:average-price-of')} ${ + isPerp ? '$' : '' + }${formatNumericValue(averagePrice, tickDecimals)} ${ + isPerp ? '' : quoteSymbol + }`} +

+
+ ) +} + +export default OrderbookTooltip diff --git a/components/trade/TradeAdvancedPage.tsx b/components/trade/TradeAdvancedPage.tsx index 28ebc4fd..dd6b2d4d 100644 --- a/components/trade/TradeAdvancedPage.tsx +++ b/components/trade/TradeAdvancedPage.tsx @@ -22,6 +22,7 @@ import FavoriteMarketsBar from './FavoriteMarketsBar' import useLocalStorageState from 'hooks/useLocalStorageState' import { SIDEBAR_COLLAPSE_KEY, TRADE_LAYOUT_KEY } from 'utils/constants' import TradeHotKeys from './TradeHotKeys' +import OrderbookTooltip from './OrderbookTooltip' export type TradeLayout = | 'chartLeft' @@ -304,6 +305,7 @@ const TradeAdvancedPage = () => { className="h-full border border-x-0 border-th-bkg-3" >
+
diff --git a/public/locales/en/trade.json b/public/locales/en/trade.json index 9cafc2fb..29ffef05 100644 --- a/public/locales/en/trade.json +++ b/public/locales/en/trade.json @@ -3,6 +3,7 @@ "activate-volume-alert": "Activate Volume Alert", "amount": "Amount", "average-funding": "Average {{interval}} Funding", + "average-price-of": "at an average price of", "base": "Base", "book": "Book", "buys": "Buys", @@ -20,6 +21,7 @@ "est-liq-price": "Est. Liq. Price", "avg-entry-price": "Avg. Entry Price", "est-slippage": "Est. Slippage", + "for": "for", "funding-limits": "Funding Limits", "funding-rate": "1h Avg Funding Rate", "grouping": "Grouping", diff --git a/public/locales/es/trade.json b/public/locales/es/trade.json index 9cafc2fb..29ffef05 100644 --- a/public/locales/es/trade.json +++ b/public/locales/es/trade.json @@ -3,6 +3,7 @@ "activate-volume-alert": "Activate Volume Alert", "amount": "Amount", "average-funding": "Average {{interval}} Funding", + "average-price-of": "at an average price of", "base": "Base", "book": "Book", "buys": "Buys", @@ -20,6 +21,7 @@ "est-liq-price": "Est. Liq. Price", "avg-entry-price": "Avg. Entry Price", "est-slippage": "Est. Slippage", + "for": "for", "funding-limits": "Funding Limits", "funding-rate": "1h Avg Funding Rate", "grouping": "Grouping", diff --git a/public/locales/ru/trade.json b/public/locales/ru/trade.json index 9cafc2fb..29ffef05 100644 --- a/public/locales/ru/trade.json +++ b/public/locales/ru/trade.json @@ -3,6 +3,7 @@ "activate-volume-alert": "Activate Volume Alert", "amount": "Amount", "average-funding": "Average {{interval}} Funding", + "average-price-of": "at an average price of", "base": "Base", "book": "Book", "buys": "Buys", @@ -20,6 +21,7 @@ "est-liq-price": "Est. Liq. Price", "avg-entry-price": "Avg. Entry Price", "est-slippage": "Est. Slippage", + "for": "for", "funding-limits": "Funding Limits", "funding-rate": "1h Avg Funding Rate", "grouping": "Grouping", diff --git a/public/locales/zh/trade.json b/public/locales/zh/trade.json index 729b4a04..322ae6d4 100644 --- a/public/locales/zh/trade.json +++ b/public/locales/zh/trade.json @@ -3,6 +3,7 @@ "activate-volume-alert": "Activate Volume Alert", "amount": "Amount", "average-funding": "Average {{interval}} Funding", + "average-price-of": "at an average price of", "base": "Base", "book": "Book", "buys": "Buys", @@ -20,6 +21,7 @@ "est-liq-price": "Est. Liq. Price", "avg-entry-price": "Avg. Entry Price", "est-slippage": "Est. Slippage", + "for": "for", "funding-limits": "Funding Limits", "funding-rate": "1h Avg Funding Rate", "grouping": "Grouping", diff --git a/public/locales/zh_tw/trade.json b/public/locales/zh_tw/trade.json index 013b540f..712236a3 100644 --- a/public/locales/zh_tw/trade.json +++ b/public/locales/zh_tw/trade.json @@ -4,6 +4,7 @@ "amount": "數量", "average-funding": "平均 {{interval}} 資金費", "avg-entry-price": "平均開倉價格", + "average-price-of": "at an average price of", "base": "基礎", "book": "單薄", "buys": "買", @@ -20,6 +21,7 @@ "edit-order": "編輯訂單", "est-liq-price": "預計清算價格", "est-slippage": "預計下滑", + "for": "for", "funding-limits": "資金費限制", "funding-rate": "1小時平均資金費", "grouping": "分組", diff --git a/store/mangoStore.ts b/store/mangoStore.ts index fda8ea0b..df7a6eac 100644 --- a/store/mangoStore.ts +++ b/store/mangoStore.ts @@ -59,6 +59,7 @@ import { ProfileDetails, MangoTokenStatsItem, PositionStat, + OrderbookTooltip, } from 'types' import spotBalancesUpdater from './spotBalancesUpdater' import { PerpMarket } from '@blockworks-foundation/mango-v4/' @@ -174,6 +175,7 @@ export type MangoStore = { closestToLiq: PositionStat[] } } + orderbookTooltip: OrderbookTooltip | undefined profile: { details: ProfileDetails | null loadDetails: boolean @@ -323,6 +325,7 @@ const mangoStore = create()( closestToLiq: [], }, }, + orderbookTooltip: undefined, profile: { loadDetails: false, details: { profile_name: '', trader_category: '', wallet_pk: '' }, diff --git a/types/index.ts b/types/index.ts index 217c1b05..fac9b7a0 100644 --- a/types/index.ts +++ b/types/index.ts @@ -425,7 +425,9 @@ export type MarketsDataItem = { export type cumOrderbookSide = { price: number size: number + averagePrice: number cumulativeSize: number + cumulativeValue: number sizePercent: number maxSizePercent: number cumulativeSizePercent: number @@ -439,6 +441,13 @@ export type OrderbookData = { spreadPercentage: number } +export type OrderbookTooltip = { + averagePrice: number + cumulativeSize: number + cumulativeValue: number + side: 'buy' | 'sell' +} + export interface HealthContribution { asset: string contribution: number diff --git a/utils/orderbook.ts b/utils/orderbook.ts index 5d93cf93..60c8a1f7 100644 --- a/utils/orderbook.ts +++ b/utils/orderbook.ts @@ -94,11 +94,15 @@ export const getCumulativeOrderbookSide = ( isGrouped: boolean, ): cumOrderbookSide[] => { let cumulativeSize = 0 + let cumulativeValue = 0 return orders.slice(0, depth).map(([price, size]) => { cumulativeSize += size + cumulativeValue += price * size return { price: Number(price), size, + averagePrice: cumulativeValue / cumulativeSize, + cumulativeValue: cumulativeValue, cumulativeSize, sizePercent: Math.round((cumulativeSize / (totalSize || 1)) * 100), cumulativeSizePercent: Math.round((size / (cumulativeSize || 1)) * 100),