diff --git a/apps/trading-e2e/src/integration/trading-deal-ticket-submit-account.cy.ts b/apps/trading-e2e/src/integration/trading-deal-ticket-submit-account.cy.ts
index c964a485a..b240ba7ba 100644
--- a/apps/trading-e2e/src/integration/trading-deal-ticket-submit-account.cy.ts
+++ b/apps/trading-e2e/src/integration/trading-deal-ticket-submit-account.cy.ts
@@ -3,7 +3,7 @@ import { aliasGQLQuery } from '@vegaprotocol/cypress';
import {
accountsQuery,
amendGeneralAccountBalance,
- estimateOrderQuery,
+ estimateFeesQuery,
} from '@vegaprotocol/mock';
import type { OrderSubmission } from '@vegaprotocol/wallet';
import { createOrder } from '../support/create-order';
@@ -49,7 +49,7 @@ describe(
aliasGQLQuery(req, 'Accounts', accounts);
});
cy.mockGQL((req) => {
- aliasGQLQuery(req, 'EstimateOrder', estimateOrderQuery());
+ aliasGQLQuery(req, 'EstimateFee', estimateFeesQuery());
});
cy.mockSubscription();
cy.visit('/#/markets/market-0');
diff --git a/apps/trading-e2e/src/support/trading.ts b/apps/trading-e2e/src/support/trading.ts
index 17b9f35f0..42ab1b8fb 100644
--- a/apps/trading-e2e/src/support/trading.ts
+++ b/apps/trading-e2e/src/support/trading.ts
@@ -10,7 +10,7 @@ import {
chainIdQuery,
chartQuery,
depositsQuery,
- estimateOrderQuery,
+ estimateFeesQuery,
marginsQuery,
marketCandlesQuery,
marketDataQuery,
@@ -157,7 +157,7 @@ const mockTradingPage = (
aliasGQLQuery(req, 'Candles', candlesQuery());
aliasGQLQuery(req, 'Withdrawals', withdrawalsQuery());
aliasGQLQuery(req, 'NetworkParams', networkParamsQuery());
- aliasGQLQuery(req, 'EstimateOrder', estimateOrderQuery());
+ aliasGQLQuery(req, 'EstimateFees', estimateFeesQuery());
aliasGQLQuery(req, 'ProposalsList', proposalListQuery());
aliasGQLQuery(req, 'Deposits', depositsQuery());
};
diff --git a/libs/deal-ticket/src/components/deal-ticket-estimates.tsx b/libs/deal-ticket/src/components/deal-ticket-estimates.tsx
deleted file mode 100644
index 1df6b7834..000000000
--- a/libs/deal-ticket/src/components/deal-ticket-estimates.tsx
+++ /dev/null
@@ -1,132 +0,0 @@
-import React from 'react';
-import type { ReactNode } from 'react';
-import { t } from '@vegaprotocol/i18n';
-import { Icon, Tooltip, TrafficLight } from '@vegaprotocol/ui-toolkit';
-import { IconNames } from '@blueprintjs/icons';
-import * as constants from '../constants';
-
-interface DealTicketEstimatesProps {
- quoteName?: string;
- price?: string;
- estCloseOut?: string;
- estMargin?: string;
- fees?: string;
- notionalSize?: string;
- size?: string;
- slippage?: string;
-}
-
-export const DealTicketEstimates = ({
- price,
- quoteName,
- estCloseOut,
- estMargin,
- fees,
- notionalSize,
- size,
- slippage,
-}: DealTicketEstimatesProps) => (
-
- {size && (
-
- {t('Contracts')}
-
-
- )}
- {price && (
-
- {t('Est. Price')}
-
- {price}
-
- )}
- {notionalSize && (
-
- {t('Est. Position Size')}
-
-
- )}
- {fees && (
-
- {t('Est. Fees')}
-
-
- )}
- {estMargin && (
-
- {t('Est. Margin')}
-
-
- )}
- {estCloseOut && (
-
- {t('Est. Close out')}
-
-
- )}
- {slippage && (
-
- {t('Est. Price Impact / Slippage')}
-
-
- {slippage}%
-
-
-
- )}
-
-);
-
-interface DataTitleProps {
- children: ReactNode;
- quoteName?: string;
-}
-
-export const DataTitle = ({ children, quoteName = '' }: DataTitleProps) => (
-
- {children}
- {quoteName && ({quoteName})}
-
-);
-
-interface ValueTooltipProps {
- value?: string;
- children?: ReactNode;
- description: string;
- id?: string;
-}
-
-export const ValueTooltipRow = ({
- value,
- children,
- description,
- id,
-}: ValueTooltipProps) => (
-
- {value || children}
-
-
-
-
-
-
-);
diff --git a/libs/deal-ticket/src/components/deal-ticket/deal-ticket-fee-details.tsx b/libs/deal-ticket/src/components/deal-ticket/deal-ticket-fee-details.tsx
index 16d2a877c..52969be14 100644
--- a/libs/deal-ticket/src/components/deal-ticket/deal-ticket-fee-details.tsx
+++ b/libs/deal-ticket/src/components/deal-ticket/deal-ticket-fee-details.tsx
@@ -1,24 +1,8 @@
import { Tooltip } from '@vegaprotocol/ui-toolkit';
import classnames from 'classnames';
import type { ReactNode } from 'react';
-import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
-import type { Market, MarketData } from '@vegaprotocol/market-list';
-import {
- getFeeDetailsValues,
- useFeeDealTicketDetails,
-} from '../../hooks/use-fee-deal-ticket-details';
-
-interface DealTicketFeeDetailsProps {
- order: OrderSubmissionBody['orderSubmission'];
- market: Market;
- marketData: MarketData;
- currentInitialMargin?: string;
- currentMaintenanceMargin?: string;
- estimatedInitialMargin: string;
- estimatedTotalInitialMargin: string;
- marginAccountBalance: string;
- generalAccountBalance: string;
-}
+import { getFeeDetailsValues } from '../../hooks/use-fee-deal-ticket-details';
+import type { FeeDetails } from '../../hooks/use-fee-deal-ticket-details';
export interface DealTicketFeeDetailProps {
label: string;
@@ -45,17 +29,8 @@ export const DealTicketFeeDetail = ({
);
-export const DealTicketFeeDetails = ({
- order,
- market,
- marketData,
- ...args
-}: DealTicketFeeDetailsProps) => {
- const feeDetails = useFeeDealTicketDetails(order, market, marketData);
- const details = getFeeDetailsValues({
- ...feeDetails,
- ...args,
- });
+export const DealTicketFeeDetails = (props: FeeDetails) => {
+ const details = getFeeDetailsValues(props);
return (
{details.map(({ label, value, labelDescription, symbol, indent }) => (
diff --git a/libs/deal-ticket/src/components/deal-ticket/deal-ticket.tsx b/libs/deal-ticket/src/components/deal-ticket/deal-ticket.tsx
index 2cba96fd7..87131ec52 100644
--- a/libs/deal-ticket/src/components/deal-ticket/deal-ticket.tsx
+++ b/libs/deal-ticket/src/components/deal-ticket/deal-ticket.tsx
@@ -25,6 +25,16 @@ import {
TinyScroll,
} from '@vegaprotocol/ui-toolkit';
+import {
+ useEstimatePositionQuery,
+ useOpenVolume,
+} from '@vegaprotocol/positions';
+import { addDecimal, toBigNum } from '@vegaprotocol/utils';
+import { activeOrdersProvider } from '@vegaprotocol/orders';
+import { useFeeDealTicketDetails } from '../../hooks/use-fee-deal-ticket-details';
+import { getDerivedPrice } from '../../utils/get-price';
+import type { OrderInfo } from '@vegaprotocol/types';
+
import {
validateExpiration,
validateMarketState,
@@ -34,7 +44,6 @@ import {
} from '../../utils';
import { ZeroBalanceError } from '../deal-ticket-validation/zero-balance-error';
import { SummaryValidationType } from '../../constants';
-import { useInitialMargin } from '../../hooks/use-initial-margin';
import type { Market, MarketData } from '@vegaprotocol/market-list';
import { MarginWarning } from '../deal-ticket-validation/margin-warning';
import {
@@ -104,7 +113,62 @@ export const DealTicket = ({
market.positionDecimalPlaces
);
- const { margin, totalMargin } = useInitialMargin(market.id, normalizedOrder);
+ const price = useMemo(() => {
+ return normalizedOrder && getDerivedPrice(normalizedOrder, marketData);
+ }, [normalizedOrder, marketData]);
+
+ const notionalSize = useMemo(() => {
+ if (price && normalizedOrder?.size) {
+ return toBigNum(normalizedOrder.size, market.positionDecimalPlaces)
+ .multipliedBy(addDecimal(price, market.decimalPlaces))
+ .toString();
+ }
+ return null;
+ }, [
+ price,
+ normalizedOrder?.size,
+ market.decimalPlaces,
+ market.positionDecimalPlaces,
+ ]);
+
+ const estimateFees = useFeeDealTicketDetails(
+ normalizedOrder && { ...normalizedOrder, price }
+ );
+ const { data: activeOrders } = useDataProvider({
+ dataProvider: activeOrdersProvider,
+ variables: { partyId: pubKey || '' },
+ skip: !pubKey,
+ });
+ const openVolume = useOpenVolume(pubKey, market.id) ?? '0';
+ const orders = activeOrders
+ ? activeOrders.map
(({ node: order }) => ({
+ isMarketOrder: order.type === OrderType.TYPE_MARKET,
+ price: order.price,
+ remaining: order.remaining,
+ side: order.side,
+ }))
+ : [];
+ if (normalizedOrder) {
+ orders.push({
+ isMarketOrder: normalizedOrder.type === OrderType.TYPE_MARKET,
+ price: normalizedOrder.price ?? '0',
+ remaining: normalizedOrder.size,
+ side: normalizedOrder.side,
+ });
+ }
+ const { data: positionEstimate } = useEstimatePositionQuery({
+ variables: {
+ marketId: market.id,
+ openVolume,
+ orders,
+ collateralAvailable:
+ marginAccountBalance || generalAccountBalance ? balance : undefined,
+ },
+ skip: !normalizedOrder,
+ });
+
+ const assetSymbol =
+ market.tradableInstrument.instrument.product.settlementAsset.symbol;
const { data: currentMargins } = useDataProvider({
dataProvider: marketMarginDataProvider,
@@ -401,7 +465,10 @@ export const DealTicket = ({
asset={asset}
marketTradingMode={marketData.marketTradingMode}
balance={balance}
- margin={totalMargin}
+ margin={
+ positionEstimate?.estimatePosition?.margin.bestCase.initialLevel ||
+ '0'
+ }
isReadOnly={isReadOnly}
pubKey={pubKey}
onClickCollateral={onClickCollateral}
@@ -413,15 +480,15 @@ export const DealTicket = ({
}
/>
diff --git a/libs/deal-ticket/src/components/index.ts b/libs/deal-ticket/src/components/index.ts
index dbc74f55b..23656b03d 100644
--- a/libs/deal-ticket/src/components/index.ts
+++ b/libs/deal-ticket/src/components/index.ts
@@ -1,4 +1,3 @@
export * from './deal-ticket';
export * from './deal-ticket-validation';
export * from './trading-mode-tooltip';
-export * from './deal-ticket-estimates';
diff --git a/libs/deal-ticket/src/hooks/EstimateOrder.graphql b/libs/deal-ticket/src/hooks/EstimateOrder.graphql
index 88d3a6a4e..d7dcbe99f 100644
--- a/libs/deal-ticket/src/hooks/EstimateOrder.graphql
+++ b/libs/deal-ticket/src/hooks/EstimateOrder.graphql
@@ -1,4 +1,4 @@
-query EstimateOrder(
+query EstimateFees(
$marketId: ID!
$partyId: ID!
$price: String
@@ -8,7 +8,7 @@ query EstimateOrder(
$expiration: Timestamp
$type: OrderType!
) {
- estimateOrder(
+ estimateFees(
marketId: $marketId
partyId: $partyId
price: $price
@@ -18,14 +18,11 @@ query EstimateOrder(
expiration: $expiration
type: $type
) {
- fee {
+ fees {
makerFee
infrastructureFee
liquidityFee
}
- marginLevels {
- initialLevel
- }
totalFeeAmount
}
}
diff --git a/libs/deal-ticket/src/hooks/__generated__/EstimateOrder.ts b/libs/deal-ticket/src/hooks/__generated__/EstimateOrder.ts
index abfddbe98..647e56be9 100644
--- a/libs/deal-ticket/src/hooks/__generated__/EstimateOrder.ts
+++ b/libs/deal-ticket/src/hooks/__generated__/EstimateOrder.ts
@@ -3,7 +3,7 @@ import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
-export type EstimateOrderQueryVariables = Types.Exact<{
+export type EstimateFeesQueryVariables = Types.Exact<{
marketId: Types.Scalars['ID'];
partyId: Types.Scalars['ID'];
price?: Types.InputMaybe;
@@ -15,12 +15,12 @@ export type EstimateOrderQueryVariables = Types.Exact<{
}>;
-export type EstimateOrderQuery = { __typename?: 'Query', estimateOrder: { __typename?: 'OrderEstimate', totalFeeAmount: string, fee: { __typename?: 'TradeFee', makerFee: string, infrastructureFee: string, liquidityFee: string }, marginLevels: { __typename?: 'MarginLevels', initialLevel: string } } };
+export type EstimateFeesQuery = { __typename?: 'Query', estimateFees: { __typename?: 'FeeEstimate', totalFeeAmount: string, fees: { __typename?: 'TradeFee', makerFee: string, infrastructureFee: string, liquidityFee: string } } };
-export const EstimateOrderDocument = gql`
- query EstimateOrder($marketId: ID!, $partyId: ID!, $price: String, $size: String!, $side: Side!, $timeInForce: OrderTimeInForce!, $expiration: Timestamp, $type: OrderType!) {
- estimateOrder(
+export const EstimateFeesDocument = gql`
+ query EstimateFees($marketId: ID!, $partyId: ID!, $price: String, $size: String!, $side: Side!, $timeInForce: OrderTimeInForce!, $expiration: Timestamp, $type: OrderType!) {
+ estimateFees(
marketId: $marketId
partyId: $partyId
price: $price
@@ -30,30 +30,27 @@ export const EstimateOrderDocument = gql`
expiration: $expiration
type: $type
) {
- fee {
+ fees {
makerFee
infrastructureFee
liquidityFee
}
- marginLevels {
- initialLevel
- }
totalFeeAmount
}
}
`;
/**
- * __useEstimateOrderQuery__
+ * __useEstimateFeesQuery__
*
- * To run a query within a React component, call `useEstimateOrderQuery` and pass it any options that fit your needs.
- * When your component renders, `useEstimateOrderQuery` returns an object from Apollo Client that contains loading, error, and data properties
+ * To run a query within a React component, call `useEstimateFeesQuery` and pass it any options that fit your needs.
+ * When your component renders, `useEstimateFeesQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
- * const { data, loading, error } = useEstimateOrderQuery({
+ * const { data, loading, error } = useEstimateFeesQuery({
* variables: {
* marketId: // value for 'marketId'
* partyId: // value for 'partyId'
@@ -66,14 +63,14 @@ export const EstimateOrderDocument = gql`
* },
* });
*/
-export function useEstimateOrderQuery(baseOptions: Apollo.QueryHookOptions) {
+export function useEstimateFeesQuery(baseOptions: Apollo.QueryHookOptions) {
const options = {...defaultOptions, ...baseOptions}
- return Apollo.useQuery(EstimateOrderDocument, options);
+ return Apollo.useQuery(EstimateFeesDocument, options);
}
-export function useEstimateOrderLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) {
+export function useEstimateFeesLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) {
const options = {...defaultOptions, ...baseOptions}
- return Apollo.useLazyQuery(EstimateOrderDocument, options);
+ return Apollo.useLazyQuery(EstimateFeesDocument, options);
}
-export type EstimateOrderQueryHookResult = ReturnType;
-export type EstimateOrderLazyQueryHookResult = ReturnType;
-export type EstimateOrderQueryResult = Apollo.QueryResult;
\ No newline at end of file
+export type EstimateFeesQueryHookResult = ReturnType;
+export type EstimateFeesLazyQueryHookResult = ReturnType;
+export type EstimateFeesQueryResult = Apollo.QueryResult;
\ No newline at end of file
diff --git a/libs/deal-ticket/src/hooks/estimate-order.mock.ts b/libs/deal-ticket/src/hooks/estimate-order.mock.ts
index 3fbf7562b..b644f3bc1 100644
--- a/libs/deal-ticket/src/hooks/estimate-order.mock.ts
+++ b/libs/deal-ticket/src/hooks/estimate-order.mock.ts
@@ -1,21 +1,20 @@
import type { PartialDeep } from 'type-fest';
import merge from 'lodash/merge';
-import type { EstimateOrderQuery } from './__generated__/EstimateOrder';
+import type { EstimateFeesQuery } from './__generated__/EstimateOrder';
-export const estimateOrderQuery = (
- override?: PartialDeep
-): EstimateOrderQuery => {
- const defaultResult: EstimateOrderQuery = {
- estimateOrder: {
- __typename: 'OrderEstimate',
+export const estimateFeesQuery = (
+ override?: PartialDeep
+): EstimateFeesQuery => {
+ const defaultResult: EstimateFeesQuery = {
+ estimateFees: {
+ __typename: 'FeeEstimate',
totalFeeAmount: '0.0006',
- fee: {
+ fees: {
__typename: 'TradeFee',
makerFee: '100000',
infrastructureFee: '100000',
liquidityFee: '100000',
},
- marginLevels: { __typename: 'MarginLevels', initialLevel: '1' },
},
};
return merge(defaultResult, override);
diff --git a/libs/deal-ticket/src/hooks/use-fee-deal-ticket-details.tsx b/libs/deal-ticket/src/hooks/use-fee-deal-ticket-details.tsx
index 726930c3d..0ff4607d1 100644
--- a/libs/deal-ticket/src/hooks/use-fee-deal-ticket-details.tsx
+++ b/libs/deal-ticket/src/hooks/use-fee-deal-ticket-details.tsx
@@ -1,14 +1,9 @@
import { FeesBreakdown } from '@vegaprotocol/market-info';
-import {
- addDecimal,
- addDecimalsFormatNumber,
- formatNumber,
- toBigNum,
-} from '@vegaprotocol/utils';
+import { addDecimalsFormatNumber, formatNumber } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import { useVegaWallet } from '@vegaprotocol/wallet';
-import { useMemo } from 'react';
-import type { Market, MarketData } from '@vegaprotocol/market-list';
+import type { Market } from '@vegaprotocol/market-list';
+import type { EstimatePositionQuery } from '@vegaprotocol/positions';
import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
import {
EST_TOTAL_MARGIN_TOOLTIP_TEXT,
@@ -18,57 +13,28 @@ import {
DEDUCTION_FROM_COLLATERAL_TOOLTIP_TEXT,
TOTAL_MARGIN_AVAILABLE,
} from '../constants';
-import { useMarketAccountBalance } from '@vegaprotocol/accounts';
-import { getDerivedPrice } from '../utils/get-price';
-import { useEstimateOrderQuery } from './__generated__/EstimateOrder';
-import type { EstimateOrderQuery } from './__generated__/EstimateOrder';
+
+import { useEstimateFeesQuery } from './__generated__/EstimateOrder';
+import type { EstimateFeesQuery } from './__generated__/EstimateOrder';
export const useFeeDealTicketDetails = (
- order: OrderSubmissionBody['orderSubmission'],
- market: Market,
- marketData: MarketData
+ order?: OrderSubmissionBody['orderSubmission']
) => {
const { pubKey } = useVegaWallet();
- const { accountBalance } = useMarketAccountBalance(market.id);
- const price = useMemo(() => {
- return getDerivedPrice(order, marketData);
- }, [order, marketData]);
-
- const { data: estMargin } = useEstimateOrderQuery({
- variables: {
- marketId: market.id,
+ const { data } = useEstimateFeesQuery({
+ variables: order && {
+ marketId: order.marketId,
partyId: pubKey || '',
- price,
+ price: order.price,
size: order.size,
side: order.side,
timeInForce: order.timeInForce,
type: order.type,
},
- skip: !pubKey || !market || !order.size || !price,
+ skip: !pubKey || !order?.size || !order?.price,
});
-
- const notionalSize = useMemo(() => {
- if (price && order.size) {
- return toBigNum(order.size, market.positionDecimalPlaces)
- .multipliedBy(addDecimal(price, market.decimalPlaces))
- .toString();
- }
- return null;
- }, [price, order.size, market.decimalPlaces, market.positionDecimalPlaces]);
-
- const assetSymbol =
- market.tradableInstrument.instrument.product.settlementAsset.symbol;
-
- return useMemo(() => {
- return {
- market,
- assetSymbol,
- notionalSize,
- accountBalance,
- estimateOrder: estMargin?.estimateOrder,
- };
- }, [market, assetSymbol, notionalSize, accountBalance, estMargin]);
+ return data?.estimateFees;
};
export interface FeeDetails {
@@ -77,24 +43,25 @@ export interface FeeDetails {
market: Market;
assetSymbol: string;
notionalSize: string | null;
- estimateOrder: EstimateOrderQuery['estimateOrder'] | undefined;
- estimatedInitialMargin: string;
- estimatedTotalInitialMargin: string;
+ estimateFees: EstimateFeesQuery['estimateFees'] | undefined;
currentInitialMargin?: string;
currentMaintenanceMargin?: string;
+ positionEstimate: EstimatePositionQuery['estimatePosition'];
}
export const getFeeDetailsValues = ({
marginAccountBalance,
generalAccountBalance,
assetSymbol,
- estimateOrder,
+ estimateFees,
market,
notionalSize,
- estimatedTotalInitialMargin,
currentInitialMargin,
currentMaintenanceMargin,
+ positionEstimate,
}: FeeDetails) => {
+ const liquidationEstimate = positionEstimate?.liquidation;
+ const marginEstimate = positionEstimate?.margin;
const totalBalance =
BigInt(generalAccountBalance || '0') + BigInt(marginAccountBalance || '0');
const assetDecimals =
@@ -129,8 +96,8 @@ export const getFeeDetailsValues = ({
{
label: t('Fees'),
value:
- estimateOrder?.totalFeeAmount &&
- `~${formatValueWithAssetDp(estimateOrder?.totalFeeAmount)}`,
+ estimateFees?.totalFeeAmount &&
+ `~${formatValueWithAssetDp(estimateFees?.totalFeeAmount)}`,
labelDescription: (
<>
@@ -139,7 +106,7 @@ export const getFeeDetailsValues = ({
)}
0 ? deductionFromCollateral.toString() : '0'
+ deductionFromCollateralBestCase > 0
+ ? deductionFromCollateralBestCase.toString()
+ : '0'
+ )}`,
+ symbol: assetSymbol,
+ labelDescription: DEDUCTION_FROM_COLLATERAL_TOOLTIP_TEXT(assetSymbol),
+ });
+
+ const deductionFromCollateralWorstCase =
+ BigInt(marginEstimate.worstCase.initialLevel) -
+ BigInt(marginAccountBalance);
+
+ details.push({
+ indent: true,
+ label: t('Deduction from collateral worst case'),
+ value: `~${formatValueWithAssetDp(
+ deductionFromCollateralWorstCase > 0
+ ? deductionFromCollateralWorstCase.toString()
+ : '0'
)}`,
symbol: assetSymbol,
labelDescription: DEDUCTION_FROM_COLLATERAL_TOOLTIP_TEXT(assetSymbol),
@@ -198,7 +201,16 @@ export const getFeeDetailsValues = ({
details.push({
label: t('Projected margin'),
- value: `~${formatValueWithAssetDp(estimatedTotalInitialMargin)}`,
+ value: `~${formatValueWithAssetDp(marginEstimate.bestCase.initialLevel)}`,
+ symbol: assetSymbol,
+ labelDescription: EST_TOTAL_MARGIN_TOOLTIP_TEXT,
+ });
+
+ details.push({
+ label: t('Projected margin worst case'),
+ value: `~${formatValueWithAssetDp(
+ marginEstimate.worstCase.initialLevel
+ )}`,
symbol: assetSymbol,
labelDescription: EST_TOTAL_MARGIN_TOOLTIP_TEXT,
});
@@ -209,5 +221,46 @@ export const getFeeDetailsValues = ({
symbol: assetSymbol,
labelDescription: MARGIN_ACCOUNT_TOOLTIP_TEXT,
});
+ if (liquidationEstimate) {
+ const liquidationEstimateBestCaseIncludingBuyOrders = BigInt(
+ liquidationEstimate.bestCase.including_buy_orders.replace(/\..*/, '')
+ );
+ const liquidationEstimateBestCaseIncludingSellOrders = BigInt(
+ liquidationEstimate.bestCase.including_sell_orders.replace(/\..*/, '')
+ );
+ const liquidationEstimateBestCase =
+ liquidationEstimateBestCaseIncludingBuyOrders >
+ liquidationEstimateBestCaseIncludingSellOrders
+ ? liquidationEstimateBestCaseIncludingBuyOrders
+ : liquidationEstimateBestCaseIncludingSellOrders;
+ details.push({
+ label: t('Liquidation price estimate'),
+ value: `${formatValueWithAssetDp(
+ liquidationEstimateBestCase.toString()
+ )}`,
+ symbol: assetSymbol,
+ labelDescription: MARGIN_ACCOUNT_TOOLTIP_TEXT,
+ });
+
+ const liquidationEstimateWorstCaseIncludingBuyOrders = BigInt(
+ liquidationEstimate.worstCase.including_buy_orders.replace(/\..*/, '')
+ );
+ const liquidationEstimateWorstCaseIncludingSellOrders = BigInt(
+ liquidationEstimate.worstCase.including_sell_orders.replace(/\..*/, '')
+ );
+ const liquidationEstimateWorstCase =
+ liquidationEstimateWorstCaseIncludingBuyOrders >
+ liquidationEstimateWorstCaseIncludingSellOrders
+ ? liquidationEstimateWorstCaseIncludingBuyOrders
+ : liquidationEstimateWorstCaseIncludingSellOrders;
+ details.push({
+ label: t('Liquidation price estimate worst case'),
+ value: `${formatValueWithAssetDp(
+ liquidationEstimateWorstCase.toString()
+ )}`,
+ symbol: assetSymbol,
+ labelDescription: MARGIN_ACCOUNT_TOOLTIP_TEXT,
+ });
+ }
return details;
};
diff --git a/libs/deal-ticket/src/hooks/use-initial-margin.ts b/libs/deal-ticket/src/hooks/use-initial-margin.ts
deleted file mode 100644
index 3b0806332..000000000
--- a/libs/deal-ticket/src/hooks/use-initial-margin.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-import { useMemo } from 'react';
-import { useDataProvider } from '@vegaprotocol/data-provider';
-import { useVegaWallet } from '@vegaprotocol/wallet';
-import { marketDataProvider } from '@vegaprotocol/market-list';
-import {
- calculateMargins,
- // getDerivedPrice,
- volumeAndMarginProvider,
-} from '@vegaprotocol/positions';
-import { Side } from '@vegaprotocol/types';
-import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
-import { marketInfoProvider } from '@vegaprotocol/market-info';
-
-export const useInitialMargin = (
- marketId: OrderSubmissionBody['orderSubmission']['marketId'],
- order?: OrderSubmissionBody['orderSubmission']
-) => {
- const { pubKey } = useVegaWallet();
- const { data: marketData } = useDataProvider({
- dataProvider: marketDataProvider,
- variables: { marketId },
- });
- const { data: activeVolumeAndMargin } = useDataProvider({
- dataProvider: volumeAndMarginProvider,
- variables: { marketId, partyId: pubKey || '' },
- skip: !pubKey,
- });
- const { data: marketInfo } = useDataProvider({
- dataProvider: marketInfoProvider,
- variables: { marketId },
- });
- let totalMargin = '0';
- let margin = '0';
- if (marketInfo?.riskFactors && marketData && order) {
- const {
- positionDecimalPlaces,
- decimalPlaces,
- tradableInstrument,
- riskFactors,
- } = marketInfo;
- const { marginCalculator, instrument } = tradableInstrument;
- const { decimals } = instrument.product.settlementAsset;
- margin = totalMargin = calculateMargins({
- side: order.side,
- size: order.size,
- price: marketData.markPrice, // getDerivedPrice(order, marketData), same in positions-data-providers
- positionDecimalPlaces,
- decimalPlaces,
- decimals,
- scalingFactors: marginCalculator?.scalingFactors,
- riskFactors,
- }).initialMargin;
- }
-
- if (activeVolumeAndMargin) {
- let sellMargin = BigInt(activeVolumeAndMargin.sellInitialMargin);
- let buyMargin = BigInt(activeVolumeAndMargin.buyInitialMargin);
- if (order?.side === Side.SIDE_SELL) {
- sellMargin += BigInt(totalMargin);
- } else {
- buyMargin += BigInt(totalMargin);
- }
- totalMargin =
- sellMargin > buyMargin ? sellMargin.toString() : buyMargin.toString();
- }
-
- return useMemo(
- () => ({
- totalMargin,
- margin,
- }),
- [totalMargin, margin]
- );
-};
diff --git a/libs/positions/src/index.ts b/libs/positions/src/index.ts
index 99f406606..e6b3b3a92 100644
--- a/libs/positions/src/index.ts
+++ b/libs/positions/src/index.ts
@@ -2,7 +2,6 @@ export * from './lib/__generated__/Positions';
export * from './lib/positions-container';
export * from './lib/positions-data-providers';
export * from './lib/margin-data-provider';
-export * from './lib/margin-calculator';
export * from './lib/positions-table';
export * from './lib/use-market-margin';
export * from './lib/use-open-volume';
diff --git a/libs/positions/src/lib/Positions.graphql b/libs/positions/src/lib/Positions.graphql
index cc80a0aea..425c053f7 100644
--- a/libs/positions/src/lib/Positions.graphql
+++ b/libs/positions/src/lib/Positions.graphql
@@ -75,3 +75,44 @@ subscription MarginsSubscription($partyId: ID!) {
timestamp
}
}
+
+query EstimatePosition(
+ $marketId: ID!
+ $openVolume: String!
+ $orders: [OrderInfo!]
+ $collateralAvailable: String
+) {
+ estimatePosition(
+ marketId: $marketId
+ openVolume: $openVolume
+ orders: $orders
+ collateralAvailable: $collateralAvailable
+ ) {
+ margin {
+ worstCase {
+ maintenanceLevel
+ searchLevel
+ initialLevel
+ collateralReleaseLevel
+ }
+ bestCase {
+ maintenanceLevel
+ searchLevel
+ initialLevel
+ collateralReleaseLevel
+ }
+ }
+ liquidation {
+ worstCase {
+ open_volume_only
+ including_buy_orders
+ including_sell_orders
+ }
+ bestCase {
+ open_volume_only
+ including_buy_orders
+ including_sell_orders
+ }
+ }
+ }
+}
diff --git a/libs/positions/src/lib/__generated__/Positions.ts b/libs/positions/src/lib/__generated__/Positions.ts
index 96a130c3a..856053d6b 100644
--- a/libs/positions/src/lib/__generated__/Positions.ts
+++ b/libs/positions/src/lib/__generated__/Positions.ts
@@ -35,6 +35,16 @@ export type MarginsSubscriptionSubscriptionVariables = Types.Exact<{
export type MarginsSubscriptionSubscription = { __typename?: 'Subscription', margins: { __typename?: 'MarginLevelsUpdate', marketId: string, asset: string, partyId: string, maintenanceLevel: string, searchLevel: string, initialLevel: string, collateralReleaseLevel: string, timestamp: any } };
+export type EstimatePositionQueryVariables = Types.Exact<{
+ marketId: Types.Scalars['ID'];
+ openVolume: Types.Scalars['String'];
+ orders?: Types.InputMaybe | Types.OrderInfo>;
+ collateralAvailable?: Types.InputMaybe;
+}>;
+
+
+export type EstimatePositionQuery = { __typename?: 'Query', estimatePosition?: { __typename?: 'PositionEstimate', margin: { __typename?: 'MarginEstimate', worstCase: { __typename?: 'MarginLevels', maintenanceLevel: string, searchLevel: string, initialLevel: string, collateralReleaseLevel: string }, bestCase: { __typename?: 'MarginLevels', maintenanceLevel: string, searchLevel: string, initialLevel: string, collateralReleaseLevel: string } }, liquidation?: { __typename?: 'LiquidationEstimate', worstCase: { __typename?: 'LiquidationPrice', open_volume_only: string, including_buy_orders: string, including_sell_orders: string }, bestCase: { __typename?: 'LiquidationPrice', open_volume_only: string, including_buy_orders: string, including_sell_orders: string } } | null } | null };
+
export const PositionFieldsFragmentDoc = gql`
fragment PositionFields on Position {
realisedPNL
@@ -220,4 +230,72 @@ export function useMarginsSubscriptionSubscription(baseOptions: Apollo.Subscript
return Apollo.useSubscription(MarginsSubscriptionDocument, options);
}
export type MarginsSubscriptionSubscriptionHookResult = ReturnType;
-export type MarginsSubscriptionSubscriptionResult = Apollo.SubscriptionResult;
\ No newline at end of file
+export type MarginsSubscriptionSubscriptionResult = Apollo.SubscriptionResult;
+export const EstimatePositionDocument = gql`
+ query EstimatePosition($marketId: ID!, $openVolume: String!, $orders: [OrderInfo!], $collateralAvailable: String) {
+ estimatePosition(
+ marketId: $marketId
+ openVolume: $openVolume
+ orders: $orders
+ collateralAvailable: $collateralAvailable
+ ) {
+ margin {
+ worstCase {
+ maintenanceLevel
+ searchLevel
+ initialLevel
+ collateralReleaseLevel
+ }
+ bestCase {
+ maintenanceLevel
+ searchLevel
+ initialLevel
+ collateralReleaseLevel
+ }
+ }
+ liquidation {
+ worstCase {
+ open_volume_only
+ including_buy_orders
+ including_sell_orders
+ }
+ bestCase {
+ open_volume_only
+ including_buy_orders
+ including_sell_orders
+ }
+ }
+ }
+}
+ `;
+
+/**
+ * __useEstimatePositionQuery__
+ *
+ * To run a query within a React component, call `useEstimatePositionQuery` and pass it any options that fit your needs.
+ * When your component renders, `useEstimatePositionQuery` returns an object from Apollo Client that contains loading, error, and data properties
+ * you can use to render your UI.
+ *
+ * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
+ *
+ * @example
+ * const { data, loading, error } = useEstimatePositionQuery({
+ * variables: {
+ * marketId: // value for 'marketId'
+ * openVolume: // value for 'openVolume'
+ * orders: // value for 'orders'
+ * collateralAvailable: // value for 'collateralAvailable'
+ * },
+ * });
+ */
+export function useEstimatePositionQuery(baseOptions: Apollo.QueryHookOptions) {
+ const options = {...defaultOptions, ...baseOptions}
+ return Apollo.useQuery(EstimatePositionDocument, options);
+ }
+export function useEstimatePositionLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) {
+ const options = {...defaultOptions, ...baseOptions}
+ return Apollo.useLazyQuery(EstimatePositionDocument, options);
+ }
+export type EstimatePositionQueryHookResult = ReturnType;
+export type EstimatePositionLazyQueryHookResult = ReturnType;
+export type EstimatePositionQueryResult = Apollo.QueryResult;
\ No newline at end of file
diff --git a/libs/positions/src/lib/margin-calculator.ts b/libs/positions/src/lib/margin-calculator.ts
deleted file mode 100644
index 522d60e73..000000000
--- a/libs/positions/src/lib/margin-calculator.ts
+++ /dev/null
@@ -1,95 +0,0 @@
-import { toBigNum } from '@vegaprotocol/utils';
-import { Side, MarketTradingMode, OrderType } from '@vegaprotocol/types';
-import type { ScalingFactors, RiskFactor } from '@vegaprotocol/types';
-import type { MarketData } from '@vegaprotocol/market-list';
-
-export const isMarketInAuction = (marketTradingMode: MarketTradingMode) => {
- return [
- MarketTradingMode.TRADING_MODE_BATCH_AUCTION,
- MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
- MarketTradingMode.TRADING_MODE_OPENING_AUCTION,
- ].includes(marketTradingMode);
-};
-
-/**
- * Get the market price based on market mode (auction or not auction)
- */
-export const getMarketPrice = ({
- marketTradingMode,
- indicativePrice,
- markPrice,
-}: Pick) => {
- if (isMarketInAuction(marketTradingMode)) {
- // 0 can never be a valid uncrossing price
- // as it would require there being orders on the book at that price.
- if (
- indicativePrice &&
- indicativePrice !== '0' &&
- BigInt(indicativePrice) !== BigInt(0)
- ) {
- return indicativePrice;
- }
- }
- return markPrice;
-};
-
-/**
- * Gets the price for an order, order limit this is the user
- * entered value, for market this will be the mark price or
- * if in auction the indicative uncrossing price
- */
-export const getDerivedPrice = (
- order: {
- type?: OrderType | null;
- price?: string;
- },
- marketData: Pick<
- MarketData,
- 'marketTradingMode' | 'indicativePrice' | 'markPrice'
- >
-) => {
- // If order type is market we should use either the mark price
- // or the uncrossing price. If order type is limit use the price
- // the user has input
-
- // Use the market price if order is a market order
- if (order.type === OrderType.TYPE_LIMIT && order.price) {
- return order.price;
- }
- return getMarketPrice(marketData);
-};
-
-export const calculateMargins = ({
- size,
- side,
- price,
- decimals,
- positionDecimalPlaces,
- decimalPlaces,
- scalingFactors,
- riskFactors,
-}: {
- size: string;
- side: Side;
- positionDecimalPlaces: number;
- decimalPlaces: number;
- decimals: number;
- price: string;
- scalingFactors?: ScalingFactors;
- riskFactors: RiskFactor;
-}) => {
- const maintenanceMargin = toBigNum(size, positionDecimalPlaces)
- .multipliedBy(
- side === Side.SIDE_SELL ? riskFactors.short : riskFactors.long
- )
- .multipliedBy(toBigNum(price, decimalPlaces));
- return {
- maintenanceMargin: maintenanceMargin
- .multipliedBy(Math.pow(10, decimals))
- .toFixed(0),
- initialMargin: maintenanceMargin
- .multipliedBy(scalingFactors?.initialMargin ?? 1)
- .multipliedBy(Math.pow(10, decimals))
- .toFixed(0),
- };
-};
diff --git a/libs/positions/src/lib/positions-data-providers.ts b/libs/positions/src/lib/positions-data-providers.ts
index c01976a49..a507aa94e 100644
--- a/libs/positions/src/lib/positions-data-providers.ts
+++ b/libs/positions/src/lib/positions-data-providers.ts
@@ -28,14 +28,6 @@ import {
PositionsSubscriptionDocument,
} from './__generated__/Positions';
import { marginsDataProvider } from './margin-data-provider';
-import { calculateMargins } from './margin-calculator';
-import { Side } from '@vegaprotocol/types';
-import { marketInfoProvider } from '@vegaprotocol/market-info';
-import type { MarketInfoQuery } from '@vegaprotocol/market-info';
-import { marketDataProvider } from '@vegaprotocol/market-list';
-import type { MarketData } from '@vegaprotocol/market-list';
-import { activeOrdersProvider } from '@vegaprotocol/orders';
-import type { OrderFieldsFragment } from '@vegaprotocol/orders';
import type { PositionStatus } from '@vegaprotocol/types';
type PositionMarginLevel = Pick<
@@ -336,98 +328,3 @@ export const positionsMetricsProvider = makeDerivedDataProvider<
return !(previousRow && isEqual(previousRow, row));
})
);
-
-export const volumeAndMarginProvider = makeDerivedDataProvider<
- {
- buyVolume: string;
- sellVolume: string;
- buyInitialMargin: string;
- sellInitialMargin: string;
- },
- never,
- PositionsQueryVariables & MarketDataQueryVariables
->(
- [
- (callback, client, { partyId, marketId }) =>
- activeOrdersProvider(callback, client, {
- partyId,
- marketId,
- }),
- (callback, client, { marketId }) =>
- marketDataProvider(callback, client, { marketId }),
- (callback, client, { marketId }) =>
- marketInfoProvider(callback, client, { marketId }),
- openVolumeDataProvider,
- ],
- (data) => {
- const orders = data[0] as (Edge | null)[] | null;
- const marketData = data[1] as MarketData | null;
- const marketInfo = data[2] as MarketInfoQuery['market'];
- let openVolume = (data[3] as string | null) || '0';
- const shortPosition = openVolume?.startsWith('-');
- if (shortPosition) {
- openVolume = openVolume.substring(1);
- }
- let buyVolume = BigInt(shortPosition ? 0 : openVolume);
- let sellVolume = BigInt(shortPosition ? openVolume : 0);
- let buyInitialMargin = BigInt(0);
- let sellInitialMargin = BigInt(0);
- if (marketInfo?.riskFactors && marketData) {
- const {
- positionDecimalPlaces,
- decimalPlaces,
- tradableInstrument,
- riskFactors,
- } = marketInfo;
- const { marginCalculator, instrument } = tradableInstrument;
- const { decimals } = instrument.product.settlementAsset;
- const calculatorParams = {
- positionDecimalPlaces,
- decimalPlaces,
- decimals,
- scalingFactors: marginCalculator?.scalingFactors,
- riskFactors,
- };
- if (openVolume !== '0') {
- const { initialMargin } = calculateMargins({
- side: shortPosition ? Side.SIDE_SELL : Side.SIDE_BUY,
- size: openVolume,
- price: marketData.markPrice,
- ...calculatorParams,
- });
- if (shortPosition) {
- sellInitialMargin += BigInt(initialMargin);
- } else {
- buyInitialMargin += BigInt(initialMargin);
- }
- }
- orders?.forEach((order) => {
- if (!order) {
- return;
- }
- const { side, remaining: size } = order.node;
- const initialMargin = BigInt(
- calculateMargins({
- side,
- size,
- price: marketData.markPrice, //getDerivedPrice(order.node, marketData), same use-initial-margin
- ...calculatorParams,
- }).initialMargin
- );
- if (order.node.side === Side.SIDE_BUY) {
- buyVolume += BigInt(size);
- buyInitialMargin += initialMargin;
- } else {
- sellVolume += BigInt(size);
- sellInitialMargin += initialMargin;
- }
- });
- }
- return {
- buyVolume: buyVolume.toString(),
- sellVolume: sellVolume.toString(),
- buyInitialMargin: buyInitialMargin.toString(),
- sellInitialMargin: sellInitialMargin.toString(),
- };
- }
-);