feat: 991 deal ticket should show estimated crossing price for market orders when market is in auction (#1561)
* feat: #991 move order validation back in deal ticket, add tooltip * fix: align view full market list link styling * feat: #991 move tooltip add for monitoring liq auction * feat: #991 order validation messages, LP table sortable, padding sparkline, warning instead of errro * feat: #991 check only in monitoring auction to add tooltips, re-use isMarketInAuction * fix: #991 fix import for isMarketInAuction * fix: #991 console-lite imports * fix: formatting in console-lite deal-ticket-steps.tsx * fix: add market depth * fix: add market depth and fix generate orders * fix: revert market list and generate orders * fix: fix trading-e2e build * feat: #991 in monitoring auction don't show a price input color update based on intent * fix: #991 update docs links to docs.vega.xyz in tooltips
This commit is contained in:
parent
b10844147e
commit
f743828cb5
@ -1,8 +1,13 @@
|
|||||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { useForm, Controller } from 'react-hook-form';
|
import { useForm, Controller } from 'react-hook-form';
|
||||||
import { Stepper } from '../stepper';
|
import { Stepper } from '../stepper';
|
||||||
import type { DealTicketMarketFragment } from '@vegaprotocol/deal-ticket';
|
import type { DealTicketMarketFragment } from '@vegaprotocol/deal-ticket';
|
||||||
|
import {
|
||||||
|
getDefaultOrder,
|
||||||
|
useOrderValidation,
|
||||||
|
validateSize,
|
||||||
|
} from '@vegaprotocol/deal-ticket';
|
||||||
import { InputError } from '@vegaprotocol/ui-toolkit';
|
import { InputError } from '@vegaprotocol/ui-toolkit';
|
||||||
import { BigNumber } from 'bignumber.js';
|
import { BigNumber } from 'bignumber.js';
|
||||||
import { MarketSelector } from '@vegaprotocol/deal-ticket';
|
import { MarketSelector } from '@vegaprotocol/deal-ticket';
|
||||||
@ -15,14 +20,11 @@ import {
|
|||||||
removeDecimal,
|
removeDecimal,
|
||||||
} from '@vegaprotocol/react-helpers';
|
} from '@vegaprotocol/react-helpers';
|
||||||
import {
|
import {
|
||||||
getDefaultOrder,
|
|
||||||
useOrderValidation,
|
|
||||||
useOrderSubmit,
|
useOrderSubmit,
|
||||||
getOrderDialogTitle,
|
getOrderDialogTitle,
|
||||||
getOrderDialogIntent,
|
getOrderDialogIntent,
|
||||||
getOrderDialogIcon,
|
getOrderDialogIcon,
|
||||||
OrderFeedback,
|
OrderFeedback,
|
||||||
validateSize,
|
|
||||||
} from '@vegaprotocol/orders';
|
} from '@vegaprotocol/orders';
|
||||||
import { DealTicketSize } from './deal-ticket-size';
|
import { DealTicketSize } from './deal-ticket-size';
|
||||||
import MarketNameRenderer from '../simple-market-list/simple-market-renderer';
|
import MarketNameRenderer from '../simple-market-list/simple-market-renderer';
|
||||||
|
@ -28,6 +28,7 @@ export const generateDealTicketQuery = (
|
|||||||
id: '5cfa87844724df6069b94e4c8a6f03af21907d7bc251593d08e4251043ee9f7c',
|
id: '5cfa87844724df6069b94e4c8a6f03af21907d7bc251593d08e4251043ee9f7c',
|
||||||
symbol: 'tBTC',
|
symbol: 'tBTC',
|
||||||
name: 'tBTC TEST',
|
name: 'tBTC TEST',
|
||||||
|
decimals: 5,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -75,6 +75,13 @@ export const generateMarket = (override?: PartialDeep<Market>): Market => {
|
|||||||
close: null,
|
close: null,
|
||||||
__typename: 'MarketTimestamps',
|
__typename: 'MarketTimestamps',
|
||||||
},
|
},
|
||||||
|
depth: {
|
||||||
|
__typename: 'MarketDepth',
|
||||||
|
lastTrade: {
|
||||||
|
__typename: 'Trade',
|
||||||
|
price: '88470230',
|
||||||
|
},
|
||||||
|
},
|
||||||
candlesConnection: {
|
candlesConnection: {
|
||||||
__typename: 'CandleDataConnection',
|
__typename: 'CandleDataConnection',
|
||||||
edges: [
|
edges: [
|
||||||
|
@ -1 +0,0 @@
|
|||||||
export * from './trading-mode-tooltip';
|
|
@ -1,207 +0,0 @@
|
|||||||
import type { ReactNode } from 'react';
|
|
||||||
import {
|
|
||||||
t,
|
|
||||||
getDateTimeFormat,
|
|
||||||
addDecimalsFormatNumber,
|
|
||||||
} from '@vegaprotocol/react-helpers';
|
|
||||||
import { ExternalLink, Link as UiToolkitLink } from '@vegaprotocol/ui-toolkit';
|
|
||||||
import Link from 'next/link';
|
|
||||||
import { MarketTradingMode, AuctionTrigger } from '@vegaprotocol/types';
|
|
||||||
import type { Market_market } from '../../pages/markets/__generated__/Market';
|
|
||||||
|
|
||||||
type MarketDataGridProps = {
|
|
||||||
grid: {
|
|
||||||
label: string | ReactNode;
|
|
||||||
value?: ReactNode;
|
|
||||||
}[];
|
|
||||||
};
|
|
||||||
|
|
||||||
const MarketDataGrid = ({ grid }: MarketDataGridProps) => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{grid.map(
|
|
||||||
({ label, value }, index) =>
|
|
||||||
value && (
|
|
||||||
<div key={index} className="grid grid-cols-2">
|
|
||||||
<span data-testid="tooltip-label">{label}</span>
|
|
||||||
<span data-testid="tooltip-value" className="text-right">
|
|
||||||
{value}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const formatStake = (value: string, market: Market_market) => {
|
|
||||||
const formattedValue = addDecimalsFormatNumber(
|
|
||||||
value,
|
|
||||||
market.tradableInstrument.instrument.product.settlementAsset.decimals
|
|
||||||
);
|
|
||||||
const asset =
|
|
||||||
market.tradableInstrument.instrument.product.settlementAsset.symbol;
|
|
||||||
return `${formattedValue} ${asset}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const compileGridData = (
|
|
||||||
market: Market_market,
|
|
||||||
onSelect?: (id: string) => void
|
|
||||||
) => {
|
|
||||||
const grid: MarketDataGridProps['grid'] = [];
|
|
||||||
const isLiquidityMonitoringAuction =
|
|
||||||
market.tradingMode === MarketTradingMode.TRADING_MODE_MONITORING_AUCTION &&
|
|
||||||
market.data?.trigger === AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY;
|
|
||||||
|
|
||||||
if (!market.data) return grid;
|
|
||||||
|
|
||||||
if (market.data?.auctionStart) {
|
|
||||||
grid.push({
|
|
||||||
label: t('Auction start'),
|
|
||||||
value: getDateTimeFormat().format(new Date(market.data.auctionStart)),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (market.data?.auctionEnd) {
|
|
||||||
const endDate = getDateTimeFormat().format(
|
|
||||||
new Date(market.data.auctionEnd)
|
|
||||||
);
|
|
||||||
grid.push({
|
|
||||||
label: isLiquidityMonitoringAuction
|
|
||||||
? t('Est auction end')
|
|
||||||
: t('Auction end'),
|
|
||||||
value: isLiquidityMonitoringAuction ? `~${endDate}` : endDate,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isLiquidityMonitoringAuction && market.data?.targetStake) {
|
|
||||||
grid.push({
|
|
||||||
label: t('Target liquidity'),
|
|
||||||
value: formatStake(market.data.targetStake, market),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isLiquidityMonitoringAuction && market.data?.suppliedStake) {
|
|
||||||
grid.push({
|
|
||||||
label: (
|
|
||||||
<Link href={`/liquidity/${market.id}`} passHref={true}>
|
|
||||||
<UiToolkitLink onClick={() => onSelect && onSelect(market.id)}>
|
|
||||||
{t('Current liquidity')}
|
|
||||||
</UiToolkitLink>
|
|
||||||
</Link>
|
|
||||||
),
|
|
||||||
value: formatStake(market.data.suppliedStake, market),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (market.data?.indicativePrice) {
|
|
||||||
grid.push({
|
|
||||||
label: t('Est uncrossing price'),
|
|
||||||
value:
|
|
||||||
'~' +
|
|
||||||
addDecimalsFormatNumber(
|
|
||||||
market.data.indicativePrice,
|
|
||||||
market.positionDecimalPlaces
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (market.data?.indicativeVolume) {
|
|
||||||
grid.push({
|
|
||||||
label: t('Est uncrossing vol'),
|
|
||||||
value:
|
|
||||||
'~' +
|
|
||||||
addDecimalsFormatNumber(
|
|
||||||
market.data.indicativeVolume,
|
|
||||||
market.positionDecimalPlaces
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return grid;
|
|
||||||
};
|
|
||||||
|
|
||||||
type TradingModeTooltipProps = {
|
|
||||||
market: Market_market;
|
|
||||||
onSelect?: (marketId: string) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const TradingModeTooltip = ({
|
|
||||||
market,
|
|
||||||
onSelect,
|
|
||||||
}: TradingModeTooltipProps) => {
|
|
||||||
switch (market.tradingMode) {
|
|
||||||
case MarketTradingMode.TRADING_MODE_CONTINUOUS: {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{t(
|
|
||||||
'This is the standard trading mode where trades are executed whenever orders are received.'
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case MarketTradingMode.TRADING_MODE_OPENING_AUCTION: {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<p className="mb-4">
|
|
||||||
<span>
|
|
||||||
{t(
|
|
||||||
'This new market is in an opening auction to determine a fair mid-price before starting continuous trading.'
|
|
||||||
)}
|
|
||||||
</span>{' '}
|
|
||||||
<ExternalLink href="https://docs.fairground.vega.xyz/docs/trading-questions/#auctions-what-happens-in-an-opening-auction">
|
|
||||||
{t('Find out more')}
|
|
||||||
</ExternalLink>
|
|
||||||
</p>
|
|
||||||
<MarketDataGrid grid={compileGridData(market)} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case MarketTradingMode.TRADING_MODE_MONITORING_AUCTION: {
|
|
||||||
switch (market.data?.trigger) {
|
|
||||||
case AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY: {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<p data-testid="tooltip-market-info" className="mb-4">
|
|
||||||
<span>
|
|
||||||
{t(
|
|
||||||
'This market is in auction until it reaches sufficient liquidity.'
|
|
||||||
)}
|
|
||||||
</span>{' '}
|
|
||||||
<ExternalLink href="https://docs.fairground.vega.xyz/docs/trading-questions/#auctions-what-is-a-liquidity-monitoring-auction">
|
|
||||||
{t('Find out more')}
|
|
||||||
</ExternalLink>
|
|
||||||
</p>
|
|
||||||
<MarketDataGrid grid={compileGridData(market, onSelect)} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case AuctionTrigger.AUCTION_TRIGGER_PRICE: {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<p className="mb-4">
|
|
||||||
<span>
|
|
||||||
{t('This market is in auction due to high price volatility.')}
|
|
||||||
</span>{' '}
|
|
||||||
<ExternalLink href="https://docs.fairground.vega.xyz/docs/trading-questions/#auctions-what-is-a-price-monitoring-auction">
|
|
||||||
{t('Find out more')}
|
|
||||||
</ExternalLink>
|
|
||||||
</p>
|
|
||||||
<MarketDataGrid grid={compileGridData(market)} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case MarketTradingMode.TRADING_MODE_NO_TRADING: {
|
|
||||||
return <>{t('No trading enabled for this market.')}</>;
|
|
||||||
}
|
|
||||||
case MarketTradingMode.TRADING_MODE_BATCH_AUCTION:
|
|
||||||
default: {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
@ -50,6 +50,11 @@ query Market($marketId: ID!, $interval: Interval!, $since: String!) {
|
|||||||
open
|
open
|
||||||
close
|
close
|
||||||
}
|
}
|
||||||
|
depth {
|
||||||
|
lastTrade {
|
||||||
|
price
|
||||||
|
}
|
||||||
|
}
|
||||||
candlesConnection(interval: $interval, since: $since) {
|
candlesConnection(interval: $interval, since: $since) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
|
@ -69,6 +69,11 @@ const MARKET_QUERY = gql`
|
|||||||
open
|
open
|
||||||
close
|
close
|
||||||
}
|
}
|
||||||
|
depth {
|
||||||
|
lastTrade {
|
||||||
|
price
|
||||||
|
}
|
||||||
|
}
|
||||||
candlesConnection(interval: $interval, since: $since) {
|
candlesConnection(interval: $interval, since: $since) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
|
20
apps/trading/pages/markets/__generated__/Market.ts
generated
20
apps/trading/pages/markets/__generated__/Market.ts
generated
@ -169,6 +169,22 @@ export interface Market_market_marketTimestamps {
|
|||||||
close: string | null;
|
close: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Market_market_depth_lastTrade {
|
||||||
|
__typename: "Trade";
|
||||||
|
/**
|
||||||
|
* The price of the trade (probably initially the passive order price, other determination algorithms are possible though) (uint64)
|
||||||
|
*/
|
||||||
|
price: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Market_market_depth {
|
||||||
|
__typename: "MarketDepth";
|
||||||
|
/**
|
||||||
|
* Last trade for the given market (if available)
|
||||||
|
*/
|
||||||
|
lastTrade: Market_market_depth_lastTrade | null;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Market_market_candlesConnection_edges_node {
|
export interface Market_market_candlesConnection_edges_node {
|
||||||
__typename: "Candle";
|
__typename: "Candle";
|
||||||
/**
|
/**
|
||||||
@ -251,6 +267,10 @@ export interface Market_market {
|
|||||||
* Timestamps for state changes in the market
|
* Timestamps for state changes in the market
|
||||||
*/
|
*/
|
||||||
marketTimestamps: Market_market_marketTimestamps;
|
marketTimestamps: Market_market_marketTimestamps;
|
||||||
|
/**
|
||||||
|
* Current depth on the order book for this market
|
||||||
|
*/
|
||||||
|
depth: Market_market_depth;
|
||||||
/**
|
/**
|
||||||
* Candles on a market, for the 'last' n candles, at 'interval' seconds as specified by parameters using cursor based pagination
|
* Candles on a market, for the 'last' n candles, at 'interval' seconds as specified by parameters using cursor based pagination
|
||||||
*/
|
*/
|
||||||
|
@ -10,7 +10,7 @@ export type MarketQueryVariables = Types.Exact<{
|
|||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
|
||||||
export type MarketQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, tradingMode: Types.MarketTradingMode, state: Types.MarketState, decimalPlaces: number, positionDecimalPlaces: number, data?: { __typename?: 'MarketData', auctionStart?: string | null, auctionEnd?: string | null, markPrice: string, indicativeVolume: string, indicativePrice: string, suppliedStake?: string | null, targetStake?: string | null, bestBidVolume: string, bestOfferVolume: string, bestStaticBidVolume: string, bestStaticOfferVolume: string, trigger: Types.AuctionTrigger, market: { __typename?: 'Market', id: string } } | null, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename?: 'Future', quoteName: string, oracleSpecForTradingTermination: { __typename?: 'OracleSpec', id: string }, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number } } } }, marketTimestamps: { __typename?: 'MarketTimestamps', open?: string | null, close?: string | null }, candlesConnection?: { __typename?: 'CandleDataConnection', edges?: Array<{ __typename?: 'CandleEdge', node: { __typename?: 'Candle', open: string, close: string, volume: string } } | null> | null } | null } | null };
|
export type MarketQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, tradingMode: Types.MarketTradingMode, state: Types.MarketState, decimalPlaces: number, positionDecimalPlaces: number, data?: { __typename?: 'MarketData', auctionStart?: string | null, auctionEnd?: string | null, markPrice: string, indicativeVolume: string, indicativePrice: string, suppliedStake?: string | null, targetStake?: string | null, bestBidVolume: string, bestOfferVolume: string, bestStaticBidVolume: string, bestStaticOfferVolume: string, trigger: Types.AuctionTrigger, market: { __typename?: 'Market', id: string } } | null, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename?: 'Future', quoteName: string, oracleSpecForTradingTermination: { __typename?: 'OracleSpec', id: string }, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number } } } }, marketTimestamps: { __typename?: 'MarketTimestamps', open?: string | null, close?: string | null }, depth: { __typename?: 'MarketDepth', lastTrade?: { __typename?: 'Trade', price: string } | null }, candlesConnection?: { __typename?: 'CandleDataConnection', edges?: Array<{ __typename?: 'CandleEdge', node: { __typename?: 'Candle', open: string, close: string, volume: string } } | null> | null } | null } | null };
|
||||||
|
|
||||||
|
|
||||||
export const MarketDocument = gql`
|
export const MarketDocument = gql`
|
||||||
@ -66,6 +66,11 @@ export const MarketDocument = gql`
|
|||||||
open
|
open
|
||||||
close
|
close
|
||||||
}
|
}
|
||||||
|
depth {
|
||||||
|
lastTrade {
|
||||||
|
price
|
||||||
|
}
|
||||||
|
}
|
||||||
candlesConnection(interval: $interval, since: $since) {
|
candlesConnection(interval: $interval, since: $since) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
import { DealTicketContainer } from '@vegaprotocol/deal-ticket';
|
import {
|
||||||
|
compileGridData,
|
||||||
|
DealTicketContainer,
|
||||||
|
TradingModeTooltip,
|
||||||
|
} from '@vegaprotocol/deal-ticket';
|
||||||
import { MarketInfoContainer } from '@vegaprotocol/market-info';
|
import { MarketInfoContainer } from '@vegaprotocol/market-info';
|
||||||
import { OrderbookContainer } from '@vegaprotocol/market-depth';
|
import { OrderbookContainer } from '@vegaprotocol/market-depth';
|
||||||
import { ColumnKind, SelectMarketPopover } from '@vegaprotocol/market-list';
|
import { ColumnKind, SelectMarketPopover } from '@vegaprotocol/market-list';
|
||||||
@ -38,7 +42,6 @@ import {
|
|||||||
MarketTradingMode,
|
MarketTradingMode,
|
||||||
MarketTradingModeMapping,
|
MarketTradingModeMapping,
|
||||||
} from '@vegaprotocol/types';
|
} from '@vegaprotocol/types';
|
||||||
import { TradingModeTooltip } from '../../components/trading-mode-tooltip';
|
|
||||||
import { Header, HeaderStat } from '../../components/header';
|
import { Header, HeaderStat } from '../../components/header';
|
||||||
import { AccountsContainer } from '../portfolio/accounts-container';
|
import { AccountsContainer } from '../portfolio/accounts-container';
|
||||||
|
|
||||||
@ -173,9 +176,7 @@ export const TradeMarketHeader = ({
|
|||||||
description={
|
description={
|
||||||
<TradingModeTooltip
|
<TradingModeTooltip
|
||||||
market={market}
|
market={market}
|
||||||
onSelect={(marketId: string) => {
|
compiledGrid={compileGridData(market, onSelect)}
|
||||||
onSelect(marketId);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { toDecimal } from '@vegaprotocol/react-helpers';
|
import { toDecimal } from '@vegaprotocol/react-helpers';
|
||||||
import type { Order } from '../order-hooks';
|
|
||||||
import { OrderTimeInForce, OrderType, Side } from '@vegaprotocol/types';
|
import { OrderTimeInForce, OrderType, Side } from '@vegaprotocol/types';
|
||||||
|
|
||||||
export const getDefaultOrder = (market: {
|
export const getDefaultOrder = (market: {
|
||||||
@ -12,3 +11,13 @@ export const getDefaultOrder = (market: {
|
|||||||
timeInForce: OrderTimeInForce.TIME_IN_FORCE_IOC,
|
timeInForce: OrderTimeInForce.TIME_IN_FORCE_IOC,
|
||||||
size: String(toDecimal(market.positionDecimalPlaces)),
|
size: String(toDecimal(market.positionDecimalPlaces)),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export interface Order {
|
||||||
|
marketId: string;
|
||||||
|
type: OrderType;
|
||||||
|
size: string;
|
||||||
|
side: Side;
|
||||||
|
timeInForce: OrderTimeInForce;
|
||||||
|
price?: string;
|
||||||
|
expiresAt?: Date;
|
||||||
|
}
|
@ -1,2 +1,3 @@
|
|||||||
export * from './get-default-order';
|
export * from './get-default-order';
|
||||||
|
export * from './use-order-validation';
|
||||||
export * from './validate-size';
|
export * from './validate-size';
|
@ -14,12 +14,11 @@ import {
|
|||||||
import type { ValidationProps } from './use-order-validation';
|
import type { ValidationProps } from './use-order-validation';
|
||||||
import { marketTranslations } from './use-order-validation';
|
import { marketTranslations } from './use-order-validation';
|
||||||
import { useOrderValidation } from './use-order-validation';
|
import { useOrderValidation } from './use-order-validation';
|
||||||
import { ERROR_SIZE_DECIMAL } from '../utils/validate-size';
|
import { ERROR_SIZE_DECIMAL } from './validate-size';
|
||||||
|
|
||||||
jest.mock('@vegaprotocol/wallet');
|
jest.mock('@vegaprotocol/wallet');
|
||||||
|
|
||||||
const market = {
|
const market = {
|
||||||
__typename: 'Market',
|
|
||||||
id: 'market-id',
|
id: 'market-id',
|
||||||
decimalPlaces: 2,
|
decimalPlaces: 2,
|
||||||
positionDecimalPlaces: 1,
|
positionDecimalPlaces: 1,
|
||||||
@ -74,7 +73,7 @@ const ERROR = {
|
|||||||
MARKET_CONTINUOUS_LIMIT:
|
MARKET_CONTINUOUS_LIMIT:
|
||||||
'Only limit orders are permitted when market is in auction',
|
'Only limit orders are permitted when market is in auction',
|
||||||
MARKET_CONTINUOUS_TIF:
|
MARKET_CONTINUOUS_TIF:
|
||||||
'Only GTT, GTC and GFA are permitted when market is in auction',
|
'Until the auction ends, you can only place GFA, GTT, or GTC limit orders',
|
||||||
FIELD_SIZE_REQ: 'You need to provide an amount',
|
FIELD_SIZE_REQ: 'You need to provide an amount',
|
||||||
FIELD_SIZE_MIN: `The amount cannot be lower than "${defaultOrder.step}"`,
|
FIELD_SIZE_MIN: `The amount cannot be lower than "${defaultOrder.step}"`,
|
||||||
FIELD_PRICE_REQ: 'You need to provide a price',
|
FIELD_PRICE_REQ: 'You need to provide a price',
|
||||||
@ -165,10 +164,8 @@ describe('useOrderValidation', () => {
|
|||||||
market: { ...defaultOrder.market, tradingMode },
|
market: { ...defaultOrder.market, tradingMode },
|
||||||
orderType: OrderType.TYPE_MARKET,
|
orderType: OrderType.TYPE_MARKET,
|
||||||
});
|
});
|
||||||
expect(result.current).toStrictEqual({
|
expect(result.current.isDisabled).toBeTruthy();
|
||||||
isDisabled: true,
|
expect(result.current.message).toBe(errorMessage);
|
||||||
message: errorMessage,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
@ -3,22 +3,31 @@ import { useMemo } from 'react';
|
|||||||
import { t, toDecimal } from '@vegaprotocol/react-helpers';
|
import { t, toDecimal } from '@vegaprotocol/react-helpers';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
import {
|
import {
|
||||||
|
AuctionTrigger,
|
||||||
MarketState,
|
MarketState,
|
||||||
MarketStateMapping,
|
MarketStateMapping,
|
||||||
MarketTradingMode,
|
MarketTradingMode,
|
||||||
OrderTimeInForce,
|
OrderTimeInForce,
|
||||||
OrderType,
|
OrderType,
|
||||||
} from '@vegaprotocol/types';
|
} from '@vegaprotocol/types';
|
||||||
import { ERROR_SIZE_DECIMAL } from '../utils/validate-size';
|
import { Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||||
import type { Order } from './use-order-submit';
|
import type { Order } from './get-default-order';
|
||||||
|
import { ERROR_SIZE_DECIMAL } from './validate-size';
|
||||||
|
import { MarketDataGrid } from '../trading-mode-tooltip';
|
||||||
|
import { compileGridData } from '../trading-mode-tooltip/compile-grid-data';
|
||||||
|
import type { DealTicketMarketFragment } from '../deal-ticket/__generated__/DealTicket';
|
||||||
|
|
||||||
|
export const isMarketInAuction = (market: DealTicketMarketFragment) => {
|
||||||
|
return [
|
||||||
|
MarketTradingMode.TRADING_MODE_BATCH_AUCTION,
|
||||||
|
MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
|
||||||
|
MarketTradingMode.TRADING_MODE_OPENING_AUCTION,
|
||||||
|
].includes(market.tradingMode);
|
||||||
|
};
|
||||||
|
|
||||||
export type ValidationProps = {
|
export type ValidationProps = {
|
||||||
step?: number;
|
step?: number;
|
||||||
market: {
|
market: DealTicketMarketFragment;
|
||||||
state: MarketState;
|
|
||||||
tradingMode: MarketTradingMode;
|
|
||||||
positionDecimalPlaces: number;
|
|
||||||
};
|
|
||||||
orderType: OrderType;
|
orderType: OrderType;
|
||||||
orderTimeInForce: OrderTimeInForce;
|
orderTimeInForce: OrderTimeInForce;
|
||||||
fieldErrors?: FieldErrors<Order>;
|
fieldErrors?: FieldErrors<Order>;
|
||||||
@ -38,7 +47,10 @@ export const useOrderValidation = ({
|
|||||||
fieldErrors = {},
|
fieldErrors = {},
|
||||||
orderType,
|
orderType,
|
||||||
orderTimeInForce,
|
orderTimeInForce,
|
||||||
}: ValidationProps) => {
|
}: ValidationProps): {
|
||||||
|
message: React.ReactNode | string;
|
||||||
|
isDisabled: boolean;
|
||||||
|
} => {
|
||||||
const { keypair } = useVegaWallet();
|
const { keypair } = useVegaWallet();
|
||||||
const minSize = toDecimal(market.positionDecimalPlaces);
|
const minSize = toDecimal(market.positionDecimalPlaces);
|
||||||
|
|
||||||
@ -88,14 +100,54 @@ export const useOrderValidation = ({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (isMarketInAuction(market)) {
|
||||||
[
|
if (orderType === OrderType.TYPE_MARKET) {
|
||||||
MarketTradingMode.TRADING_MODE_BATCH_AUCTION,
|
if (
|
||||||
MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
|
market.tradingMode ===
|
||||||
MarketTradingMode.TRADING_MODE_OPENING_AUCTION,
|
MarketTradingMode.TRADING_MODE_MONITORING_AUCTION &&
|
||||||
].includes(market.tradingMode)
|
market.data?.trigger === AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY
|
||||||
) {
|
) {
|
||||||
if (orderType !== OrderType.TYPE_LIMIT) {
|
return {
|
||||||
|
isDisabled: true,
|
||||||
|
message: (
|
||||||
|
<span>
|
||||||
|
{t('This market is in auction until it reaches')}{' '}
|
||||||
|
<Tooltip
|
||||||
|
description={
|
||||||
|
<MarketDataGrid grid={compileGridData(market)} />
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<span>{t('sufficient liquidity')}</span>
|
||||||
|
</Tooltip>
|
||||||
|
{'. '}
|
||||||
|
{t('Only limit orders are permitted when market is in auction')}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
market.tradingMode ===
|
||||||
|
MarketTradingMode.TRADING_MODE_MONITORING_AUCTION &&
|
||||||
|
market.data?.trigger === AuctionTrigger.AUCTION_TRIGGER_PRICE
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
isDisabled: true,
|
||||||
|
message: (
|
||||||
|
<span>
|
||||||
|
{t('This market is in auction due to')}{' '}
|
||||||
|
<Tooltip
|
||||||
|
description={
|
||||||
|
<MarketDataGrid grid={compileGridData(market)} />
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<span>{t('high price volatility')}</span>
|
||||||
|
</Tooltip>
|
||||||
|
{'. '}
|
||||||
|
{t('Only limit orders are permitted when market is in auction')}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
isDisabled: true,
|
isDisabled: true,
|
||||||
message: t(
|
message: t(
|
||||||
@ -103,18 +155,68 @@ export const useOrderValidation = ({
|
|||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
orderType === OrderType.TYPE_LIMIT &&
|
||||||
[
|
[
|
||||||
OrderTimeInForce.TIME_IN_FORCE_FOK,
|
OrderTimeInForce.TIME_IN_FORCE_FOK,
|
||||||
OrderTimeInForce.TIME_IN_FORCE_IOC,
|
OrderTimeInForce.TIME_IN_FORCE_IOC,
|
||||||
OrderTimeInForce.TIME_IN_FORCE_GFN,
|
OrderTimeInForce.TIME_IN_FORCE_GFN,
|
||||||
].includes(orderTimeInForce)
|
].includes(orderTimeInForce)
|
||||||
) {
|
) {
|
||||||
|
if (
|
||||||
|
market.tradingMode ===
|
||||||
|
MarketTradingMode.TRADING_MODE_MONITORING_AUCTION &&
|
||||||
|
market.data?.trigger === AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
isDisabled: true,
|
||||||
|
message: (
|
||||||
|
<span>
|
||||||
|
{t('This market is in auction until it reaches')}{' '}
|
||||||
|
<Tooltip
|
||||||
|
description={
|
||||||
|
<MarketDataGrid grid={compileGridData(market)} />
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<span>{t('sufficient liquidity')}</span>
|
||||||
|
</Tooltip>
|
||||||
|
{'. '}
|
||||||
|
{t(
|
||||||
|
`Until the auction ends, you can only place GFA, GTT, or GTC limit orders`
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
market.tradingMode ===
|
||||||
|
MarketTradingMode.TRADING_MODE_MONITORING_AUCTION &&
|
||||||
|
market.data?.trigger === AuctionTrigger.AUCTION_TRIGGER_PRICE
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
isDisabled: true,
|
||||||
|
message: (
|
||||||
|
<span>
|
||||||
|
{t('This market is in auction due to')}{' '}
|
||||||
|
<Tooltip
|
||||||
|
description={
|
||||||
|
<MarketDataGrid grid={compileGridData(market)} />
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<span>{t('high price volatility')}</span>
|
||||||
|
</Tooltip>
|
||||||
|
{'. '}
|
||||||
|
{t(
|
||||||
|
`Until the auction ends, you can only place GFA, GTT, or GTC limit orders`
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
isDisabled: true,
|
isDisabled: true,
|
||||||
message: t(
|
message: t(
|
||||||
'Only GTT, GTC and GFA are permitted when market is in auction'
|
`Until the auction ends, you can only place GFA, GTT, or GTC limit orders`
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
@ -4,6 +4,18 @@ fragment DealTicketMarket on Market {
|
|||||||
positionDecimalPlaces
|
positionDecimalPlaces
|
||||||
state
|
state
|
||||||
tradingMode
|
tradingMode
|
||||||
|
data {
|
||||||
|
market {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
indicativePrice
|
||||||
|
indicativeVolume
|
||||||
|
targetStake
|
||||||
|
suppliedStake
|
||||||
|
auctionStart
|
||||||
|
auctionEnd
|
||||||
|
trigger
|
||||||
|
}
|
||||||
tradableInstrument {
|
tradableInstrument {
|
||||||
instrument {
|
instrument {
|
||||||
id
|
id
|
||||||
@ -14,6 +26,7 @@ fragment DealTicketMarket on Market {
|
|||||||
settlementAsset {
|
settlementAsset {
|
||||||
id
|
id
|
||||||
symbol
|
symbol
|
||||||
|
decimals
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,14 +3,14 @@ import { Schema as Types } from '@vegaprotocol/types';
|
|||||||
import { gql } from '@apollo/client';
|
import { gql } from '@apollo/client';
|
||||||
import * as Apollo from '@apollo/client';
|
import * as Apollo from '@apollo/client';
|
||||||
const defaultOptions = {} as const;
|
const defaultOptions = {} as const;
|
||||||
export type DealTicketMarketFragment = { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string } } } }, depth: { __typename?: 'MarketDepth', lastTrade?: { __typename?: 'Trade', price: string } | null } };
|
export type DealTicketMarketFragment = { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, data?: { __typename?: 'MarketData', indicativePrice: string, indicativeVolume: string, targetStake?: string | null, suppliedStake?: string | null, auctionStart?: string | null, auctionEnd?: string | null, trigger: Types.AuctionTrigger, market: { __typename?: 'Market', id: string } } | null, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, decimals: number, name: string } } } }, depth: { __typename?: 'MarketDepth', lastTrade?: { __typename?: 'Trade', price: string } | null } };
|
||||||
|
|
||||||
export type DealTicketQueryVariables = Types.Exact<{
|
export type DealTicketQueryVariables = Types.Exact<{
|
||||||
marketId: Types.Scalars['ID'];
|
marketId: Types.Scalars['ID'];
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
|
||||||
export type DealTicketQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string } } } }, depth: { __typename?: 'MarketDepth', lastTrade?: { __typename?: 'Trade', price: string } | null } } | null };
|
export type DealTicketQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, data?: { __typename?: 'MarketData', indicativePrice: string, indicativeVolume: string, targetStake?: string | null, suppliedStake?: string | null, auctionStart?: string | null, auctionEnd?: string | null, trigger: Types.AuctionTrigger, market: { __typename?: 'Market', id: string } } | null, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, decimals: number, name: string } } } }, depth: { __typename?: 'MarketDepth', lastTrade?: { __typename?: 'Trade', price: string } | null } } | null };
|
||||||
|
|
||||||
export const DealTicketMarketFragmentDoc = gql`
|
export const DealTicketMarketFragmentDoc = gql`
|
||||||
fragment DealTicketMarket on Market {
|
fragment DealTicketMarket on Market {
|
||||||
@ -19,6 +19,18 @@ export const DealTicketMarketFragmentDoc = gql`
|
|||||||
positionDecimalPlaces
|
positionDecimalPlaces
|
||||||
state
|
state
|
||||||
tradingMode
|
tradingMode
|
||||||
|
data {
|
||||||
|
market {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
indicativePrice
|
||||||
|
indicativeVolume
|
||||||
|
targetStake
|
||||||
|
suppliedStake
|
||||||
|
auctionStart
|
||||||
|
auctionEnd
|
||||||
|
trigger
|
||||||
|
}
|
||||||
tradableInstrument {
|
tradableInstrument {
|
||||||
instrument {
|
instrument {
|
||||||
id
|
id
|
||||||
@ -29,6 +41,7 @@ export const DealTicketMarketFragmentDoc = gql`
|
|||||||
settlementAsset {
|
settlementAsset {
|
||||||
id
|
id
|
||||||
symbol
|
symbol
|
||||||
|
decimals
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { FormGroup, Input } from '@vegaprotocol/ui-toolkit';
|
import { FormGroup, Input } from '@vegaprotocol/ui-toolkit';
|
||||||
import { t, toDecimal } from '@vegaprotocol/react-helpers';
|
import { t, toDecimal } from '@vegaprotocol/react-helpers';
|
||||||
import { validateSize } from '@vegaprotocol/orders';
|
|
||||||
import type { DealTicketAmountProps } from './deal-ticket-amount';
|
import type { DealTicketAmountProps } from './deal-ticket-amount';
|
||||||
|
import { validateSize } from '../deal-ticket-validation';
|
||||||
|
|
||||||
export type DealTicketLimitAmountProps = Omit<
|
export type DealTicketLimitAmountProps = Omit<
|
||||||
DealTicketAmountProps,
|
DealTicketAmountProps,
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { FormGroup, Input } from '@vegaprotocol/ui-toolkit';
|
import { FormGroup, Input, Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||||
import { t, toDecimal } from '@vegaprotocol/react-helpers';
|
import { t, toDecimal } from '@vegaprotocol/react-helpers';
|
||||||
import { validateSize } from '@vegaprotocol/orders';
|
|
||||||
import type { DealTicketAmountProps } from './deal-ticket-amount';
|
import type { DealTicketAmountProps } from './deal-ticket-amount';
|
||||||
|
import { validateSize } from '../deal-ticket-validation/validate-size';
|
||||||
|
import { isMarketInAuction } from '../deal-ticket-validation/use-order-validation';
|
||||||
|
|
||||||
export type DealTicketMarketAmountProps = Omit<
|
export type DealTicketMarketAmountProps = Omit<
|
||||||
DealTicketAmountProps,
|
DealTicketAmountProps,
|
||||||
@ -37,13 +38,26 @@ export const DealTicketMarketAmount = ({
|
|||||||
</div>
|
</div>
|
||||||
<div>@</div>
|
<div>@</div>
|
||||||
<div className="flex-1" data-testid="last-price">
|
<div className="flex-1" data-testid="last-price">
|
||||||
{price && quoteName ? (
|
{isMarketInAuction(market) && (
|
||||||
<>
|
<Tooltip
|
||||||
~{price} {quoteName}
|
description={t(
|
||||||
</>
|
'This market is in auction. The uncrossing price is an indication of what the price is expected to be when the auction ends.'
|
||||||
) : (
|
)}
|
||||||
'-'
|
>
|
||||||
|
<span className={'block mb-2 text-sm text-left'}>
|
||||||
|
{t(`Estimated uncrossing price`)}
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
|
<span className="text-sm">
|
||||||
|
{price && quoteName ? (
|
||||||
|
<>
|
||||||
|
~{price} {quoteName}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
'-'
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -27,6 +27,7 @@ const market: DealTicketMarketFragment = {
|
|||||||
id: 'asset-id',
|
id: 'asset-id',
|
||||||
name: 'asset-name',
|
name: 'asset-name',
|
||||||
symbol: 'asset-symbol',
|
symbol: 'asset-symbol',
|
||||||
|
decimals: 2,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -12,9 +12,11 @@ import { DealTicketAmount } from './deal-ticket-amount';
|
|||||||
import { TimeInForceSelector } from './time-in-force-selector';
|
import { TimeInForceSelector } from './time-in-force-selector';
|
||||||
import type { DealTicketMarketFragment } from './__generated__/DealTicket';
|
import type { DealTicketMarketFragment } from './__generated__/DealTicket';
|
||||||
import { ExpirySelector } from './expiry-selector';
|
import { ExpirySelector } from './expiry-selector';
|
||||||
import type { Order } from '@vegaprotocol/orders';
|
|
||||||
import { getDefaultOrder, useOrderValidation } from '@vegaprotocol/orders';
|
|
||||||
import { OrderTimeInForce, OrderType } from '@vegaprotocol/types';
|
import { OrderTimeInForce, OrderType } from '@vegaprotocol/types';
|
||||||
|
import type { Order } from '../deal-ticket-validation';
|
||||||
|
import { getDefaultOrder } from '../deal-ticket-validation';
|
||||||
|
import { useOrderValidation } from '../deal-ticket-validation/use-order-validation';
|
||||||
|
import { MarketTradingMode } from '@vegaprotocol/types';
|
||||||
|
|
||||||
export type TransactionStatus = 'default' | 'pending';
|
export type TransactionStatus = 'default' | 'pending';
|
||||||
|
|
||||||
@ -70,6 +72,22 @@ export const DealTicket = ({
|
|||||||
[isDisabled, submit, market.decimalPlaces, market.positionDecimalPlaces]
|
[isDisabled, submit, market.decimalPlaces, market.positionDecimalPlaces]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const getPrice = () => {
|
||||||
|
if (
|
||||||
|
market.tradingMode === MarketTradingMode.TRADING_MODE_OPENING_AUCTION ||
|
||||||
|
market.tradingMode === MarketTradingMode.TRADING_MODE_BATCH_AUCTION
|
||||||
|
) {
|
||||||
|
return market.data?.indicativePrice;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
market.tradingMode === MarketTradingMode.TRADING_MODE_MONITORING_AUCTION
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return market.depth.lastTrade?.price;
|
||||||
|
};
|
||||||
|
const price = getPrice();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(onSubmit)} className="p-4" noValidate>
|
<form onSubmit={handleSubmit(onSubmit)} className="p-4" noValidate>
|
||||||
<Controller
|
<Controller
|
||||||
@ -101,11 +119,8 @@ export const DealTicket = ({
|
|||||||
market={market}
|
market={market}
|
||||||
register={register}
|
register={register}
|
||||||
price={
|
price={
|
||||||
market.depth.lastTrade
|
price
|
||||||
? addDecimalsFormatNumber(
|
? addDecimalsFormatNumber(price, market.decimalPlaces)
|
||||||
market.depth.lastTrade.price,
|
|
||||||
market.decimalPlaces
|
|
||||||
)
|
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
quoteName={market.tradableInstrument.instrument.product.quoteName}
|
quoteName={market.tradableInstrument.instrument.product.quoteName}
|
||||||
|
@ -1 +1,3 @@
|
|||||||
export * from './deal-ticket';
|
export * from './deal-ticket';
|
||||||
|
export * from './deal-ticket-validation';
|
||||||
|
export * from './trading-mode-tooltip';
|
||||||
|
@ -0,0 +1,98 @@
|
|||||||
|
import {
|
||||||
|
t,
|
||||||
|
getDateTimeFormat,
|
||||||
|
addDecimalsFormatNumber,
|
||||||
|
} from '@vegaprotocol/react-helpers';
|
||||||
|
import { Link as UiToolkitLink } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import Link from 'next/link';
|
||||||
|
import { MarketTradingMode, AuctionTrigger } from '@vegaprotocol/types';
|
||||||
|
import type { ReactNode } from 'react';
|
||||||
|
import type { MarketDataGridProps } from './market-data-grid';
|
||||||
|
import type { DealTicketMarketFragment } from '../deal-ticket/__generated__/DealTicket';
|
||||||
|
|
||||||
|
export const compileGridData = (
|
||||||
|
market: DealTicketMarketFragment,
|
||||||
|
onSelect?: (id: string) => void
|
||||||
|
): { label: ReactNode; value?: ReactNode }[] => {
|
||||||
|
const grid: MarketDataGridProps['grid'] = [];
|
||||||
|
const isLiquidityMonitoringAuction =
|
||||||
|
market.tradingMode === MarketTradingMode.TRADING_MODE_MONITORING_AUCTION &&
|
||||||
|
market.data?.trigger === AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY;
|
||||||
|
|
||||||
|
const formatStake = (value: string) => {
|
||||||
|
const formattedValue = addDecimalsFormatNumber(
|
||||||
|
value,
|
||||||
|
market.tradableInstrument.instrument.product.settlementAsset.decimals
|
||||||
|
);
|
||||||
|
const asset =
|
||||||
|
market.tradableInstrument.instrument.product.settlementAsset.symbol;
|
||||||
|
return `${formattedValue} ${asset}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!market.data) return grid;
|
||||||
|
|
||||||
|
if (market.data?.auctionStart) {
|
||||||
|
grid.push({
|
||||||
|
label: t('Auction start'),
|
||||||
|
value: getDateTimeFormat().format(new Date(market.data.auctionStart)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (market.data?.auctionEnd) {
|
||||||
|
const endDate = getDateTimeFormat().format(
|
||||||
|
new Date(market.data.auctionEnd)
|
||||||
|
);
|
||||||
|
grid.push({
|
||||||
|
label: isLiquidityMonitoringAuction
|
||||||
|
? t('Est auction end')
|
||||||
|
: t('Auction end'),
|
||||||
|
value: isLiquidityMonitoringAuction ? `~${endDate}` : endDate,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLiquidityMonitoringAuction && market.data?.targetStake) {
|
||||||
|
grid.push({
|
||||||
|
label: t('Target liquidity'),
|
||||||
|
value: formatStake(market.data.targetStake),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLiquidityMonitoringAuction && market.data?.suppliedStake) {
|
||||||
|
grid.push({
|
||||||
|
label: (
|
||||||
|
<Link href={`/liquidity/${market.id}`} passHref={true}>
|
||||||
|
<UiToolkitLink onClick={() => onSelect && onSelect(market.id)}>
|
||||||
|
{t('Current liquidity')}
|
||||||
|
</UiToolkitLink>
|
||||||
|
</Link>
|
||||||
|
),
|
||||||
|
value: formatStake(market.data.suppliedStake),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (market.data?.indicativePrice) {
|
||||||
|
grid.push({
|
||||||
|
label: t('Est uncrossing price'),
|
||||||
|
value:
|
||||||
|
'~' +
|
||||||
|
addDecimalsFormatNumber(
|
||||||
|
market.data.indicativePrice,
|
||||||
|
market.positionDecimalPlaces
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (market.data?.indicativeVolume) {
|
||||||
|
grid.push({
|
||||||
|
label: t('Est uncrossing vol'),
|
||||||
|
value:
|
||||||
|
'~' +
|
||||||
|
addDecimalsFormatNumber(
|
||||||
|
market.data.indicativeVolume,
|
||||||
|
market.positionDecimalPlaces
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return grid;
|
||||||
|
};
|
@ -0,0 +1,3 @@
|
|||||||
|
export * from './market-data-grid';
|
||||||
|
export * from './trading-mode-tooltip';
|
||||||
|
export * from './compile-grid-data';
|
@ -0,0 +1,26 @@
|
|||||||
|
import type { ReactNode } from 'react';
|
||||||
|
|
||||||
|
export type MarketDataGridProps = {
|
||||||
|
grid: {
|
||||||
|
label: string | ReactNode;
|
||||||
|
value?: ReactNode;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const MarketDataGrid = ({ grid }: MarketDataGridProps) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{grid.map(
|
||||||
|
({ label, value }, index) =>
|
||||||
|
value && (
|
||||||
|
<div key={index} className="grid grid-cols-2">
|
||||||
|
<span data-testid="tooltip-label">{label}</span>
|
||||||
|
<span data-testid="tooltip-value" className="text-right">
|
||||||
|
{value}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,93 @@
|
|||||||
|
import { t } from '@vegaprotocol/react-helpers';
|
||||||
|
import { MarketTradingMode, AuctionTrigger } from '@vegaprotocol/types';
|
||||||
|
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import type { ReactNode } from 'react';
|
||||||
|
import { MarketDataGrid } from './market-data-grid';
|
||||||
|
|
||||||
|
type TradingModeTooltipProps = {
|
||||||
|
market: {
|
||||||
|
tradingMode: MarketTradingMode;
|
||||||
|
data: { trigger: AuctionTrigger | null } | null;
|
||||||
|
};
|
||||||
|
compiledGrid: { label: ReactNode; value?: ReactNode }[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TradingModeTooltip = ({
|
||||||
|
market,
|
||||||
|
compiledGrid,
|
||||||
|
}: TradingModeTooltipProps) => {
|
||||||
|
switch (market.tradingMode) {
|
||||||
|
case MarketTradingMode.TRADING_MODE_CONTINUOUS: {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{t(
|
||||||
|
'This is the standard trading mode where trades are executed whenever orders are received.'
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case MarketTradingMode.TRADING_MODE_OPENING_AUCTION: {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p className="mb-4">
|
||||||
|
<span>
|
||||||
|
{t(
|
||||||
|
'This new market is in an opening auction to determine a fair mid-price before starting continuous trading.'
|
||||||
|
)}
|
||||||
|
</span>{' '}
|
||||||
|
<ExternalLink href="https://docs.vega.xyz/docs/testnet/concepts/trading-on-vega/trading-modes#auction-type-opening">
|
||||||
|
{t('Find out more')}
|
||||||
|
</ExternalLink>
|
||||||
|
</p>
|
||||||
|
<MarketDataGrid grid={compiledGrid} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case MarketTradingMode.TRADING_MODE_MONITORING_AUCTION: {
|
||||||
|
switch (market.data?.trigger) {
|
||||||
|
case AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY: {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p data-testid="tooltip-market-info" className="mb-4">
|
||||||
|
<span>
|
||||||
|
{t(
|
||||||
|
'This market is in auction until it reaches sufficient liquidity.'
|
||||||
|
)}
|
||||||
|
</span>{' '}
|
||||||
|
<ExternalLink href="https://docs.vega.xyz/docs/testnet/concepts/trading-on-vega/trading-modes#auction-type-liquidity-monitoring">
|
||||||
|
{t('Find out more')}
|
||||||
|
</ExternalLink>
|
||||||
|
</p>
|
||||||
|
<MarketDataGrid grid={compiledGrid} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case AuctionTrigger.AUCTION_TRIGGER_PRICE: {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p className="mb-4">
|
||||||
|
<span>
|
||||||
|
{t('This market is in auction due to high price volatility.')}
|
||||||
|
</span>{' '}
|
||||||
|
<ExternalLink href="https://docs.vega.xyz/docs/testnet/concepts/trading-on-vega/trading-modes#auction-type-price-monitoring">
|
||||||
|
{t('Find out more')}
|
||||||
|
</ExternalLink>
|
||||||
|
</p>
|
||||||
|
<MarketDataGrid grid={compiledGrid} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case MarketTradingMode.TRADING_MODE_NO_TRADING: {
|
||||||
|
return <>{t('No trading enabled for this market.')}</>;
|
||||||
|
}
|
||||||
|
case MarketTradingMode.TRADING_MODE_BATCH_AUCTION:
|
||||||
|
default: {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -39,7 +39,7 @@ export const LiquidityTable = forwardRef<AgGridReact, LiquidityTableProps>(
|
|||||||
({ data, symbol = '', assetDecimalPlaces }, ref) => {
|
({ data, symbol = '', assetDecimalPlaces }, ref) => {
|
||||||
const assetDecimalsFormatter = ({ value }: ValueFormatterParams) => {
|
const assetDecimalsFormatter = ({ value }: ValueFormatterParams) => {
|
||||||
if (!value) return '-';
|
if (!value) return '-';
|
||||||
return `${addDecimalsFormatNumber(value, assetDecimalPlaces ?? 0)}`;
|
return `${addDecimalsFormatNumber(value, assetDecimalPlaces ?? 0, 5)}`;
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<AgGrid
|
<AgGrid
|
||||||
@ -54,6 +54,7 @@ export const LiquidityTable = forwardRef<AgGridReact, LiquidityTableProps>(
|
|||||||
resizable: true,
|
resizable: true,
|
||||||
minWidth: 100,
|
minWidth: 100,
|
||||||
tooltipComponent: TooltipCellComponent,
|
tooltipComponent: TooltipCellComponent,
|
||||||
|
sortable: true,
|
||||||
}}
|
}}
|
||||||
rowData={data}
|
rowData={data}
|
||||||
>
|
>
|
||||||
|
@ -77,7 +77,9 @@ export const SelectMarketLandingTable = ({
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<Link href="/markets">{'Or view full market list'}</Link>
|
<div className="mt-4 text-md">
|
||||||
|
<Link href="/markets">{'Or view full market list'}</Link>
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,3 +1,2 @@
|
|||||||
export * from './components';
|
export * from './components';
|
||||||
export * from './order-hooks';
|
export * from './order-hooks';
|
||||||
export * from './utils';
|
|
||||||
|
@ -2,5 +2,4 @@ export * from './__generated__/OrderEvent';
|
|||||||
export * from './order-event-query';
|
export * from './order-event-query';
|
||||||
export * from './use-order-cancel';
|
export * from './use-order-cancel';
|
||||||
export * from './use-order-submit';
|
export * from './use-order-submit';
|
||||||
export * from './use-order-validation';
|
|
||||||
export * from './use-order-edit';
|
export * from './use-order-edit';
|
||||||
|
2
libs/types/src/__generated__/types.ts
generated
2
libs/types/src/__generated__/types.ts
generated
@ -3625,7 +3625,7 @@ export type QuerypartiesConnectionArgs = {
|
|||||||
|
|
||||||
/** Queries allow a caller to read data and filter data via GraphQL. */
|
/** Queries allow a caller to read data and filter data via GraphQL. */
|
||||||
export type QuerypartyArgs = {
|
export type QuerypartyArgs = {
|
||||||
id: Scalars['ID'];
|
id?: InputMaybe<Scalars['ID']>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,11 +14,15 @@ export const InputError = ({
|
|||||||
...props
|
...props
|
||||||
}: InputErrorProps) => {
|
}: InputErrorProps) => {
|
||||||
const effectiveClassName = classNames(
|
const effectiveClassName = classNames(
|
||||||
'text-sm text-vega-red flex items-center',
|
'text-sm flex items-center',
|
||||||
'mt-2',
|
'mt-2',
|
||||||
{
|
{
|
||||||
'border-danger': intent === 'danger',
|
'border-danger': intent === 'danger',
|
||||||
'border-warning': intent === 'warning',
|
'border-warning': intent === 'warning',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'text-warning': intent === 'warning',
|
||||||
|
'text-danger': intent === 'danger',
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
|
@ -92,7 +92,10 @@ export const SparklineView = ({
|
|||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
data-testid="sparkline-svg"
|
data-testid="sparkline-svg"
|
||||||
className={classNames('pt-px pr-0 w-full overflow-visible', className)}
|
className={classNames(
|
||||||
|
'pt-px pr-0 w-full overflow-visible p-2',
|
||||||
|
className
|
||||||
|
)}
|
||||||
width={width}
|
width={width}
|
||||||
height={height}
|
height={height}
|
||||||
viewBox="0 0 100 100"
|
viewBox="0 0 100 100"
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
"test": "nx test",
|
"test": "nx test",
|
||||||
"postinstall": "husky install && yarn tsc -b tools/executors/next && yarn tsc -b tools/executors/webpack",
|
"postinstall": "husky install && yarn tsc -b tools/executors/next && yarn tsc -b tools/executors/webpack",
|
||||||
"test:all": "nx run-many --all --target=test",
|
"test:all": "nx run-many --all --target=test",
|
||||||
|
"build:all": "nx run-many --all --target=build",
|
||||||
"vegacapsule": "vegacapsule network bootstrap --config-path=../frontend-monorepo/vegacapsule/config.hcl"
|
"vegacapsule": "vegacapsule network bootstrap --config-path=../frontend-monorepo/vegacapsule/config.hcl"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
Loading…
Reference in New Issue
Block a user