feat(trading): add post only and reduce only orders

This commit is contained in:
Madalina Raicu
2023-03-29 16:48:55 +01:00
parent 0c44664aa5
commit e82e6a032b
6 changed files with 89 additions and 14 deletions
@@ -1,6 +1,6 @@
import { t } from '@vegaprotocol/i18n';
import * as Schema from '@vegaprotocol/types';
import { memo, useCallback, useEffect, useState } from 'react';
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
import { Controller } from 'react-hook-form';
import { DealTicketAmount } from './deal-ticket-amount';
import { DealTicketButton } from './deal-ticket-button';
@@ -16,10 +16,12 @@ import {
useVegaWalletDialogStore,
} from '@vegaprotocol/wallet';
import {
Checkbox,
ExternalLink,
InputError,
Intent,
Notification,
Tooltip,
} from '@vegaprotocol/ui-toolkit';
import {
@@ -143,6 +145,16 @@ export const DealTicket = ({
clearErrors,
]);
const disablePostOnlyCheckbox = useMemo(() => {
const disabled = order
? [
Schema.OrderTimeInForce.TIME_IN_FORCE_IOC,
Schema.OrderTimeInForce.TIME_IN_FORCE_FOK,
].includes(order.timeInForce)
: true;
return disabled;
}, [order]);
const onSubmit = useCallback(
(order: OrderSubmission) => {
submit(
@@ -230,8 +242,8 @@ export const DealTicket = ({
value={order.timeInForce}
orderType={order.type}
onSelect={(timeInForce) => {
update({ timeInForce });
// Set tif value for the given order type, so that when switching
update({ timeInForce, reduceOnly: false, postOnly: false });
// Set TIF value for the given order type, so that when switching
// types we know the last used TIF for the given order type
setLastTIF((curr) => ({
...curr,
@@ -267,6 +279,66 @@ export const DealTicket = ({
)}
/>
)}
<div className="flex gap-2 pb-2 justify-between">
<Controller
name="postOnly"
control={control}
render={() => (
<Checkbox
name="post-only"
checked={order.postOnly}
disabled={disablePostOnlyCheckbox}
onCheckedChange={() => {
update({ postOnly: !order.postOnly, reduceOnly: false });
}}
label={
<Tooltip
description={
<span>
{disablePostOnlyCheckbox
? t(
'"Post only" can not be used on "Fill or Kill" or "Immediate or Cancel" orders.'
)
: t(
'"Post only" will ensure the order is not filled immediately but is placed on the order book as a passive order. When the order is processed it is either stopped (if it would not be filled immediately), or placed in the order book as a passive order until the price taker matches with it.'
)}
</span>
}
>
<span className="text-xs">{t('Post only')}</span>
</Tooltip>
}
/>
)}
/>
<Controller
name="reduceOnly"
control={control}
render={() => (
<Checkbox
name="reduce-only"
checked={order.reduceOnly}
onCheckedChange={() => {
update({ postOnly: false, reduceOnly: !order.reduceOnly });
}}
label={
<Tooltip
description={
<span>
{t(
'"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.'
)}
</span>
}
>
<span className="text-xs">{t('Reduce only')}</span>
</Tooltip>
}
/>
)}
/>
</div>
<SummaryMessage
errorMessage={errors.summary?.message}
asset={asset}
@@ -96,16 +96,6 @@ export const TimeInForceSelector = ({
id="select-time-in-force"
value={value}
onChange={(e) => {
// setPreviousTimeInForce({
// ...previousTimeInForce,
// [orderType]: e.target.value,
// });
// if (previousOrderType !== orderType) {
// setPreviousOrderType(orderType);
// const prev = previousTimeInForce[orderType as OrderType];
// onSelect(prev);
// }
onSelect(e.target.value as Schema.OrderTimeInForce);
}}
className="w-full"
@@ -13,6 +13,8 @@ export type OrderObj = {
price?: string;
expiresAt?: string | undefined;
persist: boolean; // key used to determine if order should be kept in localStorage
postOnly?: boolean;
reduceOnly?: boolean;
};
type OrderMap = { [marketId: string]: OrderObj | undefined };
@@ -114,4 +116,6 @@ export const getDefaultOrder = (marketId: string): OrderObj => ({
price: '0',
expiresAt: undefined,
persist: false,
postOnly: false,
reduceOnly: false,
});
@@ -54,7 +54,12 @@ export const Checkbox = ({
)}
</CheckboxPrimitive.CheckboxIndicator>
</CheckboxPrimitive.Root>
<label htmlFor={name} className="text-sm">
<label
htmlFor={name}
className={classNames('text-sm', {
'dark:text-neutral-400 text-neutral-600': disabled,
})}
>
{label}
</label>
</div>
@@ -45,6 +45,8 @@ export interface OrderSubmission {
size: string;
price?: string;
expiresAt?: string;
postOnly?: boolean;
reduceOnly?: boolean;
}
export interface OrderCancellation {
+2
View File
@@ -48,6 +48,8 @@ export const normalizeOrderSubmission = (
order.expiresAt && order.timeInForce === OrderTimeInForce.TIME_IN_FORCE_GTT
? toNanoSeconds(order.expiresAt)
: undefined,
postOnly: order.postOnly,
reduceOnly: order.reduceOnly,
});
export const normalizeOrderAmendment = <T extends Exact<OrderAmendment, T>>(