Compare commits

...
Author SHA1 Message Date
Dariusz Majcherczyk 2b03d4dcf9 Merge branch 'develop' into feat/4672-max-number-of-active-stop-orders 2023-09-06 13:08:47 +02:00
Dariusz Majcherczyk 8c3fbe8d96 feat: add data-testid for market propose 2023-09-06 12:23:48 +02:00
Sam Keen 13ab1a2045 feat(explorer): add headings and anchors to net params page (#4691) 2023-09-06 12:23:48 +02:00
Sam Keen 6d201b831a feat(governance): vote status improvements (#4654) 2023-09-06 12:23:48 +02:00
Matthew Russell f8db790c93 chore: release v0.21.0-core-0.72.14 2023-09-06 12:23:48 +02:00
Matthew Russell 11075ad596 chore(trading): minor style changes 2 (#4698) 2023-09-06 12:23:48 +02:00
Matthew Russell ceead104a8 chore(trading): update flags for 0.72.14 (#4697) 2023-09-06 12:23:47 +02:00
Matthew Russell 81f257966a chore(trading): minor style tweaks (#4696) 2023-09-06 12:23:47 +02:00
Bartłomiej Głownia 69843d87d2 feat(deal-ticket): make margin values section expandable (#4664) 2023-09-06 12:23:47 +02:00
m.rayandMatthew Russell 9901c83d70 chore(trading): update dropdowns (#4694)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-09-06 12:23:47 +02:00
Matthew Russell 2dac99b0e8 feat(market-depth): additional orderbook grouping levels (#4658) 2023-09-06 12:23:47 +02:00
Sam Keen 9c50d52111 fix(governance,utils,react-helpers): token locale formatting issue (#4678) 2023-09-06 12:23:47 +02:00
Joe Tsang f58bdbaa7a test(explorer): add e2e test for explorer oracles (#4657) 2023-09-06 12:23:46 +02:00
Edd 3e939109df chore(explorer,trading,governance): reconfigure hooks (#4655) 2023-09-06 12:23:46 +02:00
Maciek cf34fb2ef9 chore(trading): handle negative decimals (#4659) 2023-09-06 12:23:46 +02:00
Bartłomiej Głownia f772dade7f feat: show maxmimum number of active stop orders limit warning 2023-09-04 16:16:15 +02:00
8 changed files with 93 additions and 11 deletions
@@ -39,7 +39,11 @@ export const MarketsPage = () => {
id="proposed-markets"
name={t('Proposed markets')}
menu={
<TradingAnchorButton size="extra-small" href={externalLink}>
<TradingAnchorButton
size="extra-small"
data-testid="propose-new-market"
href={externalLink}
>
{t('Propose a new market')}
</TradingAnchorButton>
}
@@ -78,8 +78,18 @@ const triggerPriceWarningMessage = 'stop-order-warning-message-trigger-price';
const triggerTrailingPercentOffsetErrorMessage =
'stop-order-error-message-trigger-trailing-percent-offset';
const numberOfActiveOrdersLimit = 'stop-order-warning-limit';
const ocoPostfix = (id: string, postfix = true) => (postfix ? `${id}-oco` : id);
const mockDataProvider = jest.fn((...args) => ({
data: Array(0),
}));
jest.mock('@vegaprotocol/data-provider', () => ({
...jest.requireActual('@vegaprotocol/data-provider'),
useDataProvider: jest.fn((...args) => mockDataProvider(...args)),
}));
describe('StopOrder', () => {
beforeEach(() => {
localStorage.clear();
@@ -460,4 +470,26 @@ describe('StopOrder', () => {
new Date(screen.getByTestId<HTMLInputElement>(datePicker).value).getTime()
).toEqual(now);
});
it('shows limit of active stop orders number', async () => {
mockDataProvider.mockReturnValue({
data: Array(4),
});
render(generateJsx());
expect(mockDataProvider.mock.lastCall?.[0].skip).toBe(true);
await userEvent.type(screen.getByTestId(sizeInput), '0.01');
expect(mockDataProvider.mock.lastCall?.[0].skip).toBe(false);
expect(screen.getByTestId(numberOfActiveOrdersLimit)).toBeInTheDocument();
});
it('counts oco as two orders', async () => {
mockDataProvider.mockReturnValue({
data: Array(3),
});
render(generateJsx());
await userEvent.type(screen.getByTestId(sizeInput), '0.01');
expect(screen.queryByTestId(numberOfActiveOrdersLimit)).toBeNull();
await userEvent.click(screen.getByTestId(oco));
expect(screen.getByTestId(numberOfActiveOrdersLimit)).toBeInTheDocument();
});
});
@@ -26,6 +26,7 @@ import {
TradingButton as Button,
Pill,
Intent,
Notification,
} from '@vegaprotocol/ui-toolkit';
import { getDerivedPrice } from '@vegaprotocol/markets';
import type { Market } from '@vegaprotocol/markets';
@@ -53,6 +54,8 @@ import { DealTicketFeeDetails } from './deal-ticket-fee-details';
import { validateExpiration } from '../../utils';
import { NOTIONAL_SIZE_TOOLTIP_TEXT } from '../../constants';
import { KeyValue } from './key-value';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { stopOrdersProvider } from '@vegaprotocol/orders';
export interface StopOrderProps {
market: Market;
@@ -60,6 +63,8 @@ export interface StopOrderProps {
submit: (order: StopOrdersSubmission) => void;
}
const MAX_NUMBER_OF_ACTIVE_STOP_ORDERS = 4;
const POLLING_TIME = 2000;
const trailingPercentOffsetStep = '0.1';
const getDefaultValues = (
@@ -802,6 +807,27 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
const triggerTrailingPercentOffset = watch('triggerTrailingPercentOffset');
const triggerType = watch('triggerType');
const { data: activeStopOrders, reload } = useDataProvider({
dataProvider: stopOrdersProvider,
variables: {
filter: {
parties: pubKey ? [pubKey] : [],
markets: [market.id],
liveOnly: true,
},
},
skip: !(pubKey && (formState.isDirty || formState.submitCount)),
});
useEffect(() => {
const interval = setInterval(() => {
reload();
}, POLLING_TIME);
return () => {
clearInterval(interval);
};
}, [reload]);
useEffect(() => {
const storedSize = storedFormValues?.[dealTicketType]?.size;
if (storedSize && size !== storedSize) {
@@ -864,7 +890,6 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
{errors.type.message}
</InputError>
)}
<Controller
name="side"
control={control}
@@ -1095,6 +1120,20 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
</>
)}
<NoWalletWarning isReadOnly={isReadOnly} />
{(activeStopOrders?.length ?? 0) + (oco ? 2 : 1) >
MAX_NUMBER_OF_ACTIVE_STOP_ORDERS ? (
<div className="mb-2">
<Notification
intent={Intent.Warning}
testId={'stop-order-warning-limit'}
message={t(
'There is a limit of %s active stop orders per market. Orders submitted above the limit will be immediately rejected.',
[MAX_NUMBER_OF_ACTIVE_STOP_ORDERS.toString()]
)}
/>
</div>
) : null}
<SubmitButton
assetUnit={assetUnit}
market={market}
@@ -144,8 +144,8 @@ fragment StopOrderFields on StopOrder {
}
}
query StopOrders($partyId: ID!) {
stopOrders(filter: { parties: [$partyId] }) {
query StopOrders($filter: StopOrderFilter) {
stopOrders(filter: $filter) {
edges {
node {
...StopOrderFields
@@ -37,7 +37,7 @@ export type OrderSubmissionFieldsFragment = { __typename?: 'OrderSubmission', ma
export type StopOrderFieldsFragment = { __typename?: 'StopOrder', id: string, ocoLinkId?: string | null, expiresAt?: any | null, expiryStrategy?: Types.StopOrderExpiryStrategy | null, triggerDirection: Types.StopOrderTriggerDirection, status: Types.StopOrderStatus, createdAt: any, updatedAt?: any | null, partyId: string, marketId: string, order?: { __typename?: 'Order', id: string, type?: Types.OrderType | null, side: Types.Side, size: string, status: Types.OrderStatus, rejectionReason?: Types.OrderRejectionReason | null, price: string, timeInForce: Types.OrderTimeInForce, remaining: string, expiresAt?: any | null, createdAt: any, updatedAt?: any | null, postOnly?: boolean | null, reduceOnly?: boolean | null, market: { __typename?: 'Market', id: string }, liquidityProvision?: { __typename: 'LiquidityProvision' } | null, peggedOrder?: { __typename: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null, icebergOrder?: { __typename: 'IcebergOrder', peakSize: string, minimumVisibleSize: string, reservedRemaining: string } | null } | null, trigger: { __typename?: 'StopOrderPrice', price: string } | { __typename?: 'StopOrderTrailingPercentOffset', trailingPercentOffset: string }, submission: { __typename?: 'OrderSubmission', marketId: string, price: string, size: string, side: Types.Side, timeInForce: Types.OrderTimeInForce, expiresAt: any, type: Types.OrderType, reference?: string | null, postOnly?: boolean | null, reduceOnly?: boolean | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null } };
export type StopOrdersQueryVariables = Types.Exact<{
partyId: Types.Scalars['ID'];
filter?: Types.InputMaybe<Types.StopOrderFilter>;
}>;
@@ -283,8 +283,8 @@ export function useOrdersUpdateSubscription(baseOptions: Apollo.SubscriptionHook
export type OrdersUpdateSubscriptionHookResult = ReturnType<typeof useOrdersUpdateSubscription>;
export type OrdersUpdateSubscriptionResult = Apollo.SubscriptionResult<OrdersUpdateSubscription>;
export const StopOrdersDocument = gql`
query StopOrders($partyId: ID!) {
stopOrders(filter: {parties: [$partyId]}) {
query StopOrders($filter: StopOrderFilter) {
stopOrders(filter: $filter) {
edges {
node {
...StopOrderFields
@@ -306,11 +306,11 @@ export const StopOrdersDocument = gql`
* @example
* const { data, loading, error } = useStopOrdersQuery({
* variables: {
* partyId: // value for 'partyId'
* filter: // value for 'filter'
* },
* });
*/
export function useStopOrdersQuery(baseOptions: Apollo.QueryHookOptions<StopOrdersQuery, StopOrdersQueryVariables>) {
export function useStopOrdersQuery(baseOptions?: Apollo.QueryHookOptions<StopOrdersQuery, StopOrdersQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<StopOrdersQuery, StopOrdersQueryVariables>(StopOrdersDocument, options);
}
@@ -1,2 +1,3 @@
export * from './__generated__/Orders';
export * from './order-data-provider';
export * from './stop-orders-data-provider';
@@ -7,7 +7,7 @@ import type { StopOrder } from '../order-data-provider/stop-orders-data-provider
import { useDataProvider } from '@vegaprotocol/data-provider';
import { stopOrdersWithMarketProvider } from '../order-data-provider/stop-orders-data-provider';
import { OrderViewDialog } from '../order-list/order-view-dialog';
import type { Order } from '../order-data-provider';
import type { Order, StopOrdersQueryVariables } from '../order-data-provider';
export interface StopOrdersManagerProps {
partyId: string;
@@ -26,7 +26,11 @@ export const StopOrdersManager = ({
}: StopOrdersManagerProps) => {
const create = useVegaTransactionStore((state) => state.create);
const [viewOrder, setViewOrder] = useState<Order | null>(null);
const variables = { partyId };
const variables: StopOrdersQueryVariables = {
filter: {
parties: [partyId],
},
};
const { data, error, reload } = useDataProvider({
dataProvider: stopOrdersWithMarketProvider,
@@ -132,6 +132,7 @@ export const TradingAnchorButton = forwardRef<
children,
className,
subLabel,
...props
},
ref
) => (
@@ -139,6 +140,7 @@ export const TradingAnchorButton = forwardRef<
ref={ref}
href={href}
className={getClassName({ size, subLabel, intent }, className)}
{...props}
>
<Content icon={icon} subLabel={subLabel} children={children} />
</a>