feat(positions): positions losses and maintenance warnings (#2985)
This commit is contained in:
parent
56f48671cc
commit
f54a629179
@ -4,6 +4,8 @@ fragment PositionFields on Position {
|
||||
unrealisedPNL
|
||||
averageEntryPrice
|
||||
updatedAt
|
||||
positionStatus
|
||||
lossSocializationAmount
|
||||
market {
|
||||
id
|
||||
}
|
||||
@ -30,6 +32,8 @@ subscription PositionsSubscription($partyId: ID!) {
|
||||
averageEntryPrice
|
||||
updatedAt
|
||||
marketId
|
||||
lossSocializationAmount
|
||||
positionStatus
|
||||
}
|
||||
}
|
||||
|
||||
|
10
libs/positions/src/lib/__generated__/Positions.ts
generated
10
libs/positions/src/lib/__generated__/Positions.ts
generated
@ -3,21 +3,21 @@ import * as Types from '@vegaprotocol/types';
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type PositionFieldsFragment = { __typename?: 'Position', realisedPNL: string, openVolume: string, unrealisedPNL: string, averageEntryPrice: string, updatedAt?: any | null, market: { __typename?: 'Market', id: string } };
|
||||
export type PositionFieldsFragment = { __typename?: 'Position', realisedPNL: string, openVolume: string, unrealisedPNL: string, averageEntryPrice: string, updatedAt?: any | null, positionStatus: Types.PositionStatus, lossSocializationAmount: string, market: { __typename?: 'Market', id: string } };
|
||||
|
||||
export type PositionsQueryVariables = Types.Exact<{
|
||||
partyId: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type PositionsQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, positionsConnection?: { __typename?: 'PositionConnection', edges?: Array<{ __typename?: 'PositionEdge', node: { __typename?: 'Position', realisedPNL: string, openVolume: string, unrealisedPNL: string, averageEntryPrice: string, updatedAt?: any | null, market: { __typename?: 'Market', id: string } } }> | null } | null } | null };
|
||||
export type PositionsQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, positionsConnection?: { __typename?: 'PositionConnection', edges?: Array<{ __typename?: 'PositionEdge', node: { __typename?: 'Position', realisedPNL: string, openVolume: string, unrealisedPNL: string, averageEntryPrice: string, updatedAt?: any | null, positionStatus: Types.PositionStatus, lossSocializationAmount: string, market: { __typename?: 'Market', id: string } } }> | null } | null } | null };
|
||||
|
||||
export type PositionsSubscriptionSubscriptionVariables = Types.Exact<{
|
||||
partyId: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type PositionsSubscriptionSubscription = { __typename?: 'Subscription', positions: Array<{ __typename?: 'PositionUpdate', realisedPNL: string, openVolume: string, unrealisedPNL: string, averageEntryPrice: string, updatedAt?: any | null, marketId: string }> };
|
||||
export type PositionsSubscriptionSubscription = { __typename?: 'Subscription', positions: Array<{ __typename?: 'PositionUpdate', realisedPNL: string, openVolume: string, unrealisedPNL: string, averageEntryPrice: string, updatedAt?: any | null, marketId: string, lossSocializationAmount: string, positionStatus: Types.PositionStatus }> };
|
||||
|
||||
export type MarginFieldsFragment = { __typename?: 'MarginLevels', maintenanceLevel: string, searchLevel: string, initialLevel: string, collateralReleaseLevel: string, asset: { __typename?: 'Asset', id: string }, market: { __typename?: 'Market', id: string } };
|
||||
|
||||
@ -42,6 +42,8 @@ export const PositionFieldsFragmentDoc = gql`
|
||||
unrealisedPNL
|
||||
averageEntryPrice
|
||||
updatedAt
|
||||
positionStatus
|
||||
lossSocializationAmount
|
||||
market {
|
||||
id
|
||||
}
|
||||
@ -112,6 +114,8 @@ export const PositionsSubscriptionDocument = gql`
|
||||
averageEntryPrice
|
||||
updatedAt
|
||||
marketId
|
||||
lossSocializationAmount
|
||||
positionStatus
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
@ -6,6 +6,7 @@ import type {
|
||||
MarginFieldsFragment,
|
||||
} from './__generated__/Positions';
|
||||
import { getMetrics, rejoinPositionData } from './positions-data-providers';
|
||||
import { PositionStatus } from '@vegaprotocol/types';
|
||||
|
||||
const accounts = [
|
||||
{
|
||||
@ -78,6 +79,8 @@ const positions: PositionFieldsFragment[] = [
|
||||
__typename: 'Market',
|
||||
id: '5e6035fe6a6df78c9ec44b333c231e63d357acef0a0620d2c243f5865d1dc0d8',
|
||||
},
|
||||
lossSocializationAmount: '0',
|
||||
positionStatus: PositionStatus.POSITION_STATUS_UNSPECIFIED,
|
||||
},
|
||||
{
|
||||
__typename: 'Position',
|
||||
@ -90,6 +93,8 @@ const positions: PositionFieldsFragment[] = [
|
||||
__typename: 'Market',
|
||||
id: '10c4b1114d2f6fda239b73d018bca55888b6018f0ac70029972a17fea0a6a56e',
|
||||
},
|
||||
lossSocializationAmount: '100',
|
||||
positionStatus: PositionStatus.POSITION_STATUS_ORDERS_CLOSED,
|
||||
},
|
||||
];
|
||||
|
||||
@ -225,6 +230,10 @@ describe('getMetrics && rejoinPositionData', () => {
|
||||
expect(metrics[0].totalBalance).toEqual('926178496');
|
||||
expect(metrics[0].unrealisedPNL).toEqual('43804770');
|
||||
expect(metrics[0].updatedAt).toEqual('2022-07-28T14:53:54.725477Z');
|
||||
expect(metrics[0].lossSocializationAmount).toEqual(
|
||||
positions[0].lossSocializationAmount
|
||||
);
|
||||
expect(metrics[0].status).toEqual(positions[0].positionStatus);
|
||||
|
||||
expect(metrics[1].assetSymbol).toEqual('tDAI');
|
||||
expect(metrics[1].averageEntryPrice).toEqual('840158');
|
||||
@ -248,5 +257,9 @@ describe('getMetrics && rejoinPositionData', () => {
|
||||
expect(metrics[1].totalBalance).toEqual('896098819');
|
||||
expect(metrics[1].unrealisedPNL).toEqual('-9112700');
|
||||
expect(metrics[1].updatedAt).toEqual('2022-07-28T15:09:34.441143Z');
|
||||
expect(metrics[1].lossSocializationAmount).toEqual(
|
||||
positions[1].lossSocializationAmount
|
||||
);
|
||||
expect(metrics[1].status).toEqual(positions[1].positionStatus);
|
||||
});
|
||||
});
|
||||
|
@ -24,6 +24,7 @@ import {
|
||||
PositionsSubscriptionDocument,
|
||||
} from './__generated__/Positions';
|
||||
import { marginsDataProvider } from './margin-data-provider';
|
||||
import type { PositionStatus } from '@vegaprotocol/types';
|
||||
|
||||
type PositionMarginLevel = Pick<
|
||||
MarginFieldsFragment,
|
||||
@ -38,6 +39,8 @@ interface PositionRejoined {
|
||||
updatedAt?: string | null;
|
||||
market: MarketMaybeWithData | null;
|
||||
margins: PositionMarginLevel | null;
|
||||
lossSocializationAmount: string | null;
|
||||
status: PositionStatus;
|
||||
}
|
||||
|
||||
export interface Position {
|
||||
@ -62,6 +65,8 @@ export interface Position {
|
||||
unrealisedPNL: string;
|
||||
searchPrice: string | undefined;
|
||||
updatedAt: string | null;
|
||||
lossSocializationAmount: string;
|
||||
status: PositionStatus;
|
||||
}
|
||||
|
||||
export interface Data {
|
||||
@ -178,6 +183,8 @@ export const getMetrics = (
|
||||
? searchPrice.multipliedBy(10 ** marketDecimalPlaces).toFixed(0)
|
||||
: undefined,
|
||||
updatedAt: position.updatedAt || null,
|
||||
lossSocializationAmount: position.lossSocializationAmount || '0',
|
||||
status: position.status,
|
||||
});
|
||||
});
|
||||
return metrics;
|
||||
@ -201,6 +208,8 @@ export const update = (
|
||||
openVolume: delta.openVolume,
|
||||
averageEntryPrice: delta.averageEntryPrice,
|
||||
updatedAt: delta.updatedAt,
|
||||
lossSocializationAmount: delta.lossSocializationAmount,
|
||||
positionStatus: delta.positionStatus,
|
||||
};
|
||||
} else {
|
||||
draft.unshift({
|
||||
@ -267,6 +276,8 @@ export const rejoinPositionData = (
|
||||
market:
|
||||
marketsData?.find((market) => market.id === node.market.id) || null,
|
||||
margins: upgradeMarginsConnection(node.market.id, margins),
|
||||
lossSocializationAmount: node.lossSocializationAmount,
|
||||
status: node.positionStatus,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
@ -1,8 +1,11 @@
|
||||
import type { RenderResult } from '@testing-library/react';
|
||||
import { act, render, screen } from '@testing-library/react';
|
||||
import PositionsTable from './positions-table';
|
||||
import { act, render, screen, within } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import PositionsTable, { OpenVolumeCell, PNLCell } from './positions-table';
|
||||
import type { Position } from './positions-data-providers';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { PositionStatus, PositionStatusMapping } from '@vegaprotocol/types';
|
||||
import type { ICellRendererParams } from 'ag-grid-community';
|
||||
|
||||
const singleRow: Position = {
|
||||
marketName: 'ETH/BTC (31 july 2022)',
|
||||
@ -26,6 +29,8 @@ const singleRow: Position = {
|
||||
searchPrice: '0',
|
||||
updatedAt: '2022-07-27T15:02:58.400Z',
|
||||
marginAccountBalance: '12345600',
|
||||
status: PositionStatus.POSITION_STATUS_UNSPECIFIED,
|
||||
lossSocializationAmount: '0',
|
||||
};
|
||||
|
||||
const singleRowData = [singleRow];
|
||||
@ -219,3 +224,97 @@ it('do not display close button if openVolume is zero', async () => {
|
||||
const cells = screen.getAllByRole('gridcell');
|
||||
expect(cells[12].textContent).toEqual('');
|
||||
});
|
||||
|
||||
describe('PNLCell', () => {
|
||||
const props = {
|
||||
data: undefined,
|
||||
valueFormatted: '100',
|
||||
};
|
||||
it('renders a dash if no data', () => {
|
||||
render(<PNLCell {...(props as ICellRendererParams)} />);
|
||||
expect(screen.getByText('-')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders value if no loss socialisation has occurred', () => {
|
||||
const props = {
|
||||
data: {
|
||||
...singleRow,
|
||||
lossSocialisationAmount: '0',
|
||||
},
|
||||
valueFormatted: '100',
|
||||
};
|
||||
render(<PNLCell {...(props as ICellRendererParams)} />);
|
||||
expect(screen.getByText(props.valueFormatted)).toBeInTheDocument();
|
||||
expect(screen.queryByRole('img')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders value with warning tooltip if loss socialisation occurred', async () => {
|
||||
const props = {
|
||||
data: {
|
||||
...singleRow,
|
||||
lossSocializationAmount: '500',
|
||||
decimals: 2,
|
||||
},
|
||||
valueFormatted: '100',
|
||||
};
|
||||
render(<PNLCell {...(props as ICellRendererParams)} />);
|
||||
const content = screen.getByText(props.valueFormatted);
|
||||
expect(content).toBeInTheDocument();
|
||||
expect(screen.getByRole('img')).toBeInTheDocument();
|
||||
|
||||
await userEvent.hover(content);
|
||||
const tooltip = await screen.findByRole('tooltip');
|
||||
expect(tooltip).toBeInTheDocument();
|
||||
expect(
|
||||
// using within as radix renders tooltip content twice
|
||||
within(tooltip).getByText('Lifetime loss socialisation deductions: 5.00')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('OpenVolumeCell', () => {
|
||||
const props = {
|
||||
data: undefined,
|
||||
valueFormatted: '100',
|
||||
};
|
||||
it('renders a dash if no data', () => {
|
||||
render(<OpenVolumeCell {...(props as ICellRendererParams)} />);
|
||||
expect(screen.getByText('-')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders value if no status is normal', () => {
|
||||
const props = {
|
||||
data: {
|
||||
...singleRow,
|
||||
status: PositionStatus.POSITION_STATUS_UNSPECIFIED,
|
||||
},
|
||||
valueFormatted: '100',
|
||||
};
|
||||
render(<OpenVolumeCell {...(props as ICellRendererParams)} />);
|
||||
expect(screen.getByText(props.valueFormatted)).toBeInTheDocument();
|
||||
expect(screen.queryByRole('img')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders status with warning tooltip if not normal', async () => {
|
||||
const props = {
|
||||
data: {
|
||||
...singleRow,
|
||||
status: PositionStatus.POSITION_STATUS_ORDERS_CLOSED,
|
||||
},
|
||||
valueFormatted: '100',
|
||||
};
|
||||
render(<OpenVolumeCell {...(props as ICellRendererParams)} />);
|
||||
const content = screen.getByText(props.valueFormatted);
|
||||
expect(content).toBeInTheDocument();
|
||||
expect(screen.getByRole('img')).toBeInTheDocument();
|
||||
await userEvent.hover(content);
|
||||
const tooltip = await screen.findByRole('tooltip');
|
||||
expect(tooltip).toBeInTheDocument();
|
||||
expect(
|
||||
// using within as radix renders tooltip content twice
|
||||
within(tooltip).getByText(
|
||||
`Status: ${PositionStatusMapping[props.data.status]}`
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
import classNames from 'classnames';
|
||||
import { forwardRef } from 'react';
|
||||
import type { CSSProperties } from 'react';
|
||||
import type { CSSProperties, ReactNode } from 'react';
|
||||
import type { CellRendererSelectorResult } from 'ag-grid-community';
|
||||
import type {
|
||||
VegaValueFormatterParams,
|
||||
@ -15,6 +15,15 @@ import {
|
||||
signedNumberCssClass,
|
||||
signedNumberCssClassRules,
|
||||
} from '@vegaprotocol/datagrid';
|
||||
import {
|
||||
ButtonLink,
|
||||
Tooltip,
|
||||
TooltipCellComponent,
|
||||
Link,
|
||||
ExternalLink,
|
||||
Icon,
|
||||
ProgressBarCell,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
volumePrefix,
|
||||
toBigNum,
|
||||
@ -27,13 +36,9 @@ import { AgGridColumn } from 'ag-grid-react';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import type { Position } from './positions-data-providers';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import {
|
||||
ButtonLink,
|
||||
Link,
|
||||
ProgressBarCell,
|
||||
TooltipCellComponent,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { getRowId } from './use-positions-data';
|
||||
import { PositionStatus, PositionStatusMapping } from '@vegaprotocol/types';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
|
||||
interface Props extends TypedDataAgGrid<Position> {
|
||||
onClose?: (data: Position) => void;
|
||||
@ -167,6 +172,7 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
|
||||
)
|
||||
);
|
||||
}}
|
||||
cellRenderer={OpenVolumeCell}
|
||||
/>
|
||||
<AgGridColumn
|
||||
headerName={t('Mark price')}
|
||||
@ -311,7 +317,6 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
|
||||
}}
|
||||
valueFormatter={({
|
||||
data,
|
||||
node,
|
||||
}: VegaValueFormatterParams<Position, 'marginAccountBalance'>):
|
||||
| string
|
||||
| undefined => {
|
||||
@ -329,7 +334,7 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
|
||||
field="realisedPNL"
|
||||
type="rightAligned"
|
||||
cellClassRules={signedNumberCssClassRules}
|
||||
cellClass="text-right font-mono"
|
||||
cellClass="font-mono text-right"
|
||||
filter="agNumberColumnFilter"
|
||||
valueGetter={({
|
||||
data,
|
||||
@ -348,13 +353,14 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
|
||||
headerTooltip={t(
|
||||
'Profit or loss is realised whenever your position is reduced to zero and the margin is released back to your collateral balance. P&L excludes any fees paid.'
|
||||
)}
|
||||
cellRenderer={PNLCell}
|
||||
/>
|
||||
<AgGridColumn
|
||||
headerName={t('Unrealised PNL')}
|
||||
field="unrealisedPNL"
|
||||
type="rightAligned"
|
||||
cellClassRules={signedNumberCssClassRules}
|
||||
cellClass="text-right font-mono"
|
||||
cellClass="font-mono text-right"
|
||||
filter="agNumberColumnFilter"
|
||||
valueGetter={({
|
||||
data,
|
||||
@ -373,6 +379,7 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
|
||||
headerTooltip={t(
|
||||
'Unrealised profit is the current profit on your open position. Margin is still allocated to your position.'
|
||||
)}
|
||||
cellRenderer={PNLCell}
|
||||
/>
|
||||
<AgGridColumn
|
||||
headerName={t('Updated')}
|
||||
@ -409,3 +416,106 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
|
||||
);
|
||||
|
||||
export default PositionsTable;
|
||||
|
||||
export const PNLCell = ({
|
||||
valueFormatted,
|
||||
data,
|
||||
}: VegaICellRendererParams<Position, 'realisedPNL'>) => {
|
||||
const { VEGA_DOCS_URL } = useEnvironment();
|
||||
|
||||
if (!data) {
|
||||
return <>-</>;
|
||||
}
|
||||
|
||||
const losses = parseInt(data?.lossSocializationAmount ?? '0');
|
||||
if (losses <= 0) {
|
||||
// eslint-disable-next-line react/jsx-no-useless-fragment
|
||||
return <>{valueFormatted}</>;
|
||||
}
|
||||
|
||||
const lossesFormatted = addDecimalsFormatNumber(
|
||||
data.lossSocializationAmount,
|
||||
data.decimals
|
||||
);
|
||||
|
||||
return (
|
||||
<WarningCell
|
||||
tooltipContent={
|
||||
<>
|
||||
<p className="mb-2">
|
||||
{t('Lifetime loss socialisation deductions: %s', lossesFormatted)}
|
||||
</p>
|
||||
{VEGA_DOCS_URL && (
|
||||
<ExternalLink href={VEGA_DOCS_URL}>
|
||||
{t('Read more about loss socialisation')}
|
||||
</ExternalLink>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
>
|
||||
{valueFormatted}
|
||||
</WarningCell>
|
||||
);
|
||||
};
|
||||
|
||||
export const OpenVolumeCell = ({
|
||||
valueFormatted,
|
||||
data,
|
||||
}: VegaICellRendererParams<Position, 'openVolume'>) => {
|
||||
const { VEGA_DOCS_URL } = useEnvironment();
|
||||
|
||||
if (!data) {
|
||||
return <>-</>;
|
||||
}
|
||||
|
||||
if (data.status === PositionStatus.POSITION_STATUS_UNSPECIFIED) {
|
||||
// eslint-disable-next-line react/jsx-no-useless-fragment
|
||||
return <>{valueFormatted}</>;
|
||||
}
|
||||
|
||||
return (
|
||||
<WarningCell
|
||||
tooltipContent={
|
||||
<>
|
||||
<p className="mb-2">
|
||||
{t('Your position was affected by market conditions')}
|
||||
</p>
|
||||
<p className="mb-2">
|
||||
{t(
|
||||
'Status: %s',
|
||||
PositionStatusMapping[
|
||||
PositionStatus.POSITION_STATUS_ORDERS_CLOSED
|
||||
]
|
||||
)}
|
||||
</p>
|
||||
{VEGA_DOCS_URL && (
|
||||
<ExternalLink href={VEGA_DOCS_URL}>
|
||||
{t('Read more about position resolution')}
|
||||
</ExternalLink>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
>
|
||||
{valueFormatted}
|
||||
</WarningCell>
|
||||
);
|
||||
};
|
||||
|
||||
const WarningCell = ({
|
||||
children,
|
||||
tooltipContent,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
tooltipContent: ReactNode;
|
||||
}) => {
|
||||
return (
|
||||
<Tooltip description={tooltipContent}>
|
||||
<div className="w-full flex items-center justify-between underline decoration-dashed underline-offest-2">
|
||||
<span className="text-black dark:text-white mr-1">
|
||||
<Icon name="warning-sign" size={3} />
|
||||
</span>
|
||||
<span className="text-ellipsis overflow-hidden">{children}</span>
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { PositionStatus } from '@vegaprotocol/types';
|
||||
import merge from 'lodash/merge';
|
||||
import type { PartialDeep } from 'type-fest';
|
||||
import type {
|
||||
@ -58,6 +59,8 @@ const positionFields: PositionFieldsFragment[] = [
|
||||
id: 'market-0',
|
||||
__typename: 'Market',
|
||||
},
|
||||
lossSocializationAmount: '0',
|
||||
positionStatus: PositionStatus.POSITION_STATUS_UNSPECIFIED,
|
||||
},
|
||||
{
|
||||
__typename: 'Position',
|
||||
@ -70,8 +73,11 @@ const positionFields: PositionFieldsFragment[] = [
|
||||
id: 'market-1',
|
||||
__typename: 'Market',
|
||||
},
|
||||
lossSocializationAmount: '0',
|
||||
positionStatus: PositionStatus.POSITION_STATUS_UNSPECIFIED,
|
||||
},
|
||||
{
|
||||
__typename: 'Position',
|
||||
realisedPNL: '230000',
|
||||
openVolume: '1',
|
||||
unrealisedPNL: '-22519',
|
||||
@ -81,7 +87,8 @@ const positionFields: PositionFieldsFragment[] = [
|
||||
id: 'market-2',
|
||||
__typename: 'Market',
|
||||
},
|
||||
__typename: 'Position',
|
||||
lossSocializationAmount: '0',
|
||||
positionStatus: PositionStatus.POSITION_STATUS_UNSPECIFIED,
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -1 +1,4 @@
|
||||
import '@testing-library/jest-dom';
|
||||
import ResizeObserver from 'resize-observer-polyfill';
|
||||
|
||||
global.ResizeObserver = ResizeObserver;
|
||||
|
@ -12,6 +12,7 @@ import type {
|
||||
OrderStatus,
|
||||
OrderTimeInForce,
|
||||
OrderType,
|
||||
PositionStatus,
|
||||
ProposalRejectionReason,
|
||||
ProposalState,
|
||||
Side,
|
||||
@ -445,3 +446,11 @@ export const DispatchMetricLabels: DispatchMetricLabel = {
|
||||
DISPATCH_METRIC_MAKER_FEES_RECEIVED: 'Price maker fees earned',
|
||||
DISPATCH_METRIC_MARKET_VALUE: 'Total market Value',
|
||||
};
|
||||
|
||||
export const PositionStatusMapping: {
|
||||
[T in PositionStatus]: string;
|
||||
} = {
|
||||
POSITION_STATUS_CLOSED_OUT: 'Closed by network',
|
||||
POSITION_STATUS_ORDERS_CLOSED: 'Maintained by network',
|
||||
POSITION_STATUS_UNSPECIFIED: 'Normal',
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user