feat: 24h volume + market links in orders table (#1836)
* feat: #1659 add link to market in orders table * feat: #1794 use volume 24h instead of indicative volume * fix: remove total fees that was defined but not used in select columns * fix: fix cypress test for volume (24h) header * fix: add volume 24h tooltip * fix: trading mode and volume order
This commit is contained in:
parent
2d9fdbf98c
commit
ee3b9a56c9
@ -67,7 +67,7 @@ describe('Market trading page', () => {
|
||||
it('must see market volume', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketVolume).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Volume');
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Volume (24h)');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
|
1
apps/trading/components/last-24h-volume/index.ts
Normal file
1
apps/trading/components/last-24h-volume/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './last-24h-volume';
|
90
apps/trading/components/last-24h-volume/last-24h-volume.tsx
Normal file
90
apps/trading/components/last-24h-volume/last-24h-volume.tsx
Normal file
@ -0,0 +1,90 @@
|
||||
import {
|
||||
calcCandleVolume,
|
||||
marketCandlesProvider,
|
||||
marketProvider,
|
||||
} from '@vegaprotocol/market-list';
|
||||
import {
|
||||
addDecimalsFormatNumber,
|
||||
t,
|
||||
useDataProvider,
|
||||
useYesterday,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import { Interval } from '@vegaprotocol/types';
|
||||
import throttle from 'lodash/throttle';
|
||||
import { useCallback, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import * as constants from '../constants';
|
||||
import { HeaderStat } from '../header';
|
||||
|
||||
import type {
|
||||
SingleMarketFieldsFragment,
|
||||
Candle,
|
||||
} from '@vegaprotocol/market-list';
|
||||
export const Last24hVolume = ({ marketId }: { marketId: string }) => {
|
||||
const [candleVolume, setCandleVolume] = useState<string>();
|
||||
const yesterday = useYesterday();
|
||||
// Cache timestamp for yesterday to prevent full unmount of market page when
|
||||
// a rerender occurs
|
||||
const yTimestamp = useMemo(() => {
|
||||
return new Date(yesterday).toISOString();
|
||||
}, [yesterday]);
|
||||
|
||||
const marketVariables = useMemo(
|
||||
() => ({
|
||||
marketId: marketId,
|
||||
}),
|
||||
[marketId]
|
||||
);
|
||||
|
||||
const variables = useMemo(
|
||||
() => ({
|
||||
marketId: marketId,
|
||||
interval: Interval.INTERVAL_I1H,
|
||||
since: yTimestamp,
|
||||
}),
|
||||
[marketId, yTimestamp]
|
||||
);
|
||||
|
||||
const { data, error } = useDataProvider<SingleMarketFieldsFragment, never>({
|
||||
dataProvider: marketProvider,
|
||||
variables: marketVariables,
|
||||
skip: !marketId,
|
||||
});
|
||||
|
||||
const throttledSetCandles = useRef(
|
||||
throttle((data: Candle[]) => {
|
||||
setCandleVolume(calcCandleVolume(data));
|
||||
}, constants.DEBOUNCE_UPDATE_TIME)
|
||||
).current;
|
||||
const update = useCallback(
|
||||
({ data }: { data: Candle[] }) => {
|
||||
throttledSetCandles(data);
|
||||
return true;
|
||||
},
|
||||
[throttledSetCandles]
|
||||
);
|
||||
|
||||
useDataProvider<Candle[], Candle>({
|
||||
dataProvider: marketCandlesProvider,
|
||||
update,
|
||||
variables,
|
||||
skip: !marketId || !data,
|
||||
updateOnInit: true,
|
||||
});
|
||||
|
||||
return (
|
||||
<HeaderStat
|
||||
heading={t('Volume (24h)')}
|
||||
testId="market-volume"
|
||||
description={
|
||||
error && candleVolume && data?.positionDecimalPlaces
|
||||
? t('The total amount of assets traded in the last 24 hours.')
|
||||
: null
|
||||
}
|
||||
>
|
||||
{!error && candleVolume && data?.positionDecimalPlaces
|
||||
? addDecimalsFormatNumber(candleVolume, data.positionDecimalPlaces)
|
||||
: '-'}
|
||||
</HeaderStat>
|
||||
);
|
||||
};
|
@ -1,4 +1,10 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { FeesCell } from '@vegaprotocol/market-info';
|
||||
import {
|
||||
calcCandleHigh,
|
||||
calcCandleLow,
|
||||
calcCandleVolume,
|
||||
} from '@vegaprotocol/market-list';
|
||||
import {
|
||||
addDecimalsFormatNumber,
|
||||
PriceCell,
|
||||
@ -12,16 +18,14 @@ import {
|
||||
MarketTradingModeMapping,
|
||||
} from '@vegaprotocol/types';
|
||||
import { PriceCellChange, Sparkline, Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||
import isNil from 'lodash/isNil';
|
||||
import Link from 'next/link';
|
||||
import { calcCandleHigh, calcCandleLow } from '@vegaprotocol/market-list';
|
||||
|
||||
import type { CandleClose } from '@vegaprotocol/types';
|
||||
import type {
|
||||
MarketWithData,
|
||||
MarketWithCandles,
|
||||
} from '@vegaprotocol/market-list';
|
||||
import isNil from 'lodash/isNil';
|
||||
import { FeesCell } from '@vegaprotocol/market-info';
|
||||
|
||||
type Market = MarketWithData & MarketWithCandles;
|
||||
|
||||
export const cellClassNames = 'py-1 first:text-left text-right';
|
||||
@ -103,28 +107,28 @@ const headers: Column[] = [
|
||||
},
|
||||
{
|
||||
kind: ColumnKind.High24,
|
||||
value: t('24h high'),
|
||||
value: t('24h High'),
|
||||
className: `${cellClassNames} hidden xl:table-cell`,
|
||||
onlyOnDetailed: true,
|
||||
},
|
||||
{
|
||||
kind: ColumnKind.Low24,
|
||||
value: t('24h low'),
|
||||
value: t('24h Low'),
|
||||
className: `${cellClassNames} hidden xl:table-cell`,
|
||||
onlyOnDetailed: true,
|
||||
},
|
||||
{
|
||||
kind: ColumnKind.Volume,
|
||||
value: t('24h Volume'),
|
||||
className: `${cellClassNames} hidden lg:table-cell`,
|
||||
onlyOnDetailed: true,
|
||||
},
|
||||
{
|
||||
kind: ColumnKind.TradingMode,
|
||||
value: t('Trading mode'),
|
||||
className: `${cellClassNames} hidden lg:table-cell`,
|
||||
onlyOnDetailed: true,
|
||||
},
|
||||
{
|
||||
kind: ColumnKind.Volume,
|
||||
value: t('Volume'),
|
||||
className: `${cellClassNames} hidden lg:table-cell`,
|
||||
onlyOnDetailed: true,
|
||||
},
|
||||
{
|
||||
kind: ColumnKind.Fee,
|
||||
value: <FeesInfo />,
|
||||
@ -169,6 +173,7 @@ export const columns = (
|
||||
.filter((c: string | undefined): c is CandleClose => !isNil(c));
|
||||
const candleLow = market.candles && calcCandleLow(market.candles);
|
||||
const candleHigh = market.candles && calcCandleHigh(market.candles);
|
||||
const candleVolume = market.candles && calcCandleVolume(market.candles);
|
||||
const selectMarketColumns: Column[] = [
|
||||
{
|
||||
kind: ColumnKind.Market,
|
||||
@ -273,6 +278,19 @@ export const columns = (
|
||||
className: `${cellClassNames} hidden xl:table-cell font-mono`,
|
||||
onlyOnDetailed: true,
|
||||
},
|
||||
{
|
||||
kind: ColumnKind.Volume,
|
||||
value: candleVolume
|
||||
? addDecimalsFormatNumber(
|
||||
candleVolume.toString(),
|
||||
market.positionDecimalPlaces,
|
||||
2
|
||||
)
|
||||
: '-',
|
||||
className: `${cellClassNames} hidden lg:table-cell font-mono`,
|
||||
onlyOnDetailed: true,
|
||||
dataTestId: 'market-volume',
|
||||
},
|
||||
{
|
||||
kind: ColumnKind.TradingMode,
|
||||
value:
|
||||
@ -287,19 +305,6 @@ export const columns = (
|
||||
onlyOnDetailed: true,
|
||||
dataTestId: 'trading-mode-col',
|
||||
},
|
||||
{
|
||||
kind: ColumnKind.Volume,
|
||||
value:
|
||||
market.data?.indicativeVolume && market.data.indicativeVolume !== '0'
|
||||
? addDecimalsFormatNumber(
|
||||
market.data.indicativeVolume,
|
||||
market.positionDecimalPlaces
|
||||
)
|
||||
: '-',
|
||||
className: `${cellClassNames} hidden lg:table-cell font-mono`,
|
||||
onlyOnDetailed: true,
|
||||
dataTestId: 'market-volume',
|
||||
},
|
||||
{
|
||||
kind: ColumnKind.Fee,
|
||||
value: <FeesCell feeFactors={market.fees.factors} />,
|
||||
@ -337,6 +342,7 @@ export const columnsPositionMarkets = (
|
||||
return onSelect(id);
|
||||
}
|
||||
};
|
||||
const candleVolume = market.candles && calcCandleVolume(market.candles);
|
||||
const selectMarketColumns: Column[] = [
|
||||
{
|
||||
kind: ColumnKind.Market,
|
||||
@ -454,6 +460,19 @@ export const columnsPositionMarkets = (
|
||||
className: `${cellClassNames} hidden xl:table-cell font-mono`,
|
||||
onlyOnDetailed: true,
|
||||
},
|
||||
{
|
||||
kind: ColumnKind.Volume,
|
||||
value: candleVolume
|
||||
? addDecimalsFormatNumber(
|
||||
candleVolume.toString(),
|
||||
market.positionDecimalPlaces,
|
||||
2
|
||||
)
|
||||
: '-',
|
||||
className: `${cellClassNames} hidden lg:table-cell font-mono`,
|
||||
onlyOnDetailed: true,
|
||||
dataTestId: 'market-volume',
|
||||
},
|
||||
{
|
||||
kind: ColumnKind.TradingMode,
|
||||
value:
|
||||
@ -468,18 +487,6 @@ export const columnsPositionMarkets = (
|
||||
onlyOnDetailed: true,
|
||||
dataTestId: 'trading-mode-col',
|
||||
},
|
||||
{
|
||||
kind: ColumnKind.Volume,
|
||||
value:
|
||||
market.data && market.data.indicativeVolume !== '0'
|
||||
? addDecimalsFormatNumber(
|
||||
market.data.indicativeVolume,
|
||||
market.positionDecimalPlaces
|
||||
)
|
||||
: '-',
|
||||
className: `${cellClassNames} hidden lg:table-cell font-mono`,
|
||||
onlyOnDetailed: true,
|
||||
},
|
||||
{
|
||||
kind: ColumnKind.Fee,
|
||||
value: <FeesCell feeFactors={market.fees.factors} />,
|
||||
|
@ -33,8 +33,8 @@ import type { OnCellClickHandler } from '../../components/select-market';
|
||||
import type { SingleMarketFieldsFragment } from '@vegaprotocol/market-list';
|
||||
import { Last24hPriceChange } from '../../components/last-24h-price-change';
|
||||
import { MarketMarkPrice } from '../../components/market-mark-price';
|
||||
import { MarketVolume } from '../../components/market-volume';
|
||||
import { MarketTradingModeComponent } from '../../components/market-trading-mode';
|
||||
import { Last24hVolume } from '../../components/last-24h-volume';
|
||||
|
||||
const TradingViews = {
|
||||
Candles: CandlesChartContainer,
|
||||
@ -138,7 +138,7 @@ export const TradeMarketHeader = ({
|
||||
</HeaderStat>
|
||||
<MarketMarkPrice marketId={market.id} />
|
||||
<Last24hPriceChange marketId={market.id} />
|
||||
<MarketVolume marketId={market.id} />
|
||||
<Last24hVolume marketId={market.id} />
|
||||
<MarketTradingModeComponent marketId={market.id} onSelect={onSelect} />
|
||||
{symbol ? (
|
||||
<HeaderStat
|
||||
|
@ -1,9 +1,9 @@
|
||||
import type { Market } from '@vegaprotocol/market-list';
|
||||
import { totalFeesPercentage } from '@vegaprotocol/market-list';
|
||||
import { t, formatNumberPercentage } from '@vegaprotocol/react-helpers';
|
||||
import { formatNumberPercentage, t } from '@vegaprotocol/react-helpers';
|
||||
import { Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
import type { Market } from '@vegaprotocol/market-list';
|
||||
export const FeesCell = ({
|
||||
feeFactors,
|
||||
}: {
|
||||
|
@ -69,3 +69,7 @@ export const calcCandleHigh = (candles: Candle[]): string | undefined => {
|
||||
}, new BigNumber(0))
|
||||
.toString();
|
||||
};
|
||||
|
||||
export const calcCandleVolume = (candles: Candle[]): string | undefined =>
|
||||
candles &&
|
||||
candles.reduce((acc, c) => new BigNumber(acc).plus(c.volume).toString(), '0');
|
||||
|
@ -1,41 +1,42 @@
|
||||
import {
|
||||
OrderTimeInForce,
|
||||
OrderStatus,
|
||||
Side,
|
||||
OrderType,
|
||||
OrderTypeMapping,
|
||||
OrderStatusMapping,
|
||||
OrderTimeInForceMapping,
|
||||
OrderRejectionReasonMapping,
|
||||
} from '@vegaprotocol/types';
|
||||
import {
|
||||
addDecimal,
|
||||
getDateTimeFormat,
|
||||
t,
|
||||
positiveClassNames,
|
||||
negativeClassNames,
|
||||
isNumeric,
|
||||
negativeClassNames,
|
||||
positiveClassNames,
|
||||
t,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import type {
|
||||
VegaICellRendererParams,
|
||||
VegaValueFormatterParams,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
OrderRejectionReasonMapping,
|
||||
OrderStatus,
|
||||
OrderStatusMapping,
|
||||
OrderTimeInForce,
|
||||
OrderTimeInForceMapping,
|
||||
OrderType,
|
||||
OrderTypeMapping,
|
||||
Side,
|
||||
} from '@vegaprotocol/types';
|
||||
import {
|
||||
AgGridDynamic as AgGrid,
|
||||
Button,
|
||||
Intent,
|
||||
Link,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import type { AgGridReact, AgGridReactProps } from 'ag-grid-react';
|
||||
import { AgGridColumn } from 'ag-grid-react';
|
||||
import { forwardRef, useState } from 'react';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { forwardRef, useState } from 'react';
|
||||
|
||||
import { useOrderCancel } from '../../order-hooks/use-order-cancel';
|
||||
import { useOrderEdit } from '../../order-hooks/use-order-edit';
|
||||
import { OrderEditDialog } from './order-edit-dialog';
|
||||
import type { Order } from '../';
|
||||
import { OrderFeedback } from '../order-feedback';
|
||||
import { OrderEditDialog } from './order-edit-dialog';
|
||||
|
||||
import type {
|
||||
VegaICellRendererParams,
|
||||
VegaValueFormatterParams,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import type { AgGridReact, AgGridReactProps } from 'ag-grid-react';
|
||||
import type { Order } from '../';
|
||||
type OrderListProps = AgGridReactProps;
|
||||
|
||||
export const OrderList = forwardRef<AgGridReact, OrderListProps>(
|
||||
@ -113,6 +114,21 @@ export const OrderListTable = forwardRef<AgGridReact, OrderListTableProps>(
|
||||
<AgGridColumn
|
||||
headerName={t('Market')}
|
||||
field="market.tradableInstrument.instrument.code"
|
||||
cellRenderer={({
|
||||
value,
|
||||
data,
|
||||
}: VegaICellRendererParams<
|
||||
Order,
|
||||
'market.tradableInstrument.instrument.code'
|
||||
>) =>
|
||||
data?.market?.id ? (
|
||||
<Link href={`/markets/${data?.market?.id}`} target="_blank">
|
||||
{value}
|
||||
</Link>
|
||||
) : (
|
||||
value
|
||||
)
|
||||
}
|
||||
/>
|
||||
<AgGridColumn
|
||||
headerName={t('Size')}
|
||||
|
Loading…
Reference in New Issue
Block a user