diff --git a/apps/trading/client-pages/market/market-header-stats.tsx b/apps/trading/client-pages/market/market-header-stats.tsx index e477a4e19..38952d49e 100644 --- a/apps/trading/client-pages/market/market-header-stats.tsx +++ b/apps/trading/client-pages/market/market-header-stats.tsx @@ -19,6 +19,7 @@ import { useFundingRate, useMarketTradingMode, useExternalTwap, + getQuoteName, } from '@vegaprotocol/markets'; import { MarketState as State } from '@vegaprotocol/types'; import { HeaderStat } from '../../components/header'; @@ -41,6 +42,7 @@ export const MarketHeaderStats = ({ market }: MarketHeaderStatsProps) => { const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore(); const asset = getAsset(market); + const quoteUnit = getQuoteName(market); return ( <> @@ -60,6 +62,8 @@ export const MarketHeaderStats = ({ market }: MarketHeaderStatsProps) => { ()( ); export const MarketListTable = (props: Props) => { - const columnDefs = useColumnDefs(); + const columnDefs = useMarketsColumnDefs(); const gridStore = useMarketsStore((store) => store.gridStore); const updateGridStore = useMarketsStore((store) => store.updateGridStore); diff --git a/apps/trading/client-pages/markets/use-column-defs.tsx b/apps/trading/client-pages/markets/use-column-defs.tsx index 06b55a43e..5617b2833 100644 --- a/apps/trading/client-pages/markets/use-column-defs.tsx +++ b/apps/trading/client-pages/markets/use-column-defs.tsx @@ -7,21 +7,31 @@ import type { } from '@vegaprotocol/datagrid'; import { COL_DEFS, SetFilter } from '@vegaprotocol/datagrid'; import * as Schema from '@vegaprotocol/types'; -import { addDecimalsFormatNumber, toBigNum } from '@vegaprotocol/utils'; +import { + addDecimalsFormatNumber, + formatNumber, + toBigNum, +} from '@vegaprotocol/utils'; import { ButtonLink, Tooltip } from '@vegaprotocol/ui-toolkit'; import { useAssetDetailsDialogStore } from '@vegaprotocol/assets'; import type { + MarketFieldsFragment, MarketMaybeWithData, MarketMaybeWithDataAndCandles, } from '@vegaprotocol/markets'; import { MarketActionsDropdown } from './market-table-actions'; -import { calcCandleVolume, getAsset } from '@vegaprotocol/markets'; +import { + calcCandleVolume, + calcCandleVolumePrice, + getAsset, + getQuoteName, +} from '@vegaprotocol/markets'; import { MarketCodeCell } from './market-code-cell'; import { useT } from '../../lib/use-t'; const { MarketTradingMode, AuctionTrigger } = Schema; -export const useColumnDefs = () => { +export const useMarketsColumnDefs = () => { const t = useT(); const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore(); return useMemo( @@ -158,11 +168,25 @@ export const useColumnDefs = () => { }: ValueFormatterParams) => { const candles = data?.candles; const vol = candles ? calcCandleVolume(candles) : '0'; + const quoteName = getQuoteName(data as MarketFieldsFragment); + const volPrice = + candles && + calcCandleVolumePrice( + candles, + data.decimalPlaces, + data.positionDecimalPlaces + ); + const volume = data && vol && vol !== '0' ? addDecimalsFormatNumber(vol, data.positionDecimalPlaces) : '0.00'; - return volume; + const volumePrice = + volPrice && formatNumber(volPrice, data?.decimalPlaces); + + return volumePrice + ? `${volume} (${volumePrice} ${quoteName})` + : volume; }, }, { diff --git a/libs/markets/src/lib/components/last-24h-volume/last-24h-volume.tsx b/libs/markets/src/lib/components/last-24h-volume/last-24h-volume.tsx index 6cd5860cd..7cf631a6e 100644 --- a/libs/markets/src/lib/components/last-24h-volume/last-24h-volume.tsx +++ b/libs/markets/src/lib/components/last-24h-volume/last-24h-volume.tsx @@ -1,5 +1,9 @@ -import { calcCandleVolume } from '../../market-utils'; -import { addDecimalsFormatNumber, isNumeric } from '@vegaprotocol/utils'; +import { calcCandleVolume, calcCandleVolumePrice } from '../../market-utils'; +import { + addDecimalsFormatNumber, + formatNumber, + isNumeric, +} from '@vegaprotocol/utils'; import { Tooltip } from '@vegaprotocol/ui-toolkit'; import { useCandles } from '../../hooks'; import { useT } from '../../use-t'; @@ -9,13 +13,17 @@ interface Props { positionDecimalPlaces?: number; formatDecimals?: number; initialValue?: string; + marketDecimals?: number; + quoteUnit?: string; } export const Last24hVolume = ({ marketId, + marketDecimals, positionDecimalPlaces, formatDecimals, initialValue, + quoteUnit, }: Props) => { const t = useT(); const { oneDayCandles, fiveDaysCandles } = useCandles({ @@ -28,6 +36,11 @@ export const Last24hVolume = ({ (!oneDayCandles || oneDayCandles?.length === 0) ) { const candleVolume = calcCandleVolume(fiveDaysCandles); + const candleVolumePrice = calcCandleVolumePrice( + fiveDaysCandles, + marketDecimals, + positionDecimalPlaces + ); const candleVolumeValue = candleVolume && isNumeric(positionDecimalPlaces) ? addDecimalsFormatNumber( @@ -42,8 +55,8 @@ export const Last24hVolume = ({
{t( - '24 hour change is unavailable at this time. The volume change in the last 120 hours is {{candleVolumeValue}}', - { candleVolumeValue } + '24 hour change is unavailable at this time. The volume change in the last 120 hours is {{candleVolumeValue}} ({{candleVolumePrice}} {{quoteUnit}})', + { candleVolumeValue, candleVolumePrice, quoteUnit } )}
@@ -57,10 +70,18 @@ export const Last24hVolume = ({ ? calcCandleVolume(oneDayCandles) : initialValue; + const candleVolumePrice = oneDayCandles + ? calcCandleVolumePrice( + oneDayCandles, + marketDecimals, + positionDecimalPlaces + ) + : initialValue; + return ( @@ -70,7 +91,12 @@ export const Last24hVolume = ({ positionDecimalPlaces, formatDecimals ) - : '-'} + : '-'}{' '} + ( + {candleVolumePrice && isNumeric(positionDecimalPlaces) + ? formatNumber(candleVolumePrice, formatDecimals) + : '-'}{' '} + {quoteUnit}) ); diff --git a/libs/markets/src/lib/components/market-info/market-info-panels.tsx b/libs/markets/src/lib/components/market-info/market-info-panels.tsx index a6c3b0e6c..fe3c3acbe 100644 --- a/libs/markets/src/lib/components/market-info/market-info-panels.tsx +++ b/libs/markets/src/lib/components/market-info/market-info-panels.tsx @@ -155,6 +155,7 @@ export const MarketVolumeInfoPanel = ({ market }: MarketInfoProps) => { ), openInterest: dash(data?.openInterest), diff --git a/libs/markets/src/lib/market-utils.spec.tsx b/libs/markets/src/lib/market-utils.spec.tsx index 2bb759f81..88d2d6fba 100644 --- a/libs/markets/src/lib/market-utils.spec.tsx +++ b/libs/markets/src/lib/market-utils.spec.tsx @@ -1,6 +1,7 @@ import * as Schema from '@vegaprotocol/types'; import type { Market, MarketMaybeWithDataAndCandles } from './markets-provider'; import { + calcCandleVolumePrice, calcTradedFactor, filterAndSortMarkets, sumFeesFactors, @@ -145,3 +146,31 @@ describe('sumFeesFactors', () => { ).toEqual(0.6); }); }); + +describe('calcCandleVolumePrice', () => { + it('calculates the volume price', () => { + const candles = [ + { + volume: '1000', + high: '100', + low: '10', + open: '15', + close: '90', + periodStart: '2022-05-18T13:08:27.693537312Z', + }, + { + volume: '1000', + high: '100', + low: '10', + open: '15', + close: '90', + periodStart: '2022-05-18T14:08:27.693537312Z', + }, + ]; + const marketDecimals = 3; + const positionDecimalPlaces = 2; + expect( + calcCandleVolumePrice(candles, marketDecimals, positionDecimalPlaces) + ).toEqual('2'); + }); +}); diff --git a/libs/markets/src/lib/market-utils.ts b/libs/markets/src/lib/market-utils.ts index f0b93d109..36f5f4d2b 100644 --- a/libs/markets/src/lib/market-utils.ts +++ b/libs/markets/src/lib/market-utils.ts @@ -1,4 +1,8 @@ -import { formatNumberPercentage, toBigNum } from '@vegaprotocol/utils'; +import { + addDecimal, + formatNumberPercentage, + toBigNum, +} from '@vegaprotocol/utils'; import { MarketState, MarketTradingMode } from '@vegaprotocol/types'; import BigNumber from 'bignumber.js'; import orderBy from 'lodash/orderBy'; @@ -147,10 +151,51 @@ export const calcCandleHigh = (candles: Candle[]): string | undefined => { .toString(); }; +/** + * The total number of contracts traded in the last 24 hours. + * + * @param candles + * @returns the volume of a given set of candles + */ export const calcCandleVolume = (candles: Candle[]): string | undefined => candles && candles.reduce((acc, c) => new BigNumber(acc).plus(c.volume).toString(), '0'); +/** + * The total number of contracts traded in the last 24 hours. (Total value of contracts traded in the last 24 hours) + * The volume is calculated as the sum of the product of the volume and the high price of each candle. + * The result is formatted using positionDecimalPlaces to account for the position size. + * The result is formatted using marketDecimals to account for the market precision. + * + * @param candles + * @param marketDecimals + * @param positionDecimalPlaces + * @returns the volume (in quote price) of a given set of candles + */ +export const calcCandleVolumePrice = ( + candles: Candle[], + marketDecimals: number = 1, + positionDecimalPlaces: number = 1 +): string | undefined => + candles && + candles.reduce( + (acc, c) => + new BigNumber(acc) + .plus( + BigNumber(addDecimal(c.volume, positionDecimalPlaces)).times( + addDecimal(c.high, marketDecimals) + ) + ) + .toString(), + '0' + ); + +/** + * Calculates the traded factor of a given market. + * + * @param m + * @returns + */ export const calcTradedFactor = (m: MarketMaybeWithDataAndCandles) => { const volume = Number(calcCandleVolume(m.candles || []) || 0); const price = m.data?.markPrice ? Number(m.data.markPrice) : 0;