From d85f413e4104c9746da35cf7d4d8b4d6d524a957 Mon Sep 17 00:00:00 2001 From: Matthew Russell Date: Tue, 24 Oct 2023 17:30:17 -0700 Subject: [PATCH] chore(trading,positions): update estimatePosition query to use market decimals for liquidation price (#5005) --- libs/accounts/src/lib/accounts-table.tsx | 7 +- .../deal-ticket/deal-ticket-fee-details.tsx | 8 +- libs/positions/src/lib/Positions.graphql | 5 ++ .../src/lib/__generated__/Positions.ts | 1 + .../src/lib/liquidation-price.spec.tsx | 85 +++++++++++++++++++ libs/positions/src/lib/liquidation-price.tsx | 26 +----- libs/positions/src/lib/positions-table.tsx | 6 +- 7 files changed, 103 insertions(+), 35 deletions(-) create mode 100644 libs/positions/src/lib/liquidation-price.spec.tsx diff --git a/libs/accounts/src/lib/accounts-table.tsx b/libs/accounts/src/lib/accounts-table.tsx index b17901dd9..e28f2aeea 100644 --- a/libs/accounts/src/lib/accounts-table.tsx +++ b/libs/accounts/src/lib/accounts-table.tsx @@ -33,7 +33,7 @@ import BigNumber from 'bignumber.js'; import classNames from 'classnames'; import { AccountsActionsDropdown } from './accounts-actions-dropdown'; -const colorClass = (percentageUsed: number, neutral = false) => { +const colorClass = (percentageUsed: number) => { return classNames('text-right', { 'text-vega-orange': percentageUsed >= 75 && percentageUsed < 90, 'text-vega-red': percentageUsed >= 90, @@ -210,7 +210,7 @@ export const AccountTable = ({ }, cellClass: ({ data }) => { const percentageUsed = percentageValue(data?.used, data?.total); - return colorClass(percentageUsed, true); + return colorClass(percentageUsed); }, valueFormatter: ({ value, @@ -270,7 +270,8 @@ export const AccountTable = ({ onClickDeposit && onClickDeposit(assetId); }} > - {t('Deposit')} + {' '} + {t('Deposit')} ); diff --git a/libs/deal-ticket/src/components/deal-ticket/deal-ticket-fee-details.tsx b/libs/deal-ticket/src/components/deal-ticket/deal-ticket-fee-details.tsx index 78ee5243b..12b75efa5 100644 --- a/libs/deal-ticket/src/components/deal-ticket/deal-ticket-fee-details.tsx +++ b/libs/deal-ticket/src/components/deal-ticket/deal-ticket-fee-details.tsx @@ -259,11 +259,9 @@ export const DealTicketMarginDetails = ({ ? liquidationEstimateWorstCaseIncludingBuyOrders : liquidationEstimateWorstCaseIncludingSellOrders; - // The estimate order query API gives us the liquidation price in formatted by asset decimals. - // We need to calculate it with asset decimals, but display it with market decimals precision until the API changes. liquidationPriceEstimate = formatValue( liquidationEstimateWorstCase.toString(), - assetDecimals, + market.decimalPlaces, undefined, market.decimalPlaces ); @@ -276,7 +274,7 @@ export const DealTicketMarginDetails = ({ ? liquidationEstimateBestCase : liquidationEstimateWorstCase ).toString(), - assetDecimals, + market.decimalPlaces, undefined, market.decimalPlaces ); @@ -308,7 +306,7 @@ export const DealTicketMarginDetails = ({ key={'value-dropdown'} className="flex items-center justify-between w-full gap-2" > -
+
{t('Margin required')} diff --git a/libs/positions/src/lib/Positions.graphql b/libs/positions/src/lib/Positions.graphql index 657ca9493..22cf50a68 100644 --- a/libs/positions/src/lib/Positions.graphql +++ b/libs/positions/src/lib/Positions.graphql @@ -49,6 +49,11 @@ query EstimatePosition( openVolume: $openVolume orders: $orders collateralAvailable: $collateralAvailable + # Everywhere in the codebase we expect price values of the underlying to have the right + # number of digits for formatting with market.decimalPlaces. By default the estimatePosition + # query will return a full value requiring formatting using asset.decimals. For consistency + # we can set this variable to true so that we can format with market.decimalPlaces + scaleLiquidationPriceToMarketDecimals: true ) { margin { worstCase { diff --git a/libs/positions/src/lib/__generated__/Positions.ts b/libs/positions/src/lib/__generated__/Positions.ts index 3cd120a3b..e43a9d2e1 100644 --- a/libs/positions/src/lib/__generated__/Positions.ts +++ b/libs/positions/src/lib/__generated__/Positions.ts @@ -130,6 +130,7 @@ export const EstimatePositionDocument = gql` openVolume: $openVolume orders: $orders collateralAvailable: $collateralAvailable + scaleLiquidationPriceToMarketDecimals: true ) { margin { worstCase { diff --git a/libs/positions/src/lib/liquidation-price.spec.tsx b/libs/positions/src/lib/liquidation-price.spec.tsx new file mode 100644 index 000000000..906d23d53 --- /dev/null +++ b/libs/positions/src/lib/liquidation-price.spec.tsx @@ -0,0 +1,85 @@ +import { MockedProvider } from '@apollo/client/testing'; +import type { MockedResponse } from '@apollo/client/testing'; +import { render, screen, within } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { EstimatePositionDocument } from './__generated__/Positions'; +import type { EstimatePositionQuery } from './__generated__/Positions'; +import { LiquidationPrice } from './liquidation-price'; +import { addDecimalsFormatNumber } from '@vegaprotocol/utils'; + +describe('LiquidationPrice', () => { + const props = { + marketId: 'market-id', + openVolume: '100', + collateralAvailable: '1000', + decimalPlaces: 2, + }; + const worstCaseOpenVolume = '200'; + const bestCaseOpenVolume = '100'; + const mock: MockedResponse = { + request: { + query: EstimatePositionDocument, + variables: { + marketId: props.marketId, + openVolume: props.openVolume, + collateralAvailable: props.collateralAvailable, + }, + }, + result: { + data: { + estimatePosition: { + margin: { + worstCase: { + maintenanceLevel: '100', + searchLevel: '100', + initialLevel: '100', + collateralReleaseLevel: '100', + }, + bestCase: { + maintenanceLevel: '100', + searchLevel: '100', + initialLevel: '100', + collateralReleaseLevel: '100', + }, + }, + liquidation: { + worstCase: { + open_volume_only: worstCaseOpenVolume, + including_buy_orders: '100', + including_sell_orders: '100', + }, + bestCase: { + open_volume_only: bestCaseOpenVolume, + including_buy_orders: '100', + including_sell_orders: '100', + }, + }, + }, + }, + }, + }; + + it('correctly formats best and worst case values for the tooltip', async () => { + render( + + + + ); + + expect(screen.getByText('-')).toBeInTheDocument(); + const el = await screen.findByTestId('liquidation-price'); + expect(el).toHaveTextContent( + addDecimalsFormatNumber(worstCaseOpenVolume, props.decimalPlaces) + ); + await userEvent.hover(el); + const tooltip = within(await screen.findByRole('tooltip')); + expect( + tooltip.getByText('Worst case').nextElementSibling + ).toHaveTextContent( + addDecimalsFormatNumber(worstCaseOpenVolume, props.decimalPlaces) + ); + expect(tooltip.getByText('Best case').nextElementSibling).toHaveTextContent( + addDecimalsFormatNumber(bestCaseOpenVolume, props.decimalPlaces) + ); + }); +}); diff --git a/libs/positions/src/lib/liquidation-price.tsx b/libs/positions/src/lib/liquidation-price.tsx index 283dc6d6a..5370d1475 100644 --- a/libs/positions/src/lib/liquidation-price.tsx +++ b/libs/positions/src/lib/liquidation-price.tsx @@ -8,20 +8,12 @@ export const LiquidationPrice = ({ openVolume, collateralAvailable, decimalPlaces, - formatDecimals, }: { marketId: string; openVolume: string; collateralAvailable: string; decimalPlaces: number; - formatDecimals: number; }) => { - // NOTE! - // - // The estimate order query API gives us the liquidation price unformatted but expecting to be converted - // using asset decimal placse. - // - // We need to convert it with asset decimals, but display it formatted with market decimals precision until the API changes. const { data: currentData, previousData } = useEstimatePositionQuery({ variables: { marketId, @@ -38,21 +30,11 @@ export const LiquidationPrice = ({ return -; } - let bestCase = '-'; - let worstCase = '-'; + let bestCase = data.estimatePosition.liquidation.bestCase.open_volume_only; + let worstCase = data.estimatePosition.liquidation.worstCase.open_volume_only; - bestCase = - data.estimatePosition?.liquidation?.bestCase.open_volume_only.replace( - /\..*/, - '' - ); - worstCase = - data.estimatePosition?.liquidation?.worstCase.open_volume_only.replace( - /\..*/, - '' - ); - worstCase = addDecimalsFormatNumber(worstCase, decimalPlaces, formatDecimals); - bestCase = addDecimalsFormatNumber(bestCase, decimalPlaces, formatDecimals); + worstCase = addDecimalsFormatNumber(worstCase, decimalPlaces, decimalPlaces); + bestCase = addDecimalsFormatNumber(bestCase, decimalPlaces, decimalPlaces); return ( ); },