feat: hook up new EstimatePosition and EstimateFees api methods
This commit is contained in:
@@ -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');
|
||||
|
||||
@@ -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());
|
||||
};
|
||||
|
||||
@@ -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) => (
|
||||
<dl className="text-black dark:text-white">
|
||||
{size && (
|
||||
<div className="flex justify-between mb-2">
|
||||
<DataTitle>{t('Contracts')}</DataTitle>
|
||||
<ValueTooltipRow
|
||||
value={size}
|
||||
description={constants.CONTRACTS_MARGIN_TOOLTIP_TEXT}
|
||||
id="contracts_tooltip_trigger"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{price && (
|
||||
<div className="flex justify-between mb-2">
|
||||
<DataTitle>{t('Est. Price')}</DataTitle>
|
||||
<dd>{price}</dd>
|
||||
</div>
|
||||
)}
|
||||
{notionalSize && (
|
||||
<div className="flex justify-between mb-2">
|
||||
<DataTitle quoteName={quoteName}>{t('Est. Position Size')}</DataTitle>
|
||||
<ValueTooltipRow
|
||||
value={notionalSize}
|
||||
description={constants.NOTIONAL_SIZE_TOOLTIP_TEXT(quoteName || '')}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{fees && (
|
||||
<div className="flex justify-between mb-2">
|
||||
<DataTitle quoteName={quoteName}>{t('Est. Fees')}</DataTitle>
|
||||
<ValueTooltipRow
|
||||
value={fees}
|
||||
description={constants.EST_FEES_TOOLTIP_TEXT}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{estMargin && (
|
||||
<div className="flex justify-between mb-2">
|
||||
<DataTitle quoteName={quoteName}>{t('Est. Margin')}</DataTitle>
|
||||
<ValueTooltipRow
|
||||
value={estMargin}
|
||||
description={constants.EST_MARGIN_TOOLTIP_TEXT(quoteName || '')}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{estCloseOut && (
|
||||
<div className="flex justify-between mb-2">
|
||||
<DataTitle quoteName={quoteName}>{t('Est. Close out')}</DataTitle>
|
||||
<ValueTooltipRow
|
||||
value={estCloseOut}
|
||||
description={constants.EST_CLOSEOUT_TOOLTIP_TEXT(quoteName || '')}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{slippage && (
|
||||
<div className="flex justify-between mb-2">
|
||||
<DataTitle>{t('Est. Price Impact / Slippage')}</DataTitle>
|
||||
<ValueTooltipRow description={constants.EST_SLIPPAGE}>
|
||||
<TrafficLight value={parseFloat(slippage)} q1={1} q2={5}>
|
||||
{slippage}%
|
||||
</TrafficLight>
|
||||
</ValueTooltipRow>
|
||||
</div>
|
||||
)}
|
||||
</dl>
|
||||
);
|
||||
|
||||
interface DataTitleProps {
|
||||
children: ReactNode;
|
||||
quoteName?: string;
|
||||
}
|
||||
|
||||
export const DataTitle = ({ children, quoteName = '' }: DataTitleProps) => (
|
||||
<dt>
|
||||
{children}
|
||||
{quoteName && <small> ({quoteName})</small>}
|
||||
</dt>
|
||||
);
|
||||
|
||||
interface ValueTooltipProps {
|
||||
value?: string;
|
||||
children?: ReactNode;
|
||||
description: string;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
export const ValueTooltipRow = ({
|
||||
value,
|
||||
children,
|
||||
description,
|
||||
id,
|
||||
}: ValueTooltipProps) => (
|
||||
<dd className="flex gap-x-2 items-center">
|
||||
{value || children}
|
||||
<Tooltip align="center" description={description}>
|
||||
<div className="cursor-help" id={id || ''} tabIndex={-1}>
|
||||
<Icon
|
||||
name={IconNames.ISSUE}
|
||||
className="block rotate-180"
|
||||
ariaLabel={description}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</dd>
|
||||
);
|
||||
@@ -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 = ({
|
||||
</div>
|
||||
);
|
||||
|
||||
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 (
|
||||
<div>
|
||||
{details.map(({ label, value, labelDescription, symbol, indent }) => (
|
||||
|
||||
@@ -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<OrderInfo>(({ 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 = ({
|
||||
}
|
||||
/>
|
||||
<DealTicketFeeDetails
|
||||
order={normalizedOrder}
|
||||
market={market}
|
||||
marketData={marketData}
|
||||
estimatedInitialMargin={margin}
|
||||
estimatedTotalInitialMargin={totalMargin}
|
||||
currentInitialMargin={currentMargins?.initialLevel}
|
||||
currentMaintenanceMargin={currentMargins?.maintenanceLevel}
|
||||
estimateFees={estimateFees}
|
||||
notionalSize={notionalSize}
|
||||
assetSymbol={assetSymbol}
|
||||
marginAccountBalance={marginAccountBalance}
|
||||
generalAccountBalance={generalAccountBalance}
|
||||
positionEstimate={positionEstimate?.estimatePosition}
|
||||
market={market}
|
||||
currentInitialMargin={currentMargins?.initialLevel}
|
||||
currentMaintenanceMargin={currentMargins?.maintenanceLevel}
|
||||
/>
|
||||
</form>
|
||||
</TinyScroll>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
export * from './deal-ticket';
|
||||
export * from './deal-ticket-validation';
|
||||
export * from './trading-mode-tooltip';
|
||||
export * from './deal-ticket-estimates';
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
+17
-20
@@ -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<Types.Scalars['String']>;
|
||||
@@ -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<EstimateOrderQuery, EstimateOrderQueryVariables>) {
|
||||
export function useEstimateFeesQuery(baseOptions: Apollo.QueryHookOptions<EstimateFeesQuery, EstimateFeesQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<EstimateOrderQuery, EstimateOrderQueryVariables>(EstimateOrderDocument, options);
|
||||
return Apollo.useQuery<EstimateFeesQuery, EstimateFeesQueryVariables>(EstimateFeesDocument, options);
|
||||
}
|
||||
export function useEstimateOrderLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<EstimateOrderQuery, EstimateOrderQueryVariables>) {
|
||||
export function useEstimateFeesLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<EstimateFeesQuery, EstimateFeesQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<EstimateOrderQuery, EstimateOrderQueryVariables>(EstimateOrderDocument, options);
|
||||
return Apollo.useLazyQuery<EstimateFeesQuery, EstimateFeesQueryVariables>(EstimateFeesDocument, options);
|
||||
}
|
||||
export type EstimateOrderQueryHookResult = ReturnType<typeof useEstimateOrderQuery>;
|
||||
export type EstimateOrderLazyQueryHookResult = ReturnType<typeof useEstimateOrderLazyQuery>;
|
||||
export type EstimateOrderQueryResult = Apollo.QueryResult<EstimateOrderQuery, EstimateOrderQueryVariables>;
|
||||
export type EstimateFeesQueryHookResult = ReturnType<typeof useEstimateFeesQuery>;
|
||||
export type EstimateFeesLazyQueryHookResult = ReturnType<typeof useEstimateFeesLazyQuery>;
|
||||
export type EstimateFeesQueryResult = Apollo.QueryResult<EstimateFeesQuery, EstimateFeesQueryVariables>;
|
||||
@@ -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>
|
||||
): EstimateOrderQuery => {
|
||||
const defaultResult: EstimateOrderQuery = {
|
||||
estimateOrder: {
|
||||
__typename: 'OrderEstimate',
|
||||
export const estimateFeesQuery = (
|
||||
override?: PartialDeep<EstimateFeesQuery>
|
||||
): 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);
|
||||
|
||||
@@ -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: (
|
||||
<>
|
||||
<span>
|
||||
@@ -139,7 +106,7 @@ export const getFeeDetailsValues = ({
|
||||
)}
|
||||
</span>
|
||||
<FeesBreakdown
|
||||
fees={estimateOrder?.fee}
|
||||
fees={estimateFees?.fees}
|
||||
feeFactors={market.fees.factors}
|
||||
symbol={assetSymbol}
|
||||
decimals={assetDecimals}
|
||||
@@ -148,19 +115,35 @@ export const getFeeDetailsValues = ({
|
||||
),
|
||||
symbol: assetSymbol,
|
||||
},
|
||||
{
|
||||
];
|
||||
if (marginEstimate) {
|
||||
details.push({
|
||||
label: t('Margin required'),
|
||||
value: `~${formatValueWithAssetDp(
|
||||
currentInitialMargin
|
||||
? (
|
||||
BigInt(estimatedTotalInitialMargin) - BigInt(currentInitialMargin)
|
||||
BigInt(marginEstimate.bestCase.initialLevel) -
|
||||
BigInt(currentInitialMargin)
|
||||
).toString()
|
||||
: estimatedTotalInitialMargin
|
||||
: marginEstimate.bestCase.initialLevel
|
||||
)}`,
|
||||
symbol: assetSymbol,
|
||||
labelDescription: MARGIN_DIFF_TOOLTIP_TEXT(assetSymbol),
|
||||
},
|
||||
];
|
||||
});
|
||||
details.push({
|
||||
label: t('Margin required worst case'),
|
||||
value: `~${formatValueWithAssetDp(
|
||||
currentInitialMargin
|
||||
? (
|
||||
BigInt(marginEstimate.worstCase.initialLevel) -
|
||||
BigInt(currentInitialMargin)
|
||||
).toString()
|
||||
: marginEstimate.worstCase.initialLevel
|
||||
)}`,
|
||||
symbol: assetSymbol,
|
||||
labelDescription: MARGIN_DIFF_TOOLTIP_TEXT(assetSymbol),
|
||||
});
|
||||
}
|
||||
if (totalBalance) {
|
||||
const totalMarginAvailable = (
|
||||
currentMaintenanceMargin
|
||||
@@ -180,16 +163,36 @@ export const getFeeDetailsValues = ({
|
||||
assetSymbol
|
||||
),
|
||||
});
|
||||
|
||||
}
|
||||
if (marginEstimate) {
|
||||
if (marginAccountBalance) {
|
||||
const deductionFromCollateral =
|
||||
BigInt(estimatedTotalInitialMargin) - BigInt(marginAccountBalance);
|
||||
const deductionFromCollateralBestCase =
|
||||
BigInt(marginEstimate.bestCase.initialLevel) -
|
||||
BigInt(marginAccountBalance);
|
||||
|
||||
details.push({
|
||||
indent: true,
|
||||
label: t('Deduction from collateral'),
|
||||
value: `~${formatValueWithAssetDp(
|
||||
deductionFromCollateral > 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;
|
||||
};
|
||||
|
||||
@@ -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]
|
||||
);
|
||||
};
|
||||
@@ -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';
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+79
-1
@@ -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<Array<Types.OrderInfo> | Types.OrderInfo>;
|
||||
collateralAvailable?: Types.InputMaybe<Types.Scalars['String']>;
|
||||
}>;
|
||||
|
||||
|
||||
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<MarginsSubscriptionSubscription, MarginsSubscriptionSubscriptionVariables>(MarginsSubscriptionDocument, options);
|
||||
}
|
||||
export type MarginsSubscriptionSubscriptionHookResult = ReturnType<typeof useMarginsSubscriptionSubscription>;
|
||||
export type MarginsSubscriptionSubscriptionResult = Apollo.SubscriptionResult<MarginsSubscriptionSubscription>;
|
||||
export type MarginsSubscriptionSubscriptionResult = Apollo.SubscriptionResult<MarginsSubscriptionSubscription>;
|
||||
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<EstimatePositionQuery, EstimatePositionQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<EstimatePositionQuery, EstimatePositionQueryVariables>(EstimatePositionDocument, options);
|
||||
}
|
||||
export function useEstimatePositionLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<EstimatePositionQuery, EstimatePositionQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<EstimatePositionQuery, EstimatePositionQueryVariables>(EstimatePositionDocument, options);
|
||||
}
|
||||
export type EstimatePositionQueryHookResult = ReturnType<typeof useEstimatePositionQuery>;
|
||||
export type EstimatePositionLazyQueryHookResult = ReturnType<typeof useEstimatePositionLazyQuery>;
|
||||
export type EstimatePositionQueryResult = Apollo.QueryResult<EstimatePositionQuery, EstimatePositionQueryVariables>;
|
||||
@@ -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<MarketData, 'marketTradingMode' | 'indicativePrice' | 'markPrice'>) => {
|
||||
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),
|
||||
};
|
||||
};
|
||||
@@ -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<OrderFieldsFragment> | 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(),
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user