fix(#2144): fix positions table notional value and remove total row (#2155)

This commit is contained in:
m.ray 2022-11-17 16:35:29 -05:00 committed by GitHub
parent 19d0df8b1e
commit b1fe10e6d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 60 additions and 143 deletions

View File

@ -42,22 +42,20 @@ describe('positions', { tags: '@smoke' }, () => {
cy.wrap($prices).invoke('text').should('not.be.empty');
});
cy.get('[col-id="liquidationPrice"]')
.should('contain.text', '85,093.38') // entry price
.should('contain.text', '0.00'); // liquidation price
cy.get('[col-id="liquidationPrice"]').should('contain.text', '0'); // liquidation price
cy.get('[col-id="currentLeverage"]').should('contain.text', '0.8');
cy.get('[col-id="marginAccountBalance"]') // margin allocated
.should('contain.text', '1,000.00000');
.should('contain.text', '1,000');
cy.get('[col-id="unrealisedPNL"]').each(($unrealisedPnl) => {
cy.wrap($unrealisedPnl).invoke('text').should('not.be.empty');
});
cy.get('[col-id="notional"]').should('contain.text', '276,761.40348'); // Total tDAI position
cy.get('[col-id="realisedPNL"]').should('contain.text', '0.00100'); // Total Realised PNL
cy.get('[col-id="unrealisedPNL"]').should('contain.text', '8.95000'); // Total Unrealised PNL
cy.get('[col-id="realisedPNL"]').should('contain.text', '0.001'); // Total Realised PNL
cy.get('[col-id="unrealisedPNL"]').should('contain.text', '8.95'); // Total Unrealised PNL
});
cy.getByTestId('close-position').should('be.visible').and('have.length', 3);

View File

