From dc959025c65bbe9e99c2f3618e4eeacb971e1798 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20G=C5=82ownia?= Date: Fri, 25 Aug 2023 23:27:55 +0200 Subject: [PATCH] fix(deal-ticket): disable iceberg for IOC and FOK (#4629) Co-authored-by: Matthew Russell --- .../deal-ticket/deal-ticket-stop-order.tsx | 11 +++--- .../components/deal-ticket/deal-ticket.tsx | 34 +++++++++------- .../deal-ticket/expiry-selector.tsx | 4 +- .../utils/map-form-values-to-submission.ts | 8 ++-- .../map-form-values-to-submisstion.spec.ts | 39 ++++++++++++++++++- .../utils/time-in-force-persistance.spec.ts | 23 +++++++++++ .../src/utils/time-in-force-persistance.ts | 12 ++++++ 7 files changed, 104 insertions(+), 27 deletions(-) create mode 100644 libs/deal-ticket/src/utils/time-in-force-persistance.spec.ts create mode 100644 libs/deal-ticket/src/utils/time-in-force-persistance.ts diff --git a/libs/deal-ticket/src/components/deal-ticket/deal-ticket-stop-order.tsx b/libs/deal-ticket/src/components/deal-ticket/deal-ticket-stop-order.tsx index e946e6d02..b18e72e4d 100644 --- a/libs/deal-ticket/src/components/deal-ticket/deal-ticket-stop-order.tsx +++ b/libs/deal-ticket/src/components/deal-ticket/deal-ticket-stop-order.tsx @@ -20,7 +20,8 @@ import { TradingSelect as Select, Tooltip, } from '@vegaprotocol/ui-toolkit'; -import { getDerivedPrice, type Market } from '@vegaprotocol/markets'; +import { getDerivedPrice } from '@vegaprotocol/markets'; +import type { Market } from '@vegaprotocol/markets'; import { t } from '@vegaprotocol/i18n'; import { ExpirySelector } from './expiry-selector'; import { SideSelector } from './side-selector'; @@ -35,10 +36,10 @@ import { TypeToggle } from './type-selector'; import { useDealTicketFormValues, DealTicketType, - type StopOrderFormValues, dealTicketTypeToOrderType, isStopOrderType, } from '../../hooks/use-form-values'; +import type { StopOrderFormValues } from '../../hooks/use-form-values'; import { mapFormValuesToStopOrdersSubmission } from '../../utils/map-form-values-to-submission'; import { DealTicketButton } from './deal-ticket-button'; import { DealTicketFeeDetails } from './deal-ticket-fee-details'; @@ -632,11 +633,11 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => { /> -
+

-
+
{ /> -
+
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 496c312c5..0319ee8de 100644 --- a/libs/deal-ticket/src/components/deal-ticket/deal-ticket.tsx +++ b/libs/deal-ticket/src/components/deal-ticket/deal-ticket.tsx @@ -38,8 +38,6 @@ import { } from '@vegaprotocol/utils'; import { activeOrdersProvider } from '@vegaprotocol/orders'; import { getDerivedPrice } from '@vegaprotocol/markets'; -import type { OrderInfo } from '@vegaprotocol/types'; - import { validateExpiration, validateMarketState, @@ -59,8 +57,6 @@ import { useMarketAccountBalance, useAccountBalance, } from '@vegaprotocol/accounts'; - -import { OrderType } from '@vegaprotocol/types'; import { useDataProvider } from '@vegaprotocol/data-provider'; import { DealTicketType, @@ -71,6 +67,7 @@ import type { OrderFormValues } from '../../hooks/use-form-values'; import { useDealTicketFormValues } from '../../hooks/use-form-values'; import { DealTicketSizeIceberg } from './deal-ticket-size-iceberg'; import noop from 'lodash/noop'; +import { isNonPersistentOrder } from '../../utils/time-in-force-persistance'; export const REDUCE_ONLY_TOOLTIP = '"Reduce only" will ensure that this order will not increase the size of an open position. When the order is matched, it will only trade enough volume to bring your open volume towards 0 but never change the direction of your position. If applied to a limit order that is not instantly filled, the order will be stopped.'; @@ -230,8 +227,8 @@ export const DealTicket = ({ }); const openVolume = useOpenVolume(pubKey, market.id) ?? '0'; const orders = activeOrders - ? activeOrders.map((order) => ({ - isMarketOrder: order.type === OrderType.TYPE_MARKET, + ? activeOrders.map((order) => ({ + isMarketOrder: order.type === Schema.OrderType.TYPE_MARKET, price: order.price, remaining: order.remaining, side: order.side, @@ -239,7 +236,7 @@ export const DealTicket = ({ : []; if (normalizedOrder) { orders.push({ - isMarketOrder: normalizedOrder.type === OrderType.TYPE_MARKET, + isMarketOrder: normalizedOrder.type === Schema.OrderType.TYPE_MARKET, price: normalizedOrder.price ?? '0', remaining: normalizedOrder.size, side: normalizedOrder.side, @@ -307,12 +304,10 @@ export const DealTicket = ({ pubKey, ]); - const disablePostOnlyCheckbox = [ - Schema.OrderTimeInForce.TIME_IN_FORCE_IOC, - Schema.OrderTimeInForce.TIME_IN_FORCE_FOK, - ].includes(timeInForce); - - const disableReduceOnlyCheckbox = !disablePostOnlyCheckbox; + const nonPersistentOrder = isNonPersistentOrder(timeInForce); + const disablePostOnlyCheckbox = nonPersistentOrder; + const disableReduceOnlyCheckbox = !nonPersistentOrder; + const disableIcebergCheckbox = nonPersistentOrder; const onSubmit = useCallback( (formValues: OrderFormValues) => { @@ -468,6 +463,8 @@ export const DealTicket = ({ value={field.value} orderType={type} onSelect={(value) => { + // If GTT is selected and no expiresAt time is set, or its + // behind current time then reset the value to current time if ( value === Schema.OrderTimeInForce.TIME_IN_FORCE_GTT && (!expiresAt || new Date(expiresAt).getTime() < Date.now()) @@ -476,6 +473,12 @@ export const DealTicket = ({ shouldValidate: true, }); } + + // iceberg orders must be persistent orders, so if user + // switches to to a non persisten tif value, remove iceberg selection + if (iceberg && isNonPersistentOrder(value)) { + setValue('iceberg', false); + } field.onChange(value); }} market={market} @@ -502,7 +505,7 @@ export const DealTicket = ({ )} /> )} -
+
{type === Schema.OrderType.TYPE_LIMIT && ( <> -
+
{ + const minDateRef = useRef(new Date()); + return (
onSelect(e.target.value)} - min={formatForInput(useRef(new Date()).current)} + min={formatForInput(minDateRef.current)} hasError={!!errorMessage} /> {errorMessage && ( diff --git a/libs/deal-ticket/src/utils/map-form-values-to-submission.ts b/libs/deal-ticket/src/utils/map-form-values-to-submission.ts index d6642666d..4f73d2989 100644 --- a/libs/deal-ticket/src/utils/map-form-values-to-submission.ts +++ b/libs/deal-ticket/src/utils/map-form-values-to-submission.ts @@ -9,6 +9,7 @@ import type { } from '../hooks/use-form-values'; import * as Schema from '@vegaprotocol/types'; import { removeDecimal, toNanoSeconds } from '@vegaprotocol/utils'; +import { isPersistentOrder } from './time-in-force-persistance'; export const mapFormValuesToOrderSubmission = ( order: OrderFormValues, @@ -41,11 +42,8 @@ export const mapFormValuesToOrderSubmission = ( ? false : order.reduceOnly, icebergOpts: - (order.type === Schema.OrderType.TYPE_MARKET || - [ - Schema.OrderTimeInForce.TIME_IN_FORCE_FOK, - Schema.OrderTimeInForce.TIME_IN_FORCE_IOC, - ].includes(order.timeInForce)) && + order.type === Schema.OrderType.TYPE_LIMIT && + isPersistentOrder(order.timeInForce) && order.iceberg && order.peakSize && order.minimumVisibleSize diff --git a/libs/deal-ticket/src/utils/map-form-values-to-submisstion.spec.ts b/libs/deal-ticket/src/utils/map-form-values-to-submisstion.spec.ts index b3c743603..2afccaf2c 100644 --- a/libs/deal-ticket/src/utils/map-form-values-to-submisstion.spec.ts +++ b/libs/deal-ticket/src/utils/map-form-values-to-submisstion.spec.ts @@ -1,6 +1,8 @@ import type { OrderSubmissionBody } from '@vegaprotocol/wallet'; import { mapFormValuesToOrderSubmission } from './map-form-values-to-submission'; import * as Schema from '@vegaprotocol/types'; +import { OrderTimeInForce, OrderType } from '@vegaprotocol/types'; +import type { OrderFormValues } from '../hooks'; describe('mapFormValuesToOrderSubmission', () => { it('sets and formats price only for limit orders', () => { @@ -25,7 +27,7 @@ describe('mapFormValuesToOrderSubmission', () => { ).toEqual('10000'); }); - it('sets and formats expiresAt only for time in force orders', () => { + it('sets and formats expiresAt only for GTT orders', () => { expect( mapFormValuesToOrderSubmission( { @@ -49,6 +51,41 @@ describe('mapFormValuesToOrderSubmission', () => { ).toEqual('1640995200000000000'); }); + it('sets and formats icebergOpts only for persisted orders', () => { + expect( + mapFormValuesToOrderSubmission( + { + type: OrderType.TYPE_LIMIT, + timeInForce: OrderTimeInForce.TIME_IN_FORCE_FOK, + iceberg: true, + peakSize: '10.00', + minimumVisibleSize: '10.00', + } as OrderFormValues, + 'marketId', + 2, + 2 + ).icebergOpts + ).toEqual(undefined); + + expect( + mapFormValuesToOrderSubmission( + { + type: OrderType.TYPE_LIMIT, + timeInForce: OrderTimeInForce.TIME_IN_FORCE_GTC, + iceberg: true, + peakSize: '10.00', + minimumVisibleSize: '10.00', + } as OrderFormValues, + 'marketId', + 2, + 2 + ).icebergOpts + ).toEqual({ + peakSize: '1000', + minimumVisibleSize: '1000', + }); + }); + it('formats size', () => { expect( mapFormValuesToOrderSubmission( diff --git a/libs/deal-ticket/src/utils/time-in-force-persistance.spec.ts b/libs/deal-ticket/src/utils/time-in-force-persistance.spec.ts new file mode 100644 index 000000000..ce4a783fe --- /dev/null +++ b/libs/deal-ticket/src/utils/time-in-force-persistance.spec.ts @@ -0,0 +1,23 @@ +import { OrderTimeInForce } from '@vegaprotocol/types'; +import { + isNonPersistentOrder, + isPersistentOrder, +} from './time-in-force-persistance'; + +it('isNonPeristentOrder', () => { + expect(isNonPersistentOrder(OrderTimeInForce.TIME_IN_FORCE_FOK)).toBe(true); + expect(isNonPersistentOrder(OrderTimeInForce.TIME_IN_FORCE_IOC)).toBe(true); + expect(isNonPersistentOrder(OrderTimeInForce.TIME_IN_FORCE_GTC)).toBe(false); + expect(isNonPersistentOrder(OrderTimeInForce.TIME_IN_FORCE_GTT)).toBe(false); + expect(isNonPersistentOrder(OrderTimeInForce.TIME_IN_FORCE_GFA)).toBe(false); + expect(isNonPersistentOrder(OrderTimeInForce.TIME_IN_FORCE_GFN)).toBe(false); +}); + +it('isPeristentOrder', () => { + expect(isPersistentOrder(OrderTimeInForce.TIME_IN_FORCE_FOK)).toBe(false); + expect(isPersistentOrder(OrderTimeInForce.TIME_IN_FORCE_IOC)).toBe(false); + expect(isPersistentOrder(OrderTimeInForce.TIME_IN_FORCE_GTC)).toBe(true); + expect(isPersistentOrder(OrderTimeInForce.TIME_IN_FORCE_GTT)).toBe(true); + expect(isPersistentOrder(OrderTimeInForce.TIME_IN_FORCE_GFA)).toBe(true); + expect(isPersistentOrder(OrderTimeInForce.TIME_IN_FORCE_GFN)).toBe(true); +}); diff --git a/libs/deal-ticket/src/utils/time-in-force-persistance.ts b/libs/deal-ticket/src/utils/time-in-force-persistance.ts new file mode 100644 index 000000000..bfb21c8d6 --- /dev/null +++ b/libs/deal-ticket/src/utils/time-in-force-persistance.ts @@ -0,0 +1,12 @@ +import { OrderTimeInForce } from '@vegaprotocol/types'; + +export const isNonPersistentOrder = (timeInForce: OrderTimeInForce) => { + return [ + OrderTimeInForce.TIME_IN_FORCE_FOK, + OrderTimeInForce.TIME_IN_FORCE_IOC, + ].includes(timeInForce); +}; + +export const isPersistentOrder = (timeInForce: OrderTimeInForce) => { + return !isNonPersistentOrder(timeInForce); +};