diff --git a/apps/console-lite/src/app/components/deal-ticket/deal-ticket-steps.tsx b/apps/console-lite/src/app/components/deal-ticket/deal-ticket-steps.tsx index 9767a5a64..aa4aff3f4 100644 --- a/apps/console-lite/src/app/components/deal-ticket/deal-ticket-steps.tsx +++ b/apps/console-lite/src/app/components/deal-ticket/deal-ticket-steps.tsx @@ -70,11 +70,6 @@ export const DealTicketSteps = ({ market }: DealTicketMarketProps) => { const step = toDecimal(market.positionDecimalPlaces); const order = watch(); const { pubKey } = useVegaWallet(); - const estMargin = useOrderMargin({ - order, - market, - partyId: pubKey || '', - }); const { message: invalidText, isDisabled } = useOrderValidation({ market, order, @@ -127,11 +122,18 @@ export const DealTicketSteps = ({ market }: DealTicketMarketProps) => { ); return new BigNumber(market?.depth?.lastTrade?.price) .multipliedBy(multiplier) - .toNumber(); + .toString(); } - return null; + return undefined; }, [market?.depth?.lastTrade?.price, order.side, slippage]); + const estMargin = useOrderMargin({ + order, + market, + partyId: pubKey || '', + derivedPrice: price, + }); + const formattedPrice = price && addDecimalsFormatNumber(price, market.decimalPlaces); diff --git a/libs/deal-ticket/src/components/deal-ticket-validation/margin-warning.tsx b/libs/deal-ticket/src/components/deal-ticket-validation/margin-warning.tsx index 621bb4230..12e556cc7 100644 --- a/libs/deal-ticket/src/components/deal-ticket-validation/margin-warning.tsx +++ b/libs/deal-ticket/src/components/deal-ticket-validation/margin-warning.tsx @@ -17,7 +17,7 @@ export const MarginWarning = ({ margin, balance, asset }: Props) => { return ( <>
diff --git a/libs/deal-ticket/src/components/deal-ticket/deal-ticket-market-amount.tsx b/libs/deal-ticket/src/components/deal-ticket/deal-ticket-market-amount.tsx index 4a3df131d..6ad576f73 100644 --- a/libs/deal-ticket/src/components/deal-ticket/deal-ticket-market-amount.tsx +++ b/libs/deal-ticket/src/components/deal-ticket/deal-ticket-market-amount.tsx @@ -5,8 +5,8 @@ import { } from '@vegaprotocol/react-helpers'; import { Input, InputError, Tooltip } from '@vegaprotocol/ui-toolkit'; import { isMarketInAuction, validateAmount } from '../../utils'; - import type { DealTicketAmountProps } from './deal-ticket-amount'; +import { getMarketPrice } from '../../utils/get-price'; export type DealTicketMarketAmountProps = Omit< DealTicketAmountProps, @@ -21,21 +21,7 @@ export const DealTicketMarketAmount = ({ const quoteName = market.tradableInstrument.instrument.product.settlementAsset.symbol; const sizeStep = toDecimal(market?.positionDecimalPlaces); - - let price; - if (isMarketInAuction(market)) { - // 0 can never be a valid uncrossing price - // as it would require there being orders on the book at that price. - if ( - market.data?.indicativePrice && - market.data.indicativePrice !== '0' && - BigInt(market.data?.indicativePrice) !== BigInt(0) - ) { - price = market.data.indicativePrice; - } - } else { - price = market.depth?.lastTrade?.price; - } + const price = getMarketPrice(market); const priceFormatted = price ? addDecimalsFormatNumber(price, market.decimalPlaces) diff --git a/libs/deal-ticket/src/components/deal-ticket/deal-ticket.spec.tsx b/libs/deal-ticket/src/components/deal-ticket/deal-ticket.spec.tsx index 4d43d714c..cecd7c476 100644 --- a/libs/deal-ticket/src/components/deal-ticket/deal-ticket.spec.tsx +++ b/libs/deal-ticket/src/components/deal-ticket/deal-ticket.spec.tsx @@ -69,7 +69,7 @@ describe('DealTicket', () => { // Assert last price is shown expect(screen.getByTestId('last-price')).toHaveTextContent( // eslint-disable-next-line - `~${addDecimal(market!.depth!.lastTrade!.price, market.decimalPlaces)} ${ + `~${addDecimal(market!.data.markPrice, market.decimalPlaces)} ${ market.tradableInstrument.instrument.product.settlementAsset.symbol }` ); 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 91905e2d0..22d7a2415 100644 --- a/libs/deal-ticket/src/components/deal-ticket/deal-ticket.tsx +++ b/libs/deal-ticket/src/components/deal-ticket/deal-ticket.tsx @@ -63,9 +63,11 @@ export const DealTicket = ({ const order = watch(); // When order state changes persist it in local storage useEffect(() => setPersistedOrder(order), [order, setPersistedOrder]); + const hasNoBalance = useHasNoBalance( market.tradableInstrument.instrument.product.settlementAsset.id ); + const onSubmit = useCallback( (order: OrderSubmissionBody['orderSubmission']) => { if (!pubKey) { 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 e8b721f08..c19194646 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,5 +1,6 @@ import { FeesBreakdown } from '@vegaprotocol/market-info'; import { + addDecimal, addDecimalsFormatNumber, formatNumber, t, @@ -20,34 +21,39 @@ import { useCalculateSlippage } from './use-calculate-slippage'; import { useOrderCloseOut } from './use-order-closeout'; import { useOrderMargin } from './use-order-margin'; import type { OrderMargin } from './use-order-margin'; +import { getDerivedPrice } from '../utils/get-price'; export const useFeeDealTicketDetails = ( order: OrderSubmissionBody['orderSubmission'], market: MarketDealTicket ) => { const { pubKey } = useVegaWallet(); - const slippage = useCalculateSlippage({ marketId: market.id, order }); - const price = useMemo(() => { - const estPrice = order.price || market.data.markPrice; - if (estPrice) { + const derivedPrice = useMemo(() => { + return getDerivedPrice(order, market); + }, [order, market]); + + // Note this isn't currently used anywhere + const slippageAdjustedPrice = useMemo(() => { + if (derivedPrice) { if (slippage && parseFloat(slippage) !== 0) { const isLong = order.side === Schema.Side.SIDE_BUY; const multiplier = new BigNumber(1)[isLong ? 'plus' : 'minus']( parseFloat(slippage) / 100 ); - return new BigNumber(estPrice).multipliedBy(multiplier).toNumber(); + return new BigNumber(derivedPrice).multipliedBy(multiplier).toNumber(); } - return order.price; + return derivedPrice; } return null; - }, [market.data.markPrice, order.price, order.side, slippage]); + }, [derivedPrice, order.side, slippage]); const estMargin: OrderMargin | null = useOrderMargin({ order, market, partyId: pubKey || '', + derivedPrice, }); const { data: partyBalance } = usePartyBalanceQuery({ @@ -62,11 +68,13 @@ export const useFeeDealTicketDetails = ( }); const notionalSize = useMemo(() => { - if (order.price && order.size) { - return new BigNumber(order.size).multipliedBy(order.price).toString(); + if (derivedPrice && order.size) { + return new BigNumber(order.size) + .multipliedBy(addDecimal(derivedPrice, market.decimalPlaces)) + .toString(); } return null; - }, [order.price, order.size]); + }, [derivedPrice, order.size, market.decimalPlaces]); const quoteName = market.tradableInstrument.instrument.product.quoteName; @@ -78,7 +86,7 @@ export const useFeeDealTicketDetails = ( estMargin, estCloseOut, slippage, - price, + slippageAdjustedPrice, partyData: partyBalance, }; }, [ @@ -88,7 +96,7 @@ export const useFeeDealTicketDetails = ( estMargin, estCloseOut, slippage, - price, + slippageAdjustedPrice, partyBalance, ]); }; @@ -100,7 +108,6 @@ export interface FeeDetails { estMargin: OrderMargin | null; estCloseOut: string | null; slippage: string | null; - price?: string | number | null; } export const getFeeDetailsValues = ({ diff --git a/libs/deal-ticket/src/hooks/use-order-margin.spec.ts b/libs/deal-ticket/src/hooks/use-order-margin.spec.ts index 477798377..258f75f54 100644 --- a/libs/deal-ticket/src/hooks/use-order-margin.spec.ts +++ b/libs/deal-ticket/src/hooks/use-order-margin.spec.ts @@ -5,6 +5,7 @@ import type { PositionMargin } from './use-market-positions'; import type { Props } from './use-order-margin'; import { useOrderMargin } from './use-order-margin'; import { Schema } from '@vegaprotocol/types'; +import type { MarketDealTicket } from '@vegaprotocol/market-list'; let mockEstimateData = { estimateOrder: { @@ -58,7 +59,7 @@ describe('useOrderMargin', () => { indicativePrice: '100', markPrice: '200', }, - }, + } as MarketDealTicket, partyId: 'partyId', }; diff --git a/libs/deal-ticket/src/hooks/use-order-margin.ts b/libs/deal-ticket/src/hooks/use-order-margin.ts index 173534bf7..4353777d1 100644 --- a/libs/deal-ticket/src/hooks/use-order-margin.ts +++ b/libs/deal-ticket/src/hooks/use-order-margin.ts @@ -1,27 +1,16 @@ import { BigNumber } from 'bignumber.js'; import type { OrderSubmissionBody } from '@vegaprotocol/wallet'; -import { Schema } from '@vegaprotocol/types'; import { removeDecimal } from '@vegaprotocol/react-helpers'; import { useMarketPositions } from './use-market-positions'; import type { EstimateOrderQuery } from './__generated__/EstimateOrder'; import { useEstimateOrderQuery } from './__generated__/EstimateOrder'; -import { isMarketInAuction } from '../utils'; - -interface Market { - id: string; - decimalPlaces: number; - positionDecimalPlaces: number; - tradingMode: Schema.MarketTradingMode; - data: { - indicativePrice: string; - markPrice: string; - }; -} +import type { MarketDealTicket } from '@vegaprotocol/market-list'; export interface Props { order: OrderSubmissionBody['orderSubmission']; - market: Market; + market: MarketDealTicket; partyId: string; + derivedPrice?: string; } const addFees = (feeObj: EstimateOrderQuery['estimateOrder']['fee']) => { @@ -44,21 +33,21 @@ export const useOrderMargin = ({ order, market, partyId, + derivedPrice, }: Props): OrderMargin | null => { const marketPositions = useMarketPositions({ marketId: market.id, partyId }); - const priceForEstimate = getPriceForEstimate(order, market); const { data } = useEstimateOrderQuery({ variables: { marketId: market.id, partyId, - price: priceForEstimate, + price: derivedPrice, size: removeDecimal(order.size, market.positionDecimalPlaces), side: order.side, timeInForce: order.timeInForce, type: order.type, }, - skip: !partyId || !market.id || !order.size || !priceForEstimate, + skip: !partyId || !market.id || !order.size || !derivedPrice, }); if (data?.estimateOrder.marginLevels.initialLevel) { @@ -86,32 +75,3 @@ export const useOrderMargin = ({ } return null; }; - -/** - * Gets a price to use for estimating order margin and fees. - * Market orders should use the markPrice or if in auction mode - * the indicative price. If its a limit order use user input price. - */ -const getPriceForEstimate = ( - order: { - type: Schema.OrderType; - price?: string | undefined; - }, - market: Market -) => { - // 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 - let price; - if (order.type === Schema.OrderType.TYPE_LIMIT && order.price) { - price = removeDecimal(order.price, market.decimalPlaces); - } else { - if (isMarketInAuction(market)) { - price = market.data.indicativePrice; - } else { - price = market.data.markPrice; - } - } - - return price === '0' ? undefined : price; -}; diff --git a/libs/deal-ticket/src/utils/get-price.ts b/libs/deal-ticket/src/utils/get-price.ts new file mode 100644 index 000000000..908f7c6e9 --- /dev/null +++ b/libs/deal-ticket/src/utils/get-price.ts @@ -0,0 +1,51 @@ +import type { MarketDealTicket } from '@vegaprotocol/market-list'; +import { removeDecimal } from '@vegaprotocol/react-helpers'; +import { Schema } from '@vegaprotocol/types'; +import { isMarketInAuction } from './is-market-in-auction'; + +/** + * Get the market price based on market mode (auction or not auction) + */ +export const getMarketPrice = (market: MarketDealTicket) => { + if (isMarketInAuction(market)) { + // 0 can never be a valid uncrossing price + // as it would require there being orders on the book at that price. + if ( + market.data?.indicativePrice && + market.data.indicativePrice !== '0' && + BigInt(market.data?.indicativePrice) !== BigInt(0) + ) { + return market.data.indicativePrice; + } + } else { + return market.data.markPrice; + } + return undefined; +}; + +/** + * 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: Schema.OrderType; + price?: string | undefined; + }, + market: MarketDealTicket +) => { + // 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 + let price; + if (order.type === Schema.OrderType.TYPE_LIMIT && order.price) { + price = removeDecimal(order.price, market.decimalPlaces); + } else { + price = getMarketPrice(market); + } + + return price === '0' ? undefined : price; +};