Compare commits

...
Author SHA1 Message Date
Madalina Raicu 4dbefa1067 Merge branch 'release/testnet' of github.com:vegaprotocol/frontend-monorepo into release/testnet 2022-11-17 09:21:28 -05:00
m.ray 0a4338ab9a fix: positions table fixes notional dp (#2144)
* fix: update decimals on position notional size

* fix: normalize values

* fix: fix positions unit tests

* fix: remove liquidation price

* fix: positions linting issue

* fix: remove liquidation price test

* fix: remove total summary row

* fix: remove comments

* fix: cypress test to not show trailing 0s

* fix: add back liq. price est as cell only

* fix: remove not used params
2022-11-17 14:19:09 +00:00
6 changed files with 60 additions and 143 deletions
@@ -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);
+1 -7
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>
@@ -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 () => {
@@ -138,23 +138,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 () => {
@@ -171,7 +161,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 () => {
+50 -75
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 { MarketTradingMode } 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 }}
+1 -41
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;
},
@@ -36,6 +36,6 @@ export const progressBarCellRendererSelector = (
params: ICellRendererParams
): CellRendererSelectorResult => {
return {
component: params.node.rowPinned ? EmptyCell : ProgressBarCell,
component: ProgressBarCell,
};
};