vega-frontend-monorepo/libs/positions/src/lib/positions-data-providers.ts
Bartłomiej Głownia 3bf5d823ee
chore: stagnet3 api updates (#1321)
* chore: update account, fills and orders subscriptions in data providers

* chore: update account, fills and orders subscriptions in data providers

* fixing console-lite

* fixing types in general - regen

* chore: update account, fills and orders subscriptions in data providers

* fixed console-lite unit tests, uncommented quote name

* chore: update account, fills and orders subscriptions in data providers

* type aligning of assets and proposals

* fixes liquidity data provider

* fixed accounts build errors and unit tests

* regen types, removed market name

* regen types

* fixed positions

* chore: handle updates in derived market provider, update orders

* removed taker fee mapping (renamed to maker fee)

* chore: update account, fills and orders subscriptions in data providers

* chore: update account, fills and orders subscriptions in data providers

* chore: fix Order type

* chore: fix possible null types

* chore: revert order-list.stories.tsx

* chore:  derivedDataProvider fixes

* fills unit tests fixes

* orders unit tests fixes

* added eslint ingore for generated files in liquidity

* added unique key to the list element of simple market toolbar

* changed main-branch-name to develop for pr workflows

* removed redundant waitFor

* increased test timeout to 10s

* removed mocked response

* chore: disable simple market list tests

* chore: fix e2e projects mock types

* feat: [subscription-update] - uncomment some console-lite tests

* cypress: trading-accounts

* chore: replace  market candles with candlesConnection

* chore: ignore gql errors, to be reverted after candlesConnectio n will be fixed

* feat: [subscription-update] - improve wrongly renamed hook

* feat: [subscription-update] - improve wrongly renamed hook

* feat: [subscription-update] - improve wrongly renamed hook 3

* chore: add pagination hangdling to derived data provier

* cypress: trading-fills

* feat: [stagnet3 api update] - remove redundant command in tests

* feat: [stagnet3 api update] - remove redundant command in tests

* chore: fix trading orders e2e tests

* chore: fix console lite e2e mocks

* chore: fix market-trade tests

* chore: use markets only in market selector, change Instrument cache policy

* chore: fix market-selector tests

* feat: [subscription-update] - improve ag grid conf for unit tests

* feat: [subscription-update] - improve ag grid conf for unit tests

* chore: fix candle types in console lite market mocks

* chore: revert error policy ignore

* chore: revert jest timeout

* chore: remove unused AccountFields

* chore: revert remove unused AccountFields

* chore: simplify node subscription probe

* chore: remove unused generated types in candles-chart

* chore: improve useMarketsList mock

* feat: [subscription-update] - increase jest timeout

* feat: [subscription-update] - fix jest timeout

* feat: [subscription-update] - fix jest timeout

* feat: [subscription-update] - try to fix failing test again

* chore: fix candles-chart types

* feat: [subscription-update] - temporary skip failing test

* feat: [subscription-update] - temporary skip failing test

* feat: [subscription-update] - temporary skip failing test

* feat: [subscription-update] - fix failling int test

* feat: [subscription-update] - try to restore commented unit tests

* feat: [subscription-update] - try to restore commented unit tests

* feat: [subscription-update] - improve fixed unit tests

Co-authored-by: asiaznik <artur@vegaprotocol.io>
Co-authored-by: maciek <maciek@vegaprotocol.io>
2022-09-20 08:24:28 -07:00

283 lines
7.6 KiB
TypeScript

import { gql } from '@apollo/client';
import produce from 'immer';
import BigNumber from 'bignumber.js';
import sortBy from 'lodash/sortBy';
import type { AccountFieldsFragment } from '@vegaprotocol/accounts';
import { accountsDataProvider } from '@vegaprotocol/accounts';
import { toBigNum } from '@vegaprotocol/react-helpers';
import type { Positions, Positions_party } from './__generated__/Positions';
import {
makeDataProvider,
makeDerivedDataProvider,
} from '@vegaprotocol/react-helpers';
import type {
PositionsSubscription,
PositionsSubscription_positions,
} from './__generated__/PositionsSubscription';
import { AccountType } from '@vegaprotocol/types';
import type { MarketTradingMode } from '@vegaprotocol/types';
export interface Position {
marketName: string;
averageEntryPrice: string;
capitalUtilisation: number;
currentLeverage: number;
decimals: number;
marketDecimalPlaces: number;
positionDecimalPlaces: number;
totalBalance: string;
assetSymbol: string;
liquidationPrice: string;
lowMarginLevel: boolean;
marketId: string;
marketTradingMode: MarketTradingMode;
markPrice: string;
notional: string;
openVolume: string;
realisedPNL: string;
unrealisedPNL: string;
searchPrice: string;
updatedAt: string | null;
}
export interface Data {
party: Positions_party | null;
positions: Position[] | null;
}
const POSITION_FIELDS = gql`
fragment PositionFields on Position {
realisedPNL
openVolume
unrealisedPNL
averageEntryPrice
updatedAt
marginsConnection {
edges {
node {
market {
id
}
maintenanceLevel
searchLevel
initialLevel
collateralReleaseLevel
asset {
symbol
}
}
}
}
market {
id
decimalPlaces
positionDecimalPlaces
tradingMode
tradableInstrument {
instrument {
name
}
}
data {
markPrice
market {
id
}
}
}
}
`;
export const POSITIONS_QUERY = gql`
${POSITION_FIELDS}
query Positions($partyId: ID!) {
party(id: $partyId) {
id
positionsConnection {
edges {
node {
...PositionFields
}
}
}
}
}
`;
export const POSITIONS_SUBSCRIPTION = gql`
${POSITION_FIELDS}
subscription PositionsSubscription($partyId: ID!) {
positions(partyId: $partyId) {
...PositionFields
}
}
`;
export const getMetrics = (
data: Positions_party | null,
accounts: AccountFieldsFragment[] | null
): Position[] => {
if (!data || !data?.positionsConnection?.edges) {
return [];
}
const metrics: Position[] = [];
data?.positionsConnection.edges.forEach((position) => {
const market = position.node.market;
const marketData = market.data;
const marginLevel = position.node.marginsConnection?.edges?.find(
(margin) => margin.node.market.id === market.id
)?.node;
const marginAccount = accounts?.find((account) => {
return account.market?.id === market.id;
});
if (
!marginAccount ||
!marginLevel ||
!marketData ||
position.node.openVolume === '0'
) {
return;
}
const generalAccount = accounts?.find(
(account) =>
account.asset.id === marginAccount.asset.id &&
account.type === AccountType.ACCOUNT_TYPE_GENERAL
);
const decimals = marginAccount.asset.decimals;
const { positionDecimalPlaces, decimalPlaces: marketDecimalPlaces } =
market;
const openVolume = toBigNum(
position.node.openVolume,
positionDecimalPlaces
);
const marginAccountBalance = toBigNum(marginAccount.balance ?? 0, decimals);
const generalAccountBalance = toBigNum(
generalAccount?.balance ?? 0,
decimals
);
const markPrice = toBigNum(marketData.markPrice, marketDecimalPlaces);
const notional = (
openVolume.isGreaterThan(0) ? openVolume : openVolume.multipliedBy(-1)
).multipliedBy(markPrice);
const totalBalance = marginAccountBalance.plus(generalAccountBalance);
const currentLeverage = totalBalance.isEqualTo(0)
? new BigNumber(0)
: notional.dividedBy(totalBalance);
const capitalUtilisation = totalBalance.isEqualTo(0)
? new BigNumber(0)
: marginAccountBalance.dividedBy(totalBalance).multipliedBy(100);
const marginMaintenance = toBigNum(
marginLevel.maintenanceLevel,
marketDecimalPlaces
);
const marginSearch = toBigNum(marginLevel.searchLevel, marketDecimalPlaces);
const marginInitial = toBigNum(
marginLevel.initialLevel,
marketDecimalPlaces
);
const searchPrice = marginSearch
.minus(marginAccountBalance)
.dividedBy(openVolume)
.plus(markPrice);
const liquidationPrice = BigNumber.maximum(
0,
marginMaintenance
.minus(marginAccountBalance)
.minus(generalAccountBalance)
.dividedBy(openVolume)
.plus(markPrice)
);
const lowMarginLevel =
marginAccountBalance.isLessThan(
marginSearch.plus(marginInitial.minus(marginSearch).dividedBy(2))
) && generalAccountBalance.isLessThan(marginInitial.minus(marginSearch));
metrics.push({
marketName: market.tradableInstrument.instrument.name,
averageEntryPrice: position.node.averageEntryPrice,
capitalUtilisation: Math.round(capitalUtilisation.toNumber()),
currentLeverage: currentLeverage.toNumber(),
marketDecimalPlaces,
positionDecimalPlaces,
decimals,
assetSymbol: marginLevel.asset.symbol,
totalBalance: totalBalance.multipliedBy(10 ** decimals).toFixed(),
lowMarginLevel,
liquidationPrice: liquidationPrice
.multipliedBy(10 ** marketDecimalPlaces)
.toFixed(0),
marketId: position.node.market.id,
marketTradingMode: position.node.market.tradingMode,
markPrice: marketData.markPrice,
notional: notional.multipliedBy(10 ** marketDecimalPlaces).toFixed(0),
openVolume: position.node.openVolume,
realisedPNL: position.node.realisedPNL,
unrealisedPNL: position.node.unrealisedPNL,
searchPrice: searchPrice
.multipliedBy(10 ** marketDecimalPlaces)
.toFixed(0),
updatedAt: position.node.updatedAt,
});
});
return metrics;
};
export const update = (
data: Positions_party,
deltas: PositionsSubscription_positions[]
) => {
return produce(data, (draft) => {
deltas.forEach((delta) => {
if (!draft.positionsConnection?.edges || !delta) {
return;
}
const index = draft.positionsConnection.edges.findIndex(
(edge) => edge.node.market.id === delta.market.id
);
if (index !== -1) {
draft.positionsConnection.edges[index].node = delta;
} else {
draft.positionsConnection.edges.push({
__typename: 'PositionEdge',
node: delta,
});
}
});
});
};
export const positionsDataProvider = makeDataProvider<
Positions,
Positions_party,
PositionsSubscription,
PositionsSubscription_positions[]
>({
query: POSITIONS_QUERY,
subscriptionQuery: POSITIONS_SUBSCRIPTION,
update,
getData: (responseData: Positions) => responseData.party,
getDelta: (subscriptionData: PositionsSubscription) =>
subscriptionData.positions,
});
export const positionsMetricsDataProvider = makeDerivedDataProvider<
Position[],
never
>([positionsDataProvider, accountsDataProvider], ([positions, accounts]) => {
return sortBy(
getMetrics(
positions as Positions_party | null,
accounts as AccountFieldsFragment[] | null
),
'updatedAt'
).reverse();
});