@ -1,11 +1,6 @@
import { useRef } from 'react';
import { AsyncRenderer, Icon, Intent } from '@vegaprotocol/ui-toolkit';
import {
useClosePosition,
usePositionsData,
PositionsTable,
getSummaryRowData,
} from '../';
import { useClosePosition, usePositionsData, PositionsTable } from '../';
import type { AgGridReact } from 'ag-grid-react';
import { Requested } from './close-position-dialog/requested';
import { Complete } from './close-position-dialog/complete';
@ -34,7 +29,6 @@ export const PositionsManager = ({ partyId }: PositionsManagerProps) => {
<PositionsTable
ref={gridRef}
rowData={data}
pinnedBottomRowData={data ? [getSummaryRowData(data)] : []}
onClose={(position) => submit(position)}
/>
</AsyncRenderer>

View File

@ -92,7 +92,7 @@ it('add color and sign to amount, displays positive notional value', async () =>
expect(cells[2].classList.contains('text-vega-green-dark')).toBeTruthy();
expect(cells[2].classList.contains('text-vega-red-dark')).toBeFalsy();
expect(cells[2].textContent).toEqual('+100');
expect(cells[1].textContent).toEqual('123.00');
expect(cells[1].textContent).toEqual('1,230');
await act(async () => {
result.rerender(
<PositionsTable rowData={[{ ...singleRow, openVolume: '-100' }]} />
@ -102,7 +102,7 @@ it('add color and sign to amount, displays positive notional value', async () =>
expect(cells[2].classList.contains('text-vega-green-dark')).toBeFalsy();
expect(cells[2].classList.contains('text-vega-red-dark')).toBeTruthy();
expect(cells[2].textContent?.startsWith('-100')).toBeTruthy();
expect(cells[1].textContent).toEqual('123.00');
expect(cells[1].textContent).toEqual('1,230');
});
it('displays mark price', async () => {
@ -139,23 +139,13 @@ it("displays properly entry, liquidation price and liquidation bar and it's inte
});
let cells = screen.getAllByRole('gridcell');
const entryPrice = cells[5].firstElementChild?.firstElementChild?.textContent;
const liquidationPrice =
cells[6].firstElementChild?.lastElementChild?.textContent;
const progressBarTrack = cells[6].lastElementChild;
let progressBar = progressBarTrack?.firstElementChild as HTMLElement;
const progressBarWidth = progressBar?.style?.width;
expect(entryPrice).toEqual('13.3');
expect(liquidationPrice).toEqual('8.3');
expect(progressBar.classList.contains('bg-warning')).toEqual(false);
expect(progressBarWidth).toEqual('20%');
await act(async () => {
result.rerender(
<PositionsTable rowData={[{ ...singleRow, lowMarginLevel: true }]} />
);
});
cells = screen.getAllByRole('gridcell');
progressBar = cells[6].lastElementChild?.firstElementChild as HTMLElement;
expect(progressBar?.classList.contains('bg-warning')).toEqual(true);
});
it('displays leverage', async () => {
@ -172,7 +162,7 @@ it('displays allocated margin', async () => {
});
const cells = screen.getAllByRole('gridcell');
const cell = cells[8];
expect(cell.textContent).toEqual('123,456.00');
expect(cell.textContent).toEqual('123,456');
});
it('displays realised and unrealised PNL', async () => {

View File

@ -1,20 +1,16 @@
import classNames from 'classnames';
import { forwardRef } from 'react';
import type { CSSProperties } from 'react';
import type { CellRendererSelectorResult } from 'ag-grid-community';
import type {
ICellRendererParams,
CellRendererSelectorResult,
} from 'ag-grid-community';
import type {
ValueProps as PriceCellProps,
VegaValueFormatterParams,
VegaValueGetterParams,
TypedDataAgGrid,
} from '@vegaprotocol/ui-toolkit';
import { EmptyCell, ProgressBarCell } from '@vegaprotocol/ui-toolkit';
import { ProgressBarCell } from '@vegaprotocol/ui-toolkit';
import {
PriceFlashCell,
addDecimalsFormatNumber,
addDecimalsNormalizeNumber,
volumePrefix,
t,
toBigNum,
@ -29,7 +25,7 @@ import { AgGridColumn } from 'ag-grid-react';
import type { AgGridReact } from 'ag-grid-react';
import type { Position } from './positions-data-providers';
import { Schema } from '@vegaprotocol/types';
import { Intent, Button, TooltipCellComponent } from '@vegaprotocol/ui-toolkit';
import { Button, TooltipCellComponent } from '@vegaprotocol/ui-toolkit';
import { getRowId } from './use-positions-data';
interface Props extends TypedDataAgGrid<Position> {
@ -72,11 +68,11 @@ export const AmountCell = ({ valueFormatted }: AmountCellProps) => {
className={classNames('text-right', signedNumberCssClass(openVolume))}
>
{volumePrefix(
addDecimalsFormatNumber(openVolume, positionDecimalPlaces)
addDecimalsNormalizeNumber(openVolume, positionDecimalPlaces)
)}
</div>
<div className="text-right">
{addDecimalsFormatNumber(notional, marketDecimalPlaces)}
{addDecimalsNormalizeNumber(notional, marketDecimalPlaces)}
</div>
</div>
) : null;
@ -102,27 +98,6 @@ const ButtonCell = ({
);
};
const progressBarValueFormatter = ({
data,
node,
}: VegaValueFormatterParams<Position, 'liquidationPrice'>):
| PriceCellProps['valueFormatted']
| undefined => {
if (!data || node?.rowPinned) {
return undefined;
}
const min = BigInt(data.averageEntryPrice);
const max = BigInt(data.liquidationPrice);
const mid = BigInt(data.markPrice);
const range = max - min;
return {
low: addDecimalsFormatNumber(min.toString(), data.marketDecimalPlaces),
high: addDecimalsFormatNumber(max.toString(), data.marketDecimalPlaces),
value: range ? Number(((mid - min) * BigInt(100)) / range) : 0,
intent: data.lowMarginLevel ? Intent.Warning : undefined,
};
};
export const PositionsTable = forwardRef<AgGridReact, Props>(
({ onClose, ...props }, ref) => {
return (
@ -173,14 +148,17 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
}: VegaValueGetterParams<Position, 'notional'>) => {
return data?.notional === undefined
? undefined
: toBigNum(data?.notional, data.decimals).toNumber();
: toBigNum(data?.notional, data.marketDecimalPlaces).toNumber();
}}
valueFormatter={({
data,
}: VegaValueFormatterParams<Position, 'notional'>) => {
return !data
? undefined
: addDecimalsFormatNumber(data.notional, data.decimals);
: addDecimalsNormalizeNumber(
data.notional,
data.marketDecimalPlaces
);
}}
/>
<AgGridColumn
@ -205,7 +183,7 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
return data?.openVolume === undefined
? undefined
: volumePrefix(
addDecimalsFormatNumber(
addDecimalsNormalizeNumber(
data.openVolume,
data.positionDecimalPlaces
)
@ -216,11 +194,9 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
headerName={t('Mark price')}
field="markPrice"
type="rightAligned"
cellRendererSelector={(
params: ICellRendererParams
): CellRendererSelectorResult => {
cellRendererSelector={(): CellRendererSelectorResult => {
return {
component: params.node.rowPinned ? EmptyCell : PriceFlashCell,
component: PriceFlashCell,
};
}}
filter="agNumberColumnFilter"
@ -237,7 +213,7 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
data,
node,
}: VegaValueFormatterParams<Position, 'markPrice'>) => {
if (!data || node?.rowPinned) {
if (!data) {
return undefined;
}
if (
@ -246,7 +222,7 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
) {
return '-';
}
return addDecimalsFormatNumber(
return addDecimalsNormalizeNumber(
data.markPrice,
data.marketDecimalPlaces
);
@ -257,11 +233,9 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
headerName={t('Entry price')}
field="averageEntryPrice"
type="rightAligned"
cellRendererSelector={(
params: ICellRendererParams
): CellRendererSelectorResult => {
cellRendererSelector={(): CellRendererSelectorResult => {
return {
component: params.node.rowPinned ? EmptyCell : PriceFlashCell,
component: PriceFlashCell,
};
}}
filter="agNumberColumnFilter"
@ -281,10 +255,10 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
}: VegaValueFormatterParams<Position, 'averageEntryPrice'>):
| string
| undefined => {
if (!data || node?.rowPinned) {
if (!data) {
return undefined;
}
return addDecimalsFormatNumber(
return addDecimalsNormalizeNumber(
data.averageEntryPrice,
data.marketDecimalPlaces
);
@ -293,40 +267,45 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
<AgGridColumn
headerName={t('Liquidation price (est)')}
field="liquidationPrice"
flex={2}
headerTooltip={t(
'Liquidation prices are based on the amount of collateral you have available, the risk of your position and the liquidity on the order book. They can change rapidly based on the profit and loss of your positions and any changes to collateral from opening/closing other positions and making deposits/withdrawals.'
)}
type="rightAligned"
cellRendererSelector={(): CellRendererSelectorResult => {
return {
component: PriceFlashCell,
};
}}
filter="agNumberColumnFilter"
valueGetter={({
data,
}: VegaValueGetterParams<Position, 'liquidationPrice'>) => {
return !data
return data?.liquidationPrice === undefined || !data
? undefined
: toBigNum(
data?.liquidationPrice,
data.liquidationPrice,
data.marketDecimalPlaces
).toNumber();
}}
cellRendererSelector={(
params: ICellRendererParams
): CellRendererSelectorResult => {
return {
component: params.node.rowPinned ? EmptyCell : ProgressBarCell,
};
valueFormatter={({
data,
}: VegaValueFormatterParams<Position, 'liquidationPrice'>):
| string
| undefined => {
if (!data) {
return undefined;
}
return addDecimalsNormalizeNumber(
data.liquidationPrice,
data.marketDecimalPlaces
);
}}
valueFormatter={progressBarValueFormatter}
/>
<AgGridColumn
headerName={t('Leverage')}
field="currentLeverage"
type="rightAligned"
filter="agNumberColumnFilter"
cellRendererSelector={(
params: ICellRendererParams
): CellRendererSelectorResult => {
cellRendererSelector={(): CellRendererSelectorResult => {
return {
component: params.node.rowPinned ? EmptyCell : PriceFlashCell,
component: PriceFlashCell,
};
}}
valueFormatter={({
@ -340,11 +319,9 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
field="marginAccountBalance"
type="rightAligned"
filter="agNumberColumnFilter"
cellRendererSelector={(
params: ICellRendererParams
): CellRendererSelectorResult => {
cellRendererSelector={(): CellRendererSelectorResult => {
return {
component: params.node.rowPinned ? EmptyCell : PriceFlashCell,
component: PriceFlashCell,
};
}}
valueGetter={({
@ -360,10 +337,10 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
}: VegaValueFormatterParams<Position, 'marginAccountBalance'>):
| string
| undefined => {
if (!data || node?.rowPinned) {
if (!data) {
return undefined;
}
return addDecimalsFormatNumber(
return addDecimalsNormalizeNumber(
data.marginAccountBalance,
data.decimals
);
@ -387,7 +364,7 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
}: VegaValueFormatterParams<Position, 'realisedPNL'>) => {
return !data
? undefined
: addDecimalsFormatNumber(data.realisedPNL, data.decimals);
: addDecimalsNormalizeNumber(data.realisedPNL, data.decimals);
}}
cellRenderer="PriceFlashCell"
headerTooltip={t(
@ -412,7 +389,7 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
}: VegaValueFormatterParams<Position, 'unrealisedPNL'>) =>
!data
? undefined
: addDecimalsFormatNumber(data.unrealisedPNL, data.decimals)
: addDecimalsNormalizeNumber(data.unrealisedPNL, data.decimals)
}
cellRenderer="PriceFlashCell"
headerTooltip={t(
@ -435,11 +412,9 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
/>
{onClose ? (
<AgGridColumn
cellRendererSelector={(
params: ICellRendererParams
): CellRendererSelectorResult => {
cellRendererSelector={(): CellRendererSelectorResult => {
return {
component: params.node.rowPinned ? EmptyCell : ButtonCell,
component: ButtonCell,
};
}}
cellRendererParams={{ onClick: onClose }}

View File

@ -1,41 +1,13 @@
import { useCallback, useMemo, useRef } from 'react';
import type { RefObject } from 'react';
import { BigNumber } from 'bignumber.js';
import type { AgGridReact } from 'ag-grid-react';
import type { Position } from './positions-data-providers';
import { positionsMetricsProvider } from './positions-data-providers';
import type { PositionsMetricsProviderVariables } from './positions-data-providers';
import { t, toBigNum, useDataProvider } from '@vegaprotocol/react-helpers';
import { useDataProvider } from '@vegaprotocol/react-helpers';
export const getRowId = ({ data }: { data: Position }) => data.marketId;
export const getSummaryRowData = (positions: Position[]) => {
const summaryRow = {
notional: new BigNumber(0),
realisedPNL: BigInt(0),
unrealisedPNL: BigInt(0),
};
positions.forEach((position) => {
summaryRow.notional = summaryRow.notional.plus(
toBigNum(position.notional, position.marketDecimalPlaces)
);
summaryRow.realisedPNL += BigInt(position.realisedPNL);
summaryRow.unrealisedPNL += BigInt(position.unrealisedPNL);
});
const decimals = positions[0]?.decimals || 0;
return {
marketName: t('Total'),
// we are using asset decimals instead of market decimals because each market can have different decimals
notional: summaryRow.notional
.multipliedBy(10 ** decimals)
.toFixed()
.toString(),
realisedPNL: summaryRow.realisedPNL.toString(),
unrealisedPNL: summaryRow.unrealisedPNL.toString(),
decimals,
};
};
export const usePositionsData = (
partyId: string,
gridRef: RefObject<AgGridReact>
@ -76,18 +48,6 @@ export const usePositionsData = (
add,
addIndex: 0,
});
const summaryRowNode = gridRef.current.api.getPinnedBottomRow(0);
if (summaryRowNode && dataRef.current) {
summaryRowNode.data = getSummaryRowData(dataRef.current);
gridRef.current.api.refreshCells({
force: true,
rowNodes: [summaryRowNode],
});
} else {
gridRef.current.api.setPinnedBottomRowData(
dataRef.current ? [getSummaryRowData(dataRef.current)] : []
);
}
}
return true;
},

View File

@ -36,6 +36,6 @@ export const progressBarCellRendererSelector = (
params: ICellRendererParams
): CellRendererSelectorResult => {
return {
component: params.node.rowPinned ? EmptyCell : ProgressBarCell,
component: ProgressBarCell,
};
};