feat(trading): update validation and rules on take profit and stop loss (#5987)

This commit is contained in:
m.ray 2024-03-13 19:43:18 +02:00 committed by GitHub
parent be9a5a3437
commit b89523efb2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 56 additions and 48 deletions

View File

@ -48,7 +48,7 @@ export const TxDetailsShared = ({
}: TxDetailsSharedProps) => { }: TxDetailsSharedProps) => {
const { data } = useExplorerEpochForBlockQuery({ const { data } = useExplorerEpochForBlockQuery({
errorPolicy: 'ignore', errorPolicy: 'ignore',
variables: { block: txData?.block.toString() || '' }, variables: { block: txData?.block?.toString() || '' },
}); });
if (!txData) { if (!txData) {

View File

@ -21,7 +21,7 @@ describe('TX: Transfer: getLabelForTransfer', () => {
}, },
}; };
expect(getTypeLabelForTransfer(mock)).toEqual('Reward top up transfer'); expect(getTypeLabelForTransfer(mock)).toEqual('Reward transfer');
}); });
it('renders reward top up label if the TO party is network', () => { it('renders reward top up label if the TO party is network', () => {
@ -32,7 +32,7 @@ describe('TX: Transfer: getLabelForTransfer', () => {
}, },
}; };
expect(getTypeLabelForTransfer(mock)).toEqual('Reward top up transfer'); expect(getTypeLabelForTransfer(mock)).toEqual('Reward transfer');
}); });
it('renders recurring label if the tx has a recurring property', () => { it('renders recurring label if the tx has a recurring property', () => {

View File

@ -21,6 +21,7 @@ NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
# Cosmic elevator flags # Cosmic elevator flags
NX_SUCCESSOR_MARKETS=true NX_SUCCESSOR_MARKETS=true
NX_STOP_ORDERS=true NX_STOP_ORDERS=true
NX_TAKE_PROFIT_STOP_LOSS=true
NX_ISOLATED_MARGIN=true NX_ISOLATED_MARGIN=true
NX_ICEBERG_ORDERS=true NX_ICEBERG_ORDERS=true
NX_METAMASK_SNAPS=true NX_METAMASK_SNAPS=true

View File

@ -21,6 +21,8 @@ NX_ETH_WALLET_MNEMONIC="ozone access unlock valid olympic save include omit supp
# Cosmic elevator flags # Cosmic elevator flags
NX_SUCCESSOR_MARKETS=false NX_SUCCESSOR_MARKETS=false
NX_STOP_ORDERS=false NX_STOP_ORDERS=false
NX_TAKE_PROFIT_STOP_LOSS=false
NX_TAKE_PROFIT_STOP_LOSS=true
NX_ISOLATED_MARGIN=true NX_ISOLATED_MARGIN=true
# NX_ICEBERG_ORDERS # NX_ICEBERG_ORDERS
# NX_PRODUCT_PERPETUALS # NX_PRODUCT_PERPETUALS

View File

@ -21,6 +21,7 @@ NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
# Cosmic elevator flags # Cosmic elevator flags
NX_SUCCESSOR_MARKETS=true NX_SUCCESSOR_MARKETS=true
NX_STOP_ORDERS=true NX_STOP_ORDERS=true
NX_TAKE_PROFIT_STOP_LOSS=false
NX_ISOLATED_MARGIN=false NX_ISOLATED_MARGIN=false
NX_ICEBERG_ORDERS=true NX_ICEBERG_ORDERS=true
NX_METAMASK_SNAPS=true NX_METAMASK_SNAPS=true

View File

@ -21,6 +21,7 @@ NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
# Cosmic elevator flags # Cosmic elevator flags
NX_SUCCESSOR_MARKETS=true NX_SUCCESSOR_MARKETS=true
NX_STOP_ORDERS=true NX_STOP_ORDERS=true
NX_TAKE_PROFIT_STOP_LOSS=true
NX_ISOLATED_MARGIN=true NX_ISOLATED_MARGIN=true
NX_ICEBERG_ORDERS=true NX_ICEBERG_ORDERS=true
# NX_PRODUCT_PERPETUALS # NX_PRODUCT_PERPETUALS

View File

@ -22,6 +22,7 @@ NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
# Cosmic elevator flags # Cosmic elevator flags
NX_SUCCESSOR_MARKETS=true NX_SUCCESSOR_MARKETS=true
NX_STOP_ORDERS=true NX_STOP_ORDERS=true
NX_TAKE_PROFIT_STOP_LOSS=true
NX_ISOLATED_MARGIN=true NX_ISOLATED_MARGIN=true
NX_ICEBERG_ORDERS=true NX_ICEBERG_ORDERS=true
NX_METAMASK_SNAPS=true NX_METAMASK_SNAPS=true

View File

@ -22,6 +22,7 @@ NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/m
# Cosmic elevator flags # Cosmic elevator flags
NX_SUCCESSOR_MARKETS=true NX_SUCCESSOR_MARKETS=true
NX_STOP_ORDERS=true NX_STOP_ORDERS=true
NX_TAKE_PROFIT_STOP_LOSS=true
NX_ISOLATED_MARGIN=true NX_ISOLATED_MARGIN=true
NX_ICEBERG_ORDERS=true NX_ICEBERG_ORDERS=true
# NX_PRODUCT_PERPETUALS # NX_PRODUCT_PERPETUALS

View File

@ -28,8 +28,8 @@ export const DealTicketPriceTakeProfitStopLoss = ({
quoteName, quoteName,
}: DealTicketPriceTakeProfitStopLossProps) => { }: DealTicketPriceTakeProfitStopLossProps) => {
const t = useT(); const t = useT();
const validateAmount = useValidateAmount();
const priceStep = determinePriceStep(market); const priceStep = determinePriceStep(market);
const validateAmount = useValidateAmount();
return ( return (
<div className="mb-2"> <div className="mb-2">
@ -50,15 +50,6 @@ export const DealTicketPriceTakeProfitStopLoss = ({
name="takeProfit" name="takeProfit"
control={control} control={control}
rules={{ rules={{
min: {
value: priceStep,
message: t(
'Take profit price cannot be lower than {{priceStep}}',
{
priceStep,
}
),
},
validate: validateAmount(priceStep, 'takeProfit'), validate: validateAmount(priceStep, 'takeProfit'),
}} }}
render={({ field, fieldState }) => ( render={({ field, fieldState }) => (
@ -103,12 +94,6 @@ export const DealTicketPriceTakeProfitStopLoss = ({
name="stopLoss" name="stopLoss"
control={control} control={control}
rules={{ rules={{
min: {
value: priceStep,
message: t('Price cannot be lower than {{priceStep}}', {
priceStep,
}),
},
validate: validateAmount(priceStep, 'stopLoss'), validate: validateAmount(priceStep, 'stopLoss'),
}} }}
render={({ field, fieldState }) => ( render={({ field, fieldState }) => (

View File

@ -77,7 +77,7 @@ import { DealTicketSizeIceberg } from './deal-ticket-size-iceberg';
import noop from 'lodash/noop'; import noop from 'lodash/noop';
import { isNonPersistentOrder } from '../../utils/time-in-force-persistence'; import { isNonPersistentOrder } from '../../utils/time-in-force-persistence';
import { KeyValue } from './key-value'; import { KeyValue } from './key-value';
import { DocsLinks } from '@vegaprotocol/environment'; import { DocsLinks, useFeatureFlags } from '@vegaprotocol/environment';
import { useT } from '../../use-t'; import { useT } from '../../use-t';
import { DealTicketPriceTakeProfitStopLoss } from './deal-ticket-price-tp-sl'; import { DealTicketPriceTakeProfitStopLoss } from './deal-ticket-price-tp-sl';
import uniqueId from 'lodash/uniqueId'; import uniqueId from 'lodash/uniqueId';
@ -381,6 +381,7 @@ export const DealTicket = ({
const disablePostOnlyCheckbox = nonPersistentOrder; const disablePostOnlyCheckbox = nonPersistentOrder;
const disableReduceOnlyCheckbox = !nonPersistentOrder; const disableReduceOnlyCheckbox = !nonPersistentOrder;
const disableIcebergCheckbox = nonPersistentOrder; const disableIcebergCheckbox = nonPersistentOrder;
const featureFlags = useFeatureFlags((state) => state.flags);
const onSubmit = useCallback( const onSubmit = useCallback(
(formValues: OrderFormValues) => { (formValues: OrderFormValues) => {
@ -388,7 +389,11 @@ export const DealTicket = ({
if (lastSubmitTime.current && now - lastSubmitTime.current < 1000) { if (lastSubmitTime.current && now - lastSubmitTime.current < 1000) {
return; return;
} }
if (formValues.tpSl) { if (
featureFlags.TAKE_PROFIT_STOP_LOSS &&
formValues.tpSl &&
(formValues.takeProfit || formValues.stopLoss)
) {
const reference = `${pubKey}-${now}-${uniqueId()}`; const reference = `${pubKey}-${now}-${uniqueId()}`;
const batchMarketInstructions = mapFormValuesToTakeProfitAndStopLoss( const batchMarketInstructions = mapFormValuesToTakeProfitAndStopLoss(
formValues, formValues,
@ -409,7 +414,7 @@ export const DealTicket = ({
} }
lastSubmitTime.current = now; lastSubmitTime.current = now;
}, },
[market, pubKey, submit] [featureFlags.TAKE_PROFIT_STOP_LOSS, market, pubKey, submit]
); );
useController({ useController({
name: 'type', name: 'type',
@ -726,6 +731,7 @@ export const DealTicket = ({
)} )}
/> />
)} )}
{featureFlags.TAKE_PROFIT_STOP_LOSS && (
<Controller <Controller
name="tpSl" name="tpSl"
control={control} control={control}
@ -747,6 +753,7 @@ export const DealTicket = ({
</Tooltip> </Tooltip>
)} )}
/> />
)}
</div> </div>
{isLimitType && iceberg && ( {isLimitType && iceberg && (
<DealTicketSizeIceberg <DealTicketSizeIceberg
@ -758,7 +765,7 @@ export const DealTicket = ({
peakSize={peakSize} peakSize={peakSize}
/> />
)} )}
{tpSl && ( {featureFlags.TAKE_PROFIT_STOP_LOSS && tpSl && (
<DealTicketPriceTakeProfitStopLoss <DealTicketPriceTakeProfitStopLoss
market={market} market={market}
takeProfitError={errors.takeProfit?.message} takeProfitError={errors.takeProfit?.message}

View File

@ -390,6 +390,12 @@ export const compileFeatureFlags = (refresh = false): FeatureFlags => {
STOP_ORDERS: TRUTHY.includes( STOP_ORDERS: TRUTHY.includes(
windowOrDefault('NX_STOP_ORDERS', process.env['NX_STOP_ORDERS']) as string windowOrDefault('NX_STOP_ORDERS', process.env['NX_STOP_ORDERS']) as string
), ),
TAKE_PROFIT_STOP_LOSS: TRUTHY.includes(
windowOrDefault(
'NX_TAKE_PROFIT_STOP_LOSS',
process.env['NX_TAKE_PROFIT_STOP_LOSS']
) as string
),
ISOLATED_MARGIN: TRUTHY.includes( ISOLATED_MARGIN: TRUTHY.includes(
windowOrDefault( windowOrDefault(
'NX_ISOLATED_MARGIN', 'NX_ISOLATED_MARGIN',

View File

@ -21,6 +21,7 @@ export type CosmicElevatorFlags = Pick<
FeatureFlags, FeatureFlags,
| 'ICEBERG_ORDERS' | 'ICEBERG_ORDERS'
| 'ISOLATED_MARGIN' | 'ISOLATED_MARGIN'
| 'TAKE_PROFIT_STOP_LOSS'
| 'STOP_ORDERS' | 'STOP_ORDERS'
| 'SUCCESSOR_MARKETS' | 'SUCCESSOR_MARKETS'
| 'PRODUCT_PERPETUALS' | 'PRODUCT_PERPETUALS'

View File

@ -67,6 +67,7 @@ export const envSchema = z
const COSMIC_ELEVATOR_FLAGS = { const COSMIC_ELEVATOR_FLAGS = {
SUCCESSOR_MARKETS: z.optional(z.boolean()), SUCCESSOR_MARKETS: z.optional(z.boolean()),
STOP_ORDERS: z.optional(z.boolean()), STOP_ORDERS: z.optional(z.boolean()),
TAKE_PROFIT_STOP_LOSS: z.optional(z.boolean()),
ISOLATED_MARGIN: z.optional(z.boolean()), ISOLATED_MARGIN: z.optional(z.boolean()),
ICEBERG_ORDERS: z.optional(z.boolean()), ICEBERG_ORDERS: z.optional(z.boolean()),
PRODUCT_PERPETUALS: z.optional(z.boolean()), PRODUCT_PERPETUALS: z.optional(z.boolean()),

View File

@ -197,6 +197,7 @@ export const MarketVolumeInfoPanel = ({ market }: MarketInfoProps) => {
marketId={market.id} marketId={market.id}
positionDecimalPlaces={market.positionDecimalPlaces} positionDecimalPlaces={market.positionDecimalPlaces}
marketDecimals={market.decimalPlaces} marketDecimals={market.decimalPlaces}
quoteUnit={getQuoteName(market)}
/> />
), ),
openInterest: dash(data?.openInterest), openInterest: dash(data?.openInterest),
@ -788,7 +789,7 @@ export const PriceMonitoringBoundsInfoPanel = ({
return ( return (
<> <>
<div className="mb-2 grid grid-cols-2 text-sm"> <div className="mb-2 grid grid-cols-2 text-xs">
<p className="col-span-1"> <p className="col-span-1">
{t('{{probability}} probability price bounds', { {t('{{probability}} probability price bounds', {
probability: formatNumberPercentage( probability: formatNumberPercentage(

View File

@ -7,7 +7,7 @@ export const useValidateAmount = () => {
return useCallback( return useCallback(
(step: number | string, field: string) => { (step: number | string, field: string) => {
return (value?: string) => { return (value?: string) => {
const isValid = validateAgainstStep(step, value); const isValid = value ? validateAgainstStep(step, value) : true;
if (!isValid) { if (!isValid) {
if (new BigNumber(step).isEqualTo(1)) { if (new BigNumber(step).isEqualTo(1)) {
return t('{{field}} must be whole numbers for this market', { return t('{{field}} must be whole numbers for this market', {
@ -33,7 +33,7 @@ const isMultipleOf = (value: BigNumber, multipleOf: BigNumber) =>
export const validateAgainstStep = ( export const validateAgainstStep = (
step: string | number, step: string | number,
input?: string | number input: string | number
) => { ) => {
const stepValue = new BigNumber(step); const stepValue = new BigNumber(step);
if (stepValue.isNaN()) { if (stepValue.isNaN()) {
@ -45,6 +45,6 @@ export const validateAgainstStep = (
return true; return true;
} }
const value = new BigNumber(input || ''); const value = new BigNumber(input);
return isMultipleOf(value, stepValue); return isMultipleOf(value, stepValue);
}; };