diff --git a/apps/trading/components/market-banner/market-successor-proposal-banner.tsx b/apps/trading/components/market-banner/market-successor-proposal-banner.tsx
index 68104fe98..8797909bf 100644
--- a/apps/trading/components/market-banner/market-successor-proposal-banner.tsx
+++ b/apps/trading/components/market-banner/market-successor-proposal-banner.tsx
@@ -1,4 +1,4 @@
-import { useState } from 'react';
+import { Fragment, useState } from 'react';
import type {
SuccessorProposalListFieldsFragment,
NewMarketSuccessorFieldsFragment,
@@ -52,7 +52,7 @@ export const MarketSuccessorProposalBanner = ({
TOKEN_PROPOSAL.replace(':id', item.id || '')
);
return (
- <>
+
{
(item.terms?.change as NewMarketSuccessorFieldsFragment)
@@ -60,7 +60,7 @@ export const MarketSuccessorProposalBanner = ({
}
{i < successors.length - 1 && ', '}
- >
+
);
})}
diff --git a/apps/trading/components/market-header/market-header.tsx b/apps/trading/components/market-header/market-header.tsx
index fe7f684b2..178f67e9d 100644
--- a/apps/trading/components/market-header/market-header.tsx
+++ b/apps/trading/components/market-header/market-header.tsx
@@ -3,7 +3,7 @@ import { Header, HeaderTitle } from '../header';
import { useParams } from 'react-router-dom';
import { MarketSelector } from '../../components/market-selector/market-selector';
import { MarketHeaderStats } from '../../client-pages/market/market-header-stats';
-import { useMarket } from '@vegaprotocol/markets';
+import { useMarket, useMarketList } from '@vegaprotocol/markets';
import { useState } from 'react';
export const MarketHeader = () => {
@@ -11,6 +11,10 @@ export const MarketHeader = () => {
const { data } = useMarket(marketId);
const [open, setOpen] = useState(false);
+ // Ensure that markets are kept cached so opening the list
+ // shows all markets instantly
+ useMarketList();
+
if (!data) return null;
return (
diff --git a/apps/trading/components/market-selector/market-selector.spec.tsx b/apps/trading/components/market-selector/market-selector.spec.tsx
index a3fa3c5c8..faf20a8ff 100644
--- a/apps/trading/components/market-selector/market-selector.spec.tsx
+++ b/apps/trading/components/market-selector/market-selector.spec.tsx
@@ -132,6 +132,7 @@ describe('MarketSelector', () => {
data: markets,
loading: false,
error: undefined,
+ reload: jest.fn(),
});
it('Button "All" should be selected by default', () => {
diff --git a/apps/trading/components/market-selector/market-selector.tsx b/apps/trading/components/market-selector/market-selector.tsx
index 59ec08fc5..7750ea33d 100644
--- a/apps/trading/components/market-selector/market-selector.tsx
+++ b/apps/trading/components/market-selector/market-selector.tsx
@@ -1,6 +1,6 @@
import { t } from '@vegaprotocol/i18n';
import uniqBy from 'lodash/uniqBy';
-import type { MarketMaybeWithDataAndCandles } from '@vegaprotocol/markets';
+import { type MarketMaybeWithDataAndCandles } from '@vegaprotocol/markets';
import {
TradingInput,
TinyScroll,
@@ -8,7 +8,7 @@ import {
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import type { CSSProperties } from 'react';
-import { useCallback, useState, useMemo, useRef } from 'react';
+import { useCallback, useState, useMemo, useRef, useEffect } from 'react';
import { FixedSizeList } from 'react-window';
import { useMarketSelectorList } from './use-market-selector-list';
import type { ProductType } from './product-selector';
@@ -44,7 +44,12 @@ export const MarketSelector = ({
assets: [],
});
const allProducts = filter.product === Product.All;
- const { markets, data, loading, error } = useMarketSelectorList(filter);
+ const { markets, data, loading, error, reload } =
+ useMarketSelectorList(filter);
+
+ useEffect(() => {
+ reload();
+ }, [reload]);
return (
diff --git a/apps/trading/components/market-selector/sort-dropdown.tsx b/apps/trading/components/market-selector/sort-dropdown.tsx
index fbada5c9f..82e6382ea 100644
--- a/apps/trading/components/market-selector/sort-dropdown.tsx
+++ b/apps/trading/components/market-selector/sort-dropdown.tsx
@@ -15,6 +15,7 @@ export const Sort = {
Gained: 'Gained',
Lost: 'Lost',
New: 'New',
+ TopTraded: 'TopTraded',
} as const;
export type SortType = keyof typeof Sort;
@@ -26,6 +27,7 @@ export const SortTypeMapping: {
[Sort.Gained]: 'Top gaining',
[Sort.Lost]: 'Top losing',
[Sort.New]: 'New markets',
+ [Sort.TopTraded]: 'Top traded',
};
const SortIconMapping: {
@@ -35,6 +37,7 @@ const SortIconMapping: {
[Sort.Gained]: VegaIconNames.TREND_UP,
[Sort.Lost]: VegaIconNames.TREND_DOWN,
[Sort.New]: VegaIconNames.STAR,
+ [Sort.TopTraded]: VegaIconNames.ARROW_UP,
};
export const SortDropdown = ({
diff --git a/apps/trading/components/market-selector/use-market-selector-list.ts b/apps/trading/components/market-selector/use-market-selector-list.ts
index a9626fd66..72bef8f54 100644
--- a/apps/trading/components/market-selector/use-market-selector-list.ts
+++ b/apps/trading/components/market-selector/use-market-selector-list.ts
@@ -1,7 +1,11 @@
import { useMemo } from 'react';
import orderBy from 'lodash/orderBy';
import { MarketState } from '@vegaprotocol/types';
-import { calcCandleVolume, useMarketList } from '@vegaprotocol/markets';
+import {
+ calcCandleVolume,
+ calcTradedFactor,
+ useMarketList,
+} from '@vegaprotocol/markets';
import { priceChangePercentage } from '@vegaprotocol/utils';
import type { Filter } from '../../components/market-selector/market-selector';
import { Sort } from './sort-dropdown';
@@ -20,7 +24,7 @@ export const useMarketSelectorList = ({
sort,
searchTerm,
}: Filter) => {
- const { data, loading, error } = useMarketList();
+ const { data, loading, error, reload } = useMarketList();
const markets = useMemo(() => {
if (!data?.length) return [];
@@ -94,10 +98,14 @@ export const useMarketSelectorList = ({
);
}
+ if (sort === Sort.TopTraded) {
+ return orderBy(markets, [(m) => calcTradedFactor(m)], ['desc']);
+ }
+
return markets;
}, [data, product, searchTerm, assets, sort]);
- return { markets, data, loading, error };
+ return { markets, data, loading, error, reload };
};
export const isMarketActive = (state: MarketState) => {
diff --git a/apps/trading/components/navbar/nav-header.tsx b/apps/trading/components/navbar/nav-header.tsx
index ed78eacb1..da12b118a 100644
--- a/apps/trading/components/navbar/nav-header.tsx
+++ b/apps/trading/components/navbar/nav-header.tsx
@@ -1,6 +1,6 @@
import { VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
import { MarketSelector } from '../market-selector';
-import { useMarket } from '@vegaprotocol/markets';
+import { useMarket, useMarketList } from '@vegaprotocol/markets';
import { t } from '@vegaprotocol/i18n';
import { useParams } from 'react-router-dom';
import * as PopoverPrimitive from '@radix-ui/react-popover';
@@ -14,6 +14,10 @@ export const NavHeader = () => {
const { data } = useMarket(marketId);
const [open, setOpen] = useState(false);
+ // Ensure that markets are kept cached so opening the list
+ // shows all markets instantly
+ useMarketList();
+
if (!marketId) return null;
return (
diff --git a/libs/markets/src/lib/market-utils.spec.tsx b/libs/markets/src/lib/market-utils.spec.tsx
index a58abcffb..b974098c8 100644
--- a/libs/markets/src/lib/market-utils.spec.tsx
+++ b/libs/markets/src/lib/market-utils.spec.tsx
@@ -1,6 +1,10 @@
import * as Schema from '@vegaprotocol/types';
-import type { Market } from './markets-provider';
-import { filterAndSortMarkets, totalFeesPercentage } from './market-utils';
+import type { Market, MarketMaybeWithDataAndCandles } from './markets-provider';
+import {
+ calcTradedFactor,
+ filterAndSortMarkets,
+ totalFeesPercentage,
+} from './market-utils';
const { MarketState, MarketTradingMode } = Schema;
const MARKET_A: Partial = {
@@ -77,3 +81,52 @@ describe('totalFees', () => {
expect(totalFeesPercentage(i)).toEqual(o);
});
});
+
+describe('calcTradedFactor', () => {
+ const marketA = {
+ data: {
+ markPrice: '10',
+ },
+ candles: [
+ {
+ volume: '1000',
+ },
+ ],
+ tradableInstrument: {
+ instrument: {
+ product: {
+ settlementAsset: {
+ decimals: 18,
+ quantum: '1000000000000000000', // 1
+ },
+ },
+ },
+ },
+ };
+ const marketB = {
+ data: {
+ markPrice: '10',
+ },
+ candles: [
+ {
+ volume: '1000',
+ },
+ ],
+ tradableInstrument: {
+ instrument: {
+ product: {
+ settlementAsset: {
+ decimals: 18,
+ quantum: '1', // 0.0000000000000000001
+ },
+ },
+ },
+ },
+ };
+ it('a is "traded" more than b', () => {
+ const fa = calcTradedFactor(marketA as MarketMaybeWithDataAndCandles);
+ const fb = calcTradedFactor(marketB as MarketMaybeWithDataAndCandles);
+ // it should be true because market a's asset is "more valuable" than b's
+ expect(fa > fb).toBeTruthy();
+ });
+});
diff --git a/libs/markets/src/lib/market-utils.ts b/libs/markets/src/lib/market-utils.ts
index 871f2afa4..448acf247 100644
--- a/libs/markets/src/lib/market-utils.ts
+++ b/libs/markets/src/lib/market-utils.ts
@@ -1,8 +1,13 @@
-import { formatNumberPercentage } from '@vegaprotocol/utils';
+import { formatNumberPercentage, toBigNum } from '@vegaprotocol/utils';
import * as Schema from '@vegaprotocol/types';
import BigNumber from 'bignumber.js';
import orderBy from 'lodash/orderBy';
-import type { Market, Candle, MarketMaybeWithData } from '../';
+import type {
+ Market,
+ Candle,
+ MarketMaybeWithData,
+ MarketMaybeWithDataAndCandles,
+} from '../';
const { MarketState, MarketTradingMode } = Schema;
export const totalFees = (fees: Market['fees']['factors']) => {
@@ -86,3 +91,18 @@ export const calcCandleHigh = (candles: Candle[]): string | undefined => {
export const calcCandleVolume = (candles: Candle[]): string | undefined =>
candles &&
candles.reduce((acc, c) => new BigNumber(acc).plus(c.volume).toString(), '0');
+
+export const calcTradedFactor = (m: MarketMaybeWithDataAndCandles) => {
+ const volume = Number(calcCandleVolume(m.candles || []) || 0);
+ const price = m.data?.markPrice ? Number(m.data.markPrice) : 0;
+ const quantum = Number(
+ m.tradableInstrument.instrument.product.settlementAsset.quantum
+ );
+ const decimals = Number(
+ m.tradableInstrument.instrument.product.settlementAsset.decimals
+ );
+ const fp = toBigNum(price, decimals);
+ const fq = toBigNum(quantum, decimals);
+ const factor = fq.multipliedBy(fp).multipliedBy(volume);
+ return factor.toNumber();
+};