3bf5d823ee
* 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>
283 lines
7.6 KiB
TypeScript
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();
|
|
});
